text: /* $OpenLDAP$ */
  /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
   *
   * Copyright 2000-2018 The OpenLDAP Foundation.
   * All rights reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted only as authorized by the OpenLDAP
   * Public License.
   *
   * A copy of this license is available in the file LICENSE in the
   * top-level directory of the distribution or, alternatively, at
   * <http://www.OpenLDAP.org/license.html>.
   */
  /* ACKNOWLEDGEMENT:
   * This work was initially developed by Pierangelo Masarati for
   * inclusion in OpenLDAP Software.
   */
  
  #include <portable.h>
  #include <stdio.h>
  
  #ifdef HAVE_PWD_H
  #include <pwd.h>
  #endif
  
  #include "rewrite-int.h"
  #include "rewrite-map.h"
  
  static int num_mappers;
  static const rewrite_mapper** mappers;
  #define MAPPER_ALLOC 8
  
  struct rewrite_map* rewrite_map_parse(struct rewrite_info* info,
                                        const char* string, const char** currpos)
  {
      struct rewrite_map* map = NULL;
      struct rewrite_subst* subst = NULL;
      char *s, *begin = NULL, *end;
      const char* p;
      int l, cnt, mtx = 0, rc = 0;
  
      assert(info != NULL);
      assert(string != NULL);
      assert(currpos != NULL);
  
      *currpos = NULL;
  
      /*
       * Go to the end of the map invocation (the right closing brace)
       */
      for (p = string, cnt = 1; p[0] != '\0' && cnt > 0; p++)
      {
          if (IS_REWRITE_SUBMATCH_ESCAPE(p[0]))
          {
              /*
               * '%' marks the beginning of a new map
               */
              if (p[1] == '{')
              {
                  cnt++;
                  /*
                   * '%' followed by a digit may mark the beginning
                   * of an old map
                   */
              }
              else if (isdigit((unsigned char)p[1]) && p[2] == '{')
              {
                  cnt++;
                  p++;
              }
  
              if (p[1] != '\0')
              {
                  p++;
              }
          }
          else if (p[0] == '}')
          {
              cnt--;
          }
      }
      if (cnt != 0)
      {
          return NULL;
      }
      *currpos = p;
  
      /*
       * Copy the map invocation
       */
      l = p - string - 1;
      s = calloc(sizeof(char), l + 1);
      if (s == NULL)
      {
          return NULL;
      }
      AC_MEMCPY(s, string, l);
      s[l] = 0;
  
      /*
       * Isolate the map name (except for variable deref)
       */
      switch (s[0])
      {
          case REWRITE_OPERATOR_VARIABLE_GET:
          case REWRITE_OPERATOR_PARAM_GET:
              break;
  
          default:
              begin = strchr(s, '(');
              if (begin == NULL)
              {
                  rc = -1;
                  goto cleanup;
              }
              begin[0] = '\0';
              begin++;
              break;
      }
  
      /*
       * Check for special map types
       */
      p = s;
      switch (p[0])
      {
          case REWRITE_OPERATOR_SUBCONTEXT:
          case REWRITE_OPERATOR_COMMAND:
          case REWRITE_OPERATOR_VARIABLE_SET:
          case REWRITE_OPERATOR_VARIABLE_GET:
          case REWRITE_OPERATOR_PARAM_GET:
              p++;
              break;
      }
  
      /*
       * Variable set and get may be repeated to indicate session-wide
       * instead of operation-wide variables
       */
      switch (p[0])
      {
          case REWRITE_OPERATOR_VARIABLE_SET:
          case REWRITE_OPERATOR_VARIABLE_GET:
              p++;
              break;
      }
  
      /*
       * Variable get token can be appended to variable set to mean store
       * AND rewrite
       */
      if (p[0] == REWRITE_OPERATOR_VARIABLE_GET)
      {
          p++;
      }
  
      /*
       * Check the syntax of the variable name
       */
      if (!isalpha((unsigned char)p[0]))
      {
          rc = -1;
          goto cleanup;
      }
      for (p++; p[0] != '\0'; p++)
      {
          if (!isalnum((unsigned char)p[0]))
          {
              rc = -1;
              goto cleanup;
          }
      }
  
      /*
       * Isolate the argument of the map (except for variable deref)
       */
      switch (s[0])
      {
          case REWRITE_OPERATOR_VARIABLE_GET:
          case REWRITE_OPERATOR_PARAM_GET:
              break;
  
          default:
              end = strrchr(begin, ')');
              if (end == NULL)
              {
                  rc = -1;
                  goto cleanup;
              }
              end[0] = '\0';
  
              /*
               * Compile the substitution pattern of the map argument
               */
              subst = rewrite_subst_compile(info, begin);
              if (subst == NULL)
              {
                  rc = -1;
                  goto cleanup;
              }
              break;
      }
  
      /*
       * Create the map
       */
      map = calloc(sizeof(struct rewrite_map), 1);
      if (map == NULL)
      {
          rc = -1;
          goto cleanup;
      }
      memset(map, 0, sizeof(struct rewrite_map));
  
  #ifdef USE_REWRITE_LDAP_PVT_THREADS
      if (ldap_pvt_thread_mutex_init(&map->lm_mutex))
      {
          rc = -1;
          goto cleanup;
      }
      ++mtx;
  #endif /* USE_REWRITE_LDAP_PVT_THREADS */
  
      /*
       * No subst for variable deref
       */
      switch (s[0])
      {
          case REWRITE_OPERATOR_VARIABLE_GET:
          case REWRITE_OPERATOR_PARAM_GET:
              break;
  
          default:
              map->lm_subst = subst;
              break;
      }
  
      /*
       * Parses special map types
       */
      switch (s[0])
      {
  
          /*
           * Subcontext
           */
          case REWRITE_OPERATOR_SUBCONTEXT: /* '>' */
  
              /*
               * Fetch the rewrite context
               * it MUST have been defined previously
               */
              map->lm_type = REWRITE_MAP_SUBCONTEXT;
              map->lm_name = strdup(s + 1);
              if (map->lm_name == NULL)
              {
                  rc = -1;
                  goto cleanup;
              }
              map->lm_data = rewrite_context_find(info, s + 1);
              if (map->lm_data == NULL)
              {
                  rc = -1;
                  goto cleanup;
              }
              break;
  
          /*
           * External command (not implemented yet)
           */
          case REWRITE_OPERATOR_COMMAND: /* '|' */
              rc = -1;
              goto cleanup;
  
          /*
           * Variable set
           */
          case REWRITE_OPERATOR_VARIABLE_SET: /* '&' */
              if (s[1] == REWRITE_OPERATOR_VARIABLE_SET)
              {
                  if (s[2] == REWRITE_OPERATOR_VARIABLE_GET)
                  {
                      map->lm_type = REWRITE_MAP_SETW_SESN_VAR;
                      map->lm_name = strdup(s + 3);
                  }
                  else
                  {
                      map->lm_type = REWRITE_MAP_SET_SESN_VAR;
                      map->lm_name = strdup(s + 2);
                  }
              }
              else
              {
                  if (s[1] == REWRITE_OPERATOR_VARIABLE_GET)
                  {
                      map->lm_type = REWRITE_MAP_SETW_OP_VAR;
                      map->lm_name = strdup(s + 2);
                  }
                  else
                  {
                      map->lm_type = REWRITE_MAP_SET_OP_VAR;
                      map->lm_name = strdup(s + 1);
                  }
              }
              if (map->lm_name == NULL)
              {
                  rc = -1;
                  goto cleanup;
              }
              break;
  
          /*
           * Variable dereference
           */
          case REWRITE_OPERATOR_VARIABLE_GET: /* '*' */
              if (s[1] == REWRITE_OPERATOR_VARIABLE_GET)
              {
                  map->lm_type = REWRITE_MAP_GET_SESN_VAR;
                  map->lm_name = strdup(s + 2);
              }
              else
              {
                  map->lm_type = REWRITE_MAP_GET_OP_VAR;
                  map->lm_name = strdup(s + 1);
              }
              if (map->lm_name == NULL)
              {
                  rc = -1;
                  goto cleanup;
              }
              break;
  
          /*
           * Parameter
           */
          case REWRITE_OPERATOR_PARAM_GET: /* '$' */
              map->lm_type = REWRITE_MAP_GET_PARAM;
              map->lm_name = strdup(s + 1);
              if (map->lm_name == NULL)
              {
                  rc = -1;
                  goto cleanup;
              }
              break;
  
          /*
           * Built-in map
           */
          default:
              map->lm_type = REWRITE_MAP_BUILTIN;
              map->lm_name = strdup(s);
              if (map->lm_name == NULL)
              {
                  rc = -1;
                  goto cleanup;
              }
              map->lm_data = rewrite_builtin_map_find(info, s);
              if (map->lm_data == NULL)
              {
                  rc = -1;
                  goto cleanup;
              }
              break;
      }
  
  cleanup:
      free(s);
      if (rc)
      {
          if (subst != NULL)
          {
              free(subst);
          }
          if (map)
          {
  #ifdef USE_REWRITE_LDAP_PVT_THREADS
              if (mtx)
              {
                  ldap_pvt_thread_mutex_destroy(&map->lm_mutex);
              }
  #endif /* USE_REWRITE_LDAP_PVT_THREADS */
  
              if (map->lm_name)
              {
                  free(map->lm_name);
                  map->lm_name = NULL;
              }
              free(map);
              map = NULL;
          }
      }
  
      return map;
  }
  
  /*
   * Applies the new map type
   */
  int rewrite_map_apply(struct rewrite_info* info, struct rewrite_op* op,
                        struct rewrite_map* map, struct berval* key,
                        struct berval* val)
  {
      int rc = REWRITE_SUCCESS;
  
      assert(info != NULL);
      assert(op != NULL);
      assert(map != NULL);
      assert(key != NULL);
      assert(val != NULL);
  
      val->bv_val = NULL;
      val->bv_len = 0;
  
      switch (map->lm_type)
      {
          case REWRITE_MAP_SUBCONTEXT:
              rc = rewrite_context_apply(info, op,
                                         (struct rewrite_context*)map->lm_data,
                                         key->bv_val, &val->bv_val);
              if (val->bv_val != NULL)
              {
                  if (val->bv_val == key->bv_val)
                  {
                      val->bv_len = key->bv_len;
                      key->bv_val = NULL;
                  }
                  else
                  {
                      val->bv_len = strlen(val->bv_val);
                  }
              }
              break;
  
          case REWRITE_MAP_SET_OP_VAR:
          case REWRITE_MAP_SETW_OP_VAR:
              rc = rewrite_var_set(&op->lo_vars, map->lm_name, key->bv_val, 1)
                       ? REWRITE_SUCCESS
                       : REWRITE_ERR;
              if (rc == REWRITE_SUCCESS)
              {
                  if (map->lm_type == REWRITE_MAP_SET_OP_VAR)
                  {
                      val->bv_val = strdup("");
                  }
                  else
                  {
                      val->bv_val = strdup(key->bv_val);
                      val->bv_len = key->bv_len;
                  }
                  if (val->bv_val == NULL)
                  {
                      rc = REWRITE_ERR;
                  }
              }
              break;
  
          case REWRITE_MAP_GET_OP_VAR:
          {
              struct rewrite_var* var;
  
              var = rewrite_var_find(op->lo_vars, map->lm_name);
              if (var == NULL)
              {
                  rc = REWRITE_ERR;
              }
              else
              {
                  val->bv_val = strdup(var->lv_value.bv_val);
                  val->bv_len = var->lv_value.bv_len;
                  if (val->bv_val == NULL)
                  {
                      rc = REWRITE_ERR;
                  }
              }
              break;
          }
  
          case REWRITE_MAP_SET_SESN_VAR:
          case REWRITE_MAP_SETW_SESN_VAR:
              if (op->lo_cookie == NULL)
              {
                  rc = REWRITE_ERR;
                  break;
              }
              rc = rewrite_session_var_set(info, op->lo_cookie, map->lm_name,
                                           key->bv_val);
              if (rc == REWRITE_SUCCESS)
              {
                  if (map->lm_type == REWRITE_MAP_SET_SESN_VAR)
                  {
                      val->bv_val = strdup("");
                  }
                  else
                  {
                      val->bv_val = strdup(key->bv_val);
                      val->bv_len = key->bv_len;
                  }
                  if (val->bv_val == NULL)
                  {
                      rc = REWRITE_ERR;
                  }
              }
              break;
  
          case REWRITE_MAP_GET_SESN_VAR:
              rc =
                  rewrite_session_var_get(info, op->lo_cookie, map->lm_name, val);
              break;
  
          case REWRITE_MAP_GET_PARAM:
              rc = rewrite_param_get(info, map->lm_name, val);
              break;
  
          case REWRITE_MAP_BUILTIN:
          {
              struct rewrite_builtin_map* bmap = map->lm_data;
  
              if (bmap->lb_mapper && bmap->lb_mapper->rm_apply)
                  rc = bmap->lb_mapper->rm_apply(bmap->lb_private, key->bv_val,
                                                 val);
              else
                  rc = REWRITE_ERR;
              break;
              break;
          }
  
          default:
              rc = REWRITE_ERR;
              break;
      }
  
      return rc;
  }
  
  void rewrite_builtin_map_free(void* tmp)
  {
      struct rewrite_builtin_map* map = (struct rewrite_builtin_map*)tmp;
  
      assert(map != NULL);
  
      if (map->lb_mapper && map->lb_mapper->rm_destroy)
          map->lb_mapper->rm_destroy(map->lb_private);
  
      free(map->lb_name);
      free(map);
  }
  
  int rewrite_map_destroy(struct rewrite_map** pmap)
  {
      struct rewrite_map* map;
  
      assert(pmap != NULL);
      assert(*pmap != NULL);
  
      map = *pmap;
  
  #ifdef USE_REWRITE_LDAP_PVT_THREADS
      ldap_pvt_thread_mutex_lock(&map->lm_mutex);
  #endif /* USE_REWRITE_LDAP_PVT_THREADS */
  
      if (map->lm_name)
      {
          free(map->lm_name);
          map->lm_name = NULL;
      }
  
      if (map->lm_subst)
      {
          rewrite_subst_destroy(&map->lm_subst);
      }
  
  #ifdef USE_REWRITE_LDAP_PVT_THREADS
      ldap_pvt_thread_mutex_unlock(&map->lm_mutex);
      ldap_pvt_thread_mutex_destroy(&map->lm_mutex);
  #endif /* USE_REWRITE_LDAP_PVT_THREADS */
  
      free(map);
      *pmap = NULL;
  
      return 0;
  }
  
  /* ldapmap.c */
  extern const rewrite_mapper rewrite_ldap_mapper;
  
  const rewrite_mapper* rewrite_mapper_find(const char* name)
  {
      int i;
  
      if (!strcasecmp(name, "ldap"))
          return &rewrite_ldap_mapper;
  
      for (i = 0; i < num_mappers; i++)
          if (!strcasecmp(name, mappers[i]->rm_name))
              return mappers[i];
      return NULL;
  }
  
  int rewrite_mapper_register(const rewrite_mapper* map)
  {
      if (num_mappers % MAPPER_ALLOC == 0)
      {
          const rewrite_mapper** mnew;
          mnew = realloc(mappers,
                         (num_mappers + MAPPER_ALLOC) * sizeof(rewrite_mapper*));
          if (mnew)
              mappers = mnew;
          else
              return -1;
      }
      mappers[num_mappers++] = map;
      return 0;
  }
  
  int rewrite_mapper_unregister(const rewrite_mapper* map)
  {
      int i;
  
      for (i = 0; i < num_mappers; i++)
      {
          if (mappers[i] == map)
          {
              num_mappers--;
              mappers[i] = mappers[num_mappers];
              mappers[num_mappers] = NULL;
              return 0;
          }
      }
      /* not found */
      return -1;
  }
  