text: /*
   *  Licensed to the Apache Software Foundation (ASF) under one or more
   *  contributor license agreements.  See the NOTICE file distributed with
   *  this work for additional information regarding copyright ownership.
   *  The ASF licenses this file to You under the Apache License, Version 2.0
   *  (the "License"); you may not use this file except in compliance with
   *  the License.  You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   *  Unless required by applicable law or agreed to in writing, software
   *  distributed under the License is distributed on an "AS IS" BASIS,
   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   *  See the License for the specific language governing permissions and
   *  limitations under the License.
   */
  
  /***************************************************************************
   * Description: ajpv1.2 worker, used to call local or remote jserv hosts   *
   *              This worker is deprecated                                  *
   * Author:      Gal Shachor <shachor@il.ibm.com>                           *
   * Based on:    jserv_ajpv12.c from Jserv                                  *
   * Version:     $Revision: 1658173 $                                          *
   ***************************************************************************/
  
  #include "jk_ajp12_worker.h"
  #include "jk_pool.h"
  #include "jk_connect.h"
  #include "jk_util.h"
  #include "jk_sockbuf.h"
  #if defined(AS400) && !defined(AS400_UTF8)
  #include "util_ebcdic.h"
  #include <string.h>
  #endif
  
  #define AJP_DEF_HOST            ("localhost")
  #define AJP_DEF_PORT            (8007)
  #define READ_BUF_SIZE           (8*1024)
  #define DEF_RETRY_ATTEMPTS      (1)
  
  struct ajp12_worker
  {
      jk_sockaddr_t worker_inet_addr;
      jk_sockaddr_t worker_source_inet_addr;
      unsigned connect_retry_attempts;
      char *name;
      jk_worker_t worker;
  };
  
  typedef struct ajp12_worker ajp12_worker_t;
  
  struct ajp12_endpoint
  {
      ajp12_worker_t *worker;
  
      jk_sock_t sd;
      jk_sockbuf_t sb;
  
      jk_endpoint_t endpoint;
  };
  typedef struct ajp12_endpoint ajp12_endpoint_t;
  
  static int ajpv12_mark(ajp12_endpoint_t * p, unsigned char type);
  
  #if defined(AS400) && !defined(AS400_UTF8)
  static int ajpv12_sendasciistring(ajp12_endpoint_t * p, char *buffer);
  #endif
  
  #if defined(AS400) && !defined(AS400_UTF8)
  static int ajpv12_sendstring(ajp12_endpoint_t * p, char *buffer);
  #else
  static int ajpv12_sendstring(ajp12_endpoint_t * p, const char *buffer);
  #endif
  
  static int ajpv12_sendint(ajp12_endpoint_t * p, int d);
  
  static int ajpv12_sendnbytes(ajp12_endpoint_t * p,
                               const void *buffer, int bufferlen);
  
  static int ajpv12_flush(ajp12_endpoint_t * p);
  
  static int ajpv12_handle_response(ajp12_endpoint_t * p,
                                    jk_ws_service_t *s, jk_logger_t *l);
  
  static int ajpv12_handle_request(ajp12_endpoint_t * p,
                                   jk_ws_service_t *s, jk_logger_t *l);
  
  /*
   * Return values of service() method for ajp12 worker:
   * return value  is_error              reason
   * JK_FALSE      JK_HTTP_SERVER_ERROR  Invalid parameters (null values)
   *                                     Error during connect to the backend
   *                                     ajpv12_handle_request() returns false:
   *           Any error during reading a request body from the client or
   *           sending the request to the backend
   * JK_FALSE      JK_HTTP_OK            ajpv12_handle_response() returns false:
   *           Any error during reading parts of response from backend or
   *           sending to client
   * JK_TRUE       JK_HTTP_OK            All other cases
   */
  static int JK_METHOD service(jk_endpoint_t *e,
                               jk_ws_service_t *s,
                               jk_logger_t *l, int *is_error)
  {
      ajp12_endpoint_t *p;
      unsigned int attempt;
      int rc = -1;
      /*
       * AJP12 protocol is not recoverable.
       */
  
      JK_TRACE_ENTER(l);
  
      if (!e || !e->endpoint_private || !s || !is_error) {
          JK_LOG_NULL_PARAMS(l);
          if (is_error)
              *is_error = JK_HTTP_SERVER_ERROR;
          JK_TRACE_EXIT(l);
          return JK_FALSE;
      }
  
      p = e->endpoint_private;
  
      /* Set returned error to OK */
      *is_error = JK_HTTP_OK;
  
      for (attempt = 0; attempt < p->worker->connect_retry_attempts;
           attempt++) {
          p->sd =
              jk_open_socket(&p->worker->worker_inet_addr,
                             p->worker->worker_source_inet_addr.ipaddr_ptr != NULL ?
                                 &p->worker->worker_source_inet_addr :
                                 NULL,
                             JK_FALSE, 0, 0, 0, l);
  
          jk_log(l, JK_LOG_DEBUG, "In jk_endpoint_t::service, sd = %d",
                 p->sd);
          if (IS_VALID_SOCKET(p->sd)) {
              break;
          }
      }
      if (IS_VALID_SOCKET(p->sd)) {
  
          jk_sb_open(&p->sb, p->sd);
          if (ajpv12_handle_request(p, s, l)) {
              jk_log(l, JK_LOG_DEBUG,
                     "In jk_endpoint_t::service, sent request");
              rc = ajpv12_handle_response(p, s, l);
              JK_TRACE_EXIT(l);
              return rc;
          }
      }
      jk_log(l, JK_LOG_ERROR, "In jk_endpoint_t::service, Error sd = %d",
             p->sd);
      *is_error = JK_HTTP_SERVER_ERROR;
  
      JK_TRACE_EXIT(l);
      return JK_FALSE;
  }
  
  static int JK_METHOD done(jk_endpoint_t **e, jk_logger_t *l)
  {
      jk_log(l, JK_LOG_DEBUG, "Into jk_endpoint_t::done");
      if (e && *e && (*e)->endpoint_private) {
          ajp12_endpoint_t *p = (*e)->endpoint_private;
          if (IS_VALID_SOCKET(p->sd)) {
              jk_shutdown_socket(p->sd, l);
          }
          free(p);
          *e = NULL;
          return JK_TRUE;
      }
  
      jk_log(l, JK_LOG_ERROR, "In jk_endpoint_t::done, NULL parameters");
      return JK_FALSE;
  }
  
  static int JK_METHOD validate(jk_worker_t *pThis,
                                jk_map_t *props,
                                jk_worker_env_t *we, jk_logger_t *l)
  {
      jk_log(l, JK_LOG_DEBUG, "Into jk_worker_t::validate");
  
      if (pThis && pThis->worker_private) {
          ajp12_worker_t *p = pThis->worker_private;
          int port = jk_get_worker_port(props,
                                        p->name,
                                        AJP_DEF_PORT);
  
          const char *host = jk_get_worker_host(props,
                                          p->name,
                                          AJP_DEF_HOST);
  
          const char *source = jk_get_worker_host(props,
                                                  p->name,
                                                  "");
  
          jk_log(l, JK_LOG_DEBUG,
                 "In jk_worker_t::validate for worker %s target is %s:%d",
                 p->name, host, port);
  
          if (host) {
              if (!jk_resolve(host, port, &p->worker_inet_addr, we->pool, JK_FALSE, l)) {
                  jk_log(l, JK_LOG_ERROR,
                         "In jk_worker_t::validate, host '%s:%d' resolve failed",
                         host, port);
                  return JK_FALSE;
              }
          } else {
              jk_log(l, JK_LOG_ERROR,
                     "In jk_worker_t::validate, Error no host name given");
              return JK_FALSE;
          }
          if (source && *source) {
              if (!jk_resolve(source, 0, &p->worker_source_inet_addr, we->pool, JK_FALSE, l)) {
                  p->worker_source_inet_addr.ipaddr_ptr = NULL;
                  jk_log(l, JK_LOG_WARNING,
                         "In jk_worker_t::validate, source addr '%s' resolve failed - ignored",
                         source);
              }
          }
      }
      else {
          jk_log(l, JK_LOG_ERROR,
                 "In jk_worker_t::validate, NULL parameters");
      }
  
      return JK_FALSE;
  }
  
  static int JK_METHOD init(jk_worker_t *pThis,
                            jk_map_t *props,
                            jk_worker_env_t *we, jk_logger_t *log)
  {
      /* Nothing to do for now */
      return JK_TRUE;
  }
  
  static int JK_METHOD get_endpoint(jk_worker_t *pThis,
                                    jk_endpoint_t **pend, jk_logger_t *l)
  {
      jk_log(l, JK_LOG_DEBUG, "Into jk_worker_t::get_endpoint");
  
      if (pThis && pThis->worker_private && pend) {
          ajp12_endpoint_t *p =
              (ajp12_endpoint_t *) malloc(sizeof(ajp12_endpoint_t));
          if (p) {
              p->sd = JK_INVALID_SOCKET;
              p->worker = pThis->worker_private;
              p->endpoint.endpoint_private = p;
              p->endpoint.service = service;
              p->endpoint.done = done;
              *pend = &p->endpoint;
              return JK_TRUE;
          }
          jk_log(l, JK_LOG_ERROR,
                 "In jk_worker_t::get_endpoint, malloc failed");
      }
      else {
          jk_log(l, JK_LOG_ERROR,
                 "In jk_worker_t::get_endpoint, NULL parameters");
      }
  
      return JK_FALSE;
  }
  
  static int JK_METHOD destroy(jk_worker_t **pThis, jk_logger_t *l)
  {
      jk_log(l, JK_LOG_DEBUG, "Into jk_worker_t::destroy");
      if (pThis && *pThis && (*pThis)->worker_private) {
          ajp12_worker_t *private_data = (*pThis)->worker_private;
          free(private_data->name);
          free(private_data);
  
          return JK_TRUE;
      }
  
      jk_log(l, JK_LOG_ERROR, "In jk_worker_t::destroy, NULL parameters");
      return JK_FALSE;
  }
  
  int JK_METHOD ajp12_worker_factory(jk_worker_t **w,
                                     const char *name, jk_logger_t *l)
  {
      jk_log(l, JK_LOG_DEBUG, "Into ajp12_worker_factory");
      if (NULL != name && NULL != w) {
          ajp12_worker_t *private_data =
              (ajp12_worker_t *) malloc(sizeof(ajp12_worker_t));
  
          if (private_data) {
              private_data->name = strdup(name);
  
              if (private_data->name) {
                  private_data->connect_retry_attempts = DEF_RETRY_ATTEMPTS;
                  private_data->worker.worker_private = private_data;
  
                  private_data->worker.validate = validate;
                  private_data->worker.init = init;
                  private_data->worker.get_endpoint = get_endpoint;
                  private_data->worker.destroy = destroy;
                  private_data->worker.maintain = NULL;
  
                  *w = &private_data->worker;
                  return JK_AJP12_WORKER_TYPE;
              }
  
              free(private_data);
          }
          jk_log(l, JK_LOG_ERROR, "In ajp12_worker_factory, malloc failed");
      }
      else {
          jk_log(l, JK_LOG_ERROR, "In ajp12_worker_factory, NULL parameters");
      }
  
      return 0;
  }
  
  static int ajpv12_sendnbytes(ajp12_endpoint_t * p,
                               const void *buffer, int bufferlen)
  {
      unsigned char bytes[2];
      static const unsigned char null_b[2] =
          { (unsigned char)0xff, (unsigned char)0xff };
  
      if (buffer) {
          bytes[0] = (unsigned char)((bufferlen >> 8) & 0xff);
          bytes[1] = (unsigned char)(bufferlen & 0xff);
  
          if (jk_sb_write(&p->sb, bytes, 2)) {
              return jk_sb_write(&p->sb, buffer, bufferlen);
          }
          else {
              return JK_FALSE;
          }
      }
      else {
          return jk_sb_write(&p->sb, null_b, 2);
      }
  }
  
  #if defined(AS400) && !defined(AS400_UTF8)
  static int ajpv12_sendasciistring(ajp12_endpoint_t * p, const char *buffer)
  {
      int bufferlen;
  
      if (buffer && (bufferlen = strlen(buffer))) {
          return ajpv12_sendnbytes(p, buffer, bufferlen);
      }
      else {
          return ajpv12_sendnbytes(p, NULL, 0);
      }
  }
  #endif
  
  static int ajpv12_sendstring(ajp12_endpoint_t * p, const char *buffer)
  {
      int bufferlen;
  
      if (buffer && (bufferlen = (int)strlen(buffer))) {
  #if (defined(AS400) && !defined(AS400_UTF8)) || defined(_OSD_POSIX)
          char buf[2048];
          if (bufferlen < 2048) {
              memcpy(buf, buffer, bufferlen);
              jk_xlate_to_ascii(buf, bufferlen);
              return ajpv12_sendnbytes(p, buf, bufferlen);
          }
          else
              return -1;
  #else
          return ajpv12_sendnbytes(p, buffer, bufferlen);
  #endif
      }
      else {
          return ajpv12_sendnbytes(p, NULL, 0);
      }
  }
  
  static int ajpv12_mark(ajp12_endpoint_t * p, unsigned char type)
  {
      if (jk_sb_write(&p->sb, &type, 1)) {
          return JK_TRUE;
      }
      else {
          return JK_FALSE;
      }
  }
  
  static int ajpv12_sendint(ajp12_endpoint_t * p, int d)
  {
      char buf[20];
      sprintf(buf, "%d", d);
      return ajpv12_sendstring(p, buf);
  }
  
  static int ajpv12_flush(ajp12_endpoint_t * p)
  {
      return jk_sb_flush(&p->sb);
  }
  
  static int ajpv12_handle_request(ajp12_endpoint_t * p,
                                   jk_ws_service_t *s, jk_logger_t *l)
  {
      int ret;
  
      jk_log(l, JK_LOG_DEBUG, "Into ajpv12_handle_request");
      /*
       * Start the ajp 12 service sequence
       */
      jk_log(l, JK_LOG_DEBUG,
             "ajpv12_handle_request, sending the ajp12 start sequence");
  
      ret = (ajpv12_mark(p, 1) && ajpv12_sendstring(p, s->method) && ajpv12_sendstring(p, 0) &&   /* zone */
             ajpv12_sendstring(p, 0) &&   /* servlet */
             ajpv12_sendstring(p, s->server_name) && ajpv12_sendstring(p, 0) &&   /* doc root */
             ajpv12_sendstring(p, 0) &&   /* path info */
             ajpv12_sendstring(p, 0) &&   /* path translated */
  #if defined(AS400) && !defined(AS400_UTF8)
             ajpv12_sendasciistring(p, s->query_string) &&
  #else
             ajpv12_sendstring(p, s->query_string) &&
  #endif
             ajpv12_sendstring(p, s->remote_addr) &&
             ajpv12_sendstring(p, s->remote_host) &&
             ajpv12_sendstring(p, s->remote_user) &&
             ajpv12_sendstring(p, s->auth_type) &&
             ajpv12_sendint(p, s->server_port) &&
  #if defined(AS400) && !defined(AS400_UTF8)
             ajpv12_sendasciistring(p, s->method) &&
  #else
             ajpv12_sendstring(p, s->method) &&
  #endif
             ajpv12_sendstring(p, s->req_uri) && ajpv12_sendstring(p, 0) &&       /* */
             ajpv12_sendstring(p, 0) &&   /* SCRIPT_NAME */
  #if defined(AS400) && !defined(AS400_UTF8)
             ajpv12_sendasciistring(p, s->server_name) &&
  #else
             ajpv12_sendstring(p, s->server_name) &&
  #endif
             ajpv12_sendint(p, s->server_port) && ajpv12_sendstring(p, s->protocol) && ajpv12_sendstring(p, 0) && /* SERVER_SIGNATURE */
             ajpv12_sendstring(p, s->server_software) && ajpv12_sendstring(p, s->route) &&    /* JSERV_ROUTE */
             ajpv12_sendstring(p, "") &&  /* JSERV ajpv12 compatibility */
             ajpv12_sendstring(p, ""));   /* JSERV ajpv12 compatibility */
  
      if (!ret) {
          jk_log(l, JK_LOG_ERROR,
                 "In ajpv12_handle_request, failed to send the ajp12 start sequence");
          return JK_FALSE;
      }
  
      if (s->num_attributes > 0) {
          unsigned i;
          jk_log(l, JK_LOG_DEBUG,
                 "ajpv12_handle_request, sending the environment variables");
  
          for (i = 0; i < s->num_attributes; i++) {
              ret = (ajpv12_mark(p, 5) &&
                     ajpv12_sendstring(p, s->attributes_names[i]) &&
                     ajpv12_sendstring(p, s->attributes_values[i]));
              if (!ret) {
                  jk_log(l, JK_LOG_ERROR,
                         "In ajpv12_handle_request, failed to send environment");
                  return JK_FALSE;
              }
          }
      }
  
      jk_log(l, JK_LOG_DEBUG, "ajpv12_handle_request, sending the headers");
  
      /* Send the request headers */
      if (s->num_headers) {
          unsigned i;
          for (i = 0; i < s->num_headers; ++i) {
              ret = (ajpv12_mark(p, 3) &&
                     ajpv12_sendstring(p, s->headers_names[i]) &&
                     ajpv12_sendstring(p, s->headers_values[i]));
  
              if (!ret) {
                  jk_log(l, JK_LOG_ERROR,
                         "In ajpv12_handle_request, failed to send headers");
                  return JK_FALSE;
              }
          }
      }
  
      jk_log(l, JK_LOG_DEBUG,
             "ajpv12_handle_request, sending the terminating mark");
  
      ret = (ajpv12_mark(p, 4) && ajpv12_flush(p));
      if (!ret) {
          jk_log(l, JK_LOG_ERROR,
                 "In ajpv12_handle_request, failed to send the terminating mark");
          return JK_FALSE;
      }
  
      if (s->content_length) {
          char buf[READ_BUF_SIZE];
          jk_uint64_t so_far = 0;
  
          jk_log(l, JK_LOG_DEBUG,
                 "ajpv12_handle_request, sending the request body");
  
          while (so_far < s->content_length) {
              unsigned this_time = 0;
              unsigned to_read;
              if (s->content_length > so_far + READ_BUF_SIZE) {
                  to_read = READ_BUF_SIZE;
              }
              else {
                  to_read = (unsigned int)(s->content_length - so_far);
              }
  
              if (!s->read(s, buf, to_read, &this_time)) {
                  jk_log(l, JK_LOG_ERROR,
                         "In ajpv12_handle_request, failed to read from the web server");
                  return JK_FALSE;
              }
              jk_log(l, JK_LOG_DEBUG, "ajpv12_handle_request, read %d bytes",
                     this_time);
              if (this_time > 0) {
                  so_far += this_time;
                  if ((int)this_time != send(p->sd, buf, this_time, 0)) {
                      jk_log(l, JK_LOG_ERROR,
                             "In ajpv12_handle_request, failed to write to the container");
                      return JK_FALSE;
                  }
                  jk_log(l, JK_LOG_DEBUG,
                         "ajpv12_handle_request, sent %d bytes", this_time);
              }
              else if (this_time == 0) {
                  jk_log(l, JK_LOG_ERROR,
                         "In ajpv12_handle_request, Error: short read. content length is %" JK_UINT64_T_FMT ", read %" JK_UINT64_T_FMT,
                         s->content_length, so_far);
                  return JK_FALSE;
              }
          }
      }
  
      jk_log(l, JK_LOG_DEBUG, "ajpv12_handle_request done");
      return JK_TRUE;
  }
  
  static int ajpv12_handle_response(ajp12_endpoint_t * p,
                                    jk_ws_service_t *s, jk_logger_t *l)
  {
      int status = 200;
      char *reason = NULL;
      char **names = NULL;
      char **values = NULL;
      int headers_capacity = 0;
      int headers_len = 0;
      int write_to_ws;
  
      jk_log(l, JK_LOG_DEBUG, "Into ajpv12_handle_response");
      /*
       * Read headers ...
       */
      while (1) {
          char *line = NULL;
          char *name = NULL;
          char *value = NULL;
  #ifdef _MT_CODE_PTHREAD
          char *lasts;
  #endif
  
          if (!jk_sb_gets(&p->sb, &line)) {
              jk_log(l, JK_LOG_ERROR,
                     "ajpv12_handle_response, error reading header line");
              return JK_FALSE;
          }
  #if (defined(AS400) && !defined(AS400_UTF8)) || defined(_OSD_POSIX)
          jk_xlate_from_ascii(line, strlen(line));
  #endif
  
          jk_log(l, JK_LOG_DEBUG, "ajpv12_handle_response, read %s", line);
          if (0 == strlen(line)) {
              jk_log(l, JK_LOG_DEBUG,
                     "ajpv12_handle_response, headers are done");
              break;              /* Empty line -> end of headers */
          }
  
          name = line;
          while (jk_isspace(*name) && *name) {
              name++;             /* Skip leading white chars */
          }
          if (!*name) {           /* Empty header name */
              jk_log(l, JK_LOG_ERROR,
                     "ajpv12_handle_response, empty header name");
              return JK_FALSE;
          }
          if (!(value = strchr(name, ':'))) {
              jk_log(l, JK_LOG_ERROR,
                     "ajpv12_handle_response, no value supplied");
              return JK_FALSE;    /* No value !!! */
          }
          *value = '\0';
          value++;
          while (jk_isspace(*value) && *value) {
              value++;            /* Skip leading white chars */
          }
          if (!*value) {          /* Empty header value */
              jk_log(l, JK_LOG_ERROR,
                     "ajpv12_handle_response, empty header value");
              return JK_FALSE;
          }
  
          jk_log(l, JK_LOG_DEBUG, "ajpv12_handle_response, read %s=%s", name,
                 value);
          if (0 == strcmp("Status", name)) {
  #ifdef _MT_CODE_PTHREAD
              char *numeric = strtok_r(value, " \t", &lasts);
  #else
              char *numeric = strtok(value, " \t");
  #endif
  
              status = atoi(numeric);
              if (status < 100 || status > 999) {
                  jk_log(l, JK_LOG_ERROR,
                         "ajpv12_handle_response, invalid status code");
                  return JK_FALSE;
              }
  #ifdef _MT_CODE_PTHREAD
              reason = jk_pool_strdup(s->pool, strtok_r(NULL, " \t", &lasts));
  #else
              reason = jk_pool_strdup(s->pool, strtok(NULL, " \t"));
  #endif
          }
          else {
              if (headers_capacity == headers_len) {
                  jk_log(l, JK_LOG_DEBUG,
                         "ajpv12_handle_response, allocating header arrays");
                  names =
                      (char **)jk_pool_realloc(s->pool,
                                               sizeof(char *) *
                                               (headers_capacity + 5), names,
                                               sizeof(char *) *
                                               headers_capacity);
                  values =
                      (char **)jk_pool_realloc(s->pool,
                                               sizeof(char *) *
                                               (headers_capacity + 5), values,
                                               sizeof(char *) *
                                               headers_capacity);
                  if (!values || !names) {
                      jk_log(l, JK_LOG_ERROR,
                             "ajpv12_handle_response, malloc error");
                      return JK_FALSE;
                  }
                  headers_capacity = headers_capacity + 5;
              }
              names[headers_len] = jk_pool_strdup(s->pool, name);
              values[headers_len] = jk_pool_strdup(s->pool, value);
              headers_len++;
          }
      }
  
      jk_log(l, JK_LOG_DEBUG, "ajpv12_handle_response, starting response");
      if (!s->start_response(s,
                             status,
                             reason,
                             (const char *const *)names,
                             (const char *const *)values, headers_len)) {
          jk_log(l, JK_LOG_ERROR,
                 "ajpv12_handle_response, error starting response");
          return JK_FALSE;
      }
  
      jk_log(l, JK_LOG_DEBUG,
             "ajpv12_handle_response, reading response body");
      /*
       * Read response body
       */
      write_to_ws = JK_TRUE;
      while (1) {
          unsigned to_read = READ_BUF_SIZE;
          unsigned acc = 0;
          char *buf = NULL;
  
          if (!jk_sb_read(&p->sb, &buf, to_read, &acc)) {
              jk_log(l, JK_LOG_ERROR,
                     "ajpv12_handle_response, error reading from ");
              return JK_FALSE;
          }
  
          if (!acc) {
              jk_log(l, JK_LOG_DEBUG,
                     "ajpv12_handle_response, response body is done");
              break;
          }
  
          if (write_to_ws) {
              if (!s->write(s, buf, acc)) {
                  jk_log(l, JK_LOG_ERROR,
                         "ajpv12_handle_response, error writing back to server");
                  write_to_ws = JK_FALSE;
              }
          }
      }
  
      jk_log(l, JK_LOG_DEBUG, "ajpv12_handle_response done");
      return JK_TRUE;
  }
  