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.
   */
  
  #ifndef REWRITE_INT_H
  #define REWRITE_INT_H
  
  /*
   * These are required by every file of the library, so they're included here
   */
  #include <ac/ctype.h>
  #include <ac/regex.h>
  #include <ac/socket.h>
  #include <ac/stdlib.h>
  #include <ac/string.h>
  #include <ac/syslog.h>
  #include <ac/unistd.h>
  #include <lber.h>
  #include <ldap.h>
  #define LDAP_DEFINE_LDAP_DEBUG
  #include <avl.h>
  #include <ldap_log.h>
  #include <lutil.h>
  #include <rewrite.h>
  
  #define malloc(x) ber_memalloc(x)
  #define calloc(x, y) ber_memcalloc(x, y)
  #define realloc(x, y) ber_memrealloc(x, y)
  #define free(x) ber_memfree(x)
  #undef strdup
  #define strdup(x) ber_strdup(x)
  
  /* Uncomment to use ldap pvt threads */
  #define USE_REWRITE_LDAP_PVT_THREADS
  #include <ldap_pvt_thread.h>
  
  /*
   * For details, see RATIONALE.
   */
  
  #define REWRITE_MAX_MATCH 11 /* 0: overall string; 1-9: submatches */
  #define REWRITE_MAX_PASSES 100
  
  /*
   * Submatch escape char
   */
  /* the '\' conflicts with slapd.conf parsing */
  /* #define REWRITE_SUBMATCH_ESCAPE			'\\' */
  #define REWRITE_SUBMATCH_ESCAPE_ORIG '%'
  #define REWRITE_SUBMATCH_ESCAPE '$'
  #define IS_REWRITE_SUBMATCH_ESCAPE(c)                                          \
      ((c) == REWRITE_SUBMATCH_ESCAPE || (c) == REWRITE_SUBMATCH_ESCAPE_ORIG)
  
  /*
   * REGEX flags
   */
  
  #define REWRITE_FLAG_HONORCASE 'C'
  #define REWRITE_FLAG_BASICREGEX 'R'
  
  /*
   * Action flags
   */
  #define REWRITE_FLAG_EXECONCE ':'
  #define REWRITE_FLAG_STOP '@'
  #define REWRITE_FLAG_UNWILLING '#'
  #define REWRITE_FLAG_GOTO 'G'       /* requires an arg */
  #define REWRITE_FLAG_USER 'U'       /* requires an arg */
  #define REWRITE_FLAG_MAX_PASSES 'M' /* requires an arg */
  #define REWRITE_FLAG_IGNORE_ERR 'I'
  
  /*
   * Map operators
   */
  #define REWRITE_OPERATOR_SUBCONTEXT '>'
  #define REWRITE_OPERATOR_COMMAND '|'
  #define REWRITE_OPERATOR_VARIABLE_SET '&'
  #define REWRITE_OPERATOR_VARIABLE_GET '*'
  #define REWRITE_OPERATOR_PARAM_GET '$'
  
  /***********
   * PRIVATE *
   ***********/
  
  /*
   * Action
   */
  struct rewrite_action
  {
      struct rewrite_action* la_next;
  
  #define REWRITE_ACTION_STOP 0x0001
  #define REWRITE_ACTION_UNWILLING 0x0002
  #define REWRITE_ACTION_GOTO 0x0003
  #define REWRITE_ACTION_IGNORE_ERR 0x0004
  #define REWRITE_ACTION_USER 0x0005
      int la_type;
      void* la_args;
  };
  
  /*
   * Map
   */
  struct rewrite_map
  {
  
      /*
       * Legacy stuff
       */
  #define REWRITE_MAP_XFILEMAP 0x0001 /* Rough implementation! */
  #define REWRITE_MAP_XPWDMAP 0x0002  /* uid -> gecos */
  #define REWRITE_MAP_XLDAPMAP 0x0003 /* Not implemented yet! */
  
      /*
       * Maps with args
       */
  #define REWRITE_MAP_SUBCONTEXT 0x0101
  
  #define REWRITE_MAP_SET_OP_VAR 0x0102
  #define REWRITE_MAP_SETW_OP_VAR 0x0103
  #define REWRITE_MAP_GET_OP_VAR 0x0104
  #define REWRITE_MAP_SET_SESN_VAR 0x0105
  #define REWRITE_MAP_SETW_SESN_VAR 0x0106
  #define REWRITE_MAP_GET_SESN_VAR 0x0107
  #define REWRITE_MAP_GET_PARAM 0x0108
  #define REWRITE_MAP_BUILTIN 0x0109
      int lm_type;
  
      char* lm_name;
      void* lm_data;
  
      /*
       * Old maps store private data in _lm_args;
       * new maps store the substitution pattern in _lm_subst
       */
      union
      {
          void* _lm_args;
          struct rewrite_subst* _lm_subst;
      } lm_union;
  #define lm_args lm_union._lm_args
  #define lm_subst lm_union._lm_subst
  
  #ifdef USE_REWRITE_LDAP_PVT_THREADS
      ldap_pvt_thread_mutex_t lm_mutex;
  #endif /* USE_REWRITE_LDAP_PVT_THREADS */
  };
  
  /*
   * Builtin maps
   */
  struct rewrite_builtin_map
  {
  #define REWRITE_BUILTIN_MAP 0x0200
      int lb_type;
      char* lb_name;
      void* lb_private;
      const rewrite_mapper* lb_mapper;
  
  #ifdef USE_REWRITE_LDAP_PVT_THREADS
      ldap_pvt_thread_mutex_t lb_mutex;
  #endif /* USE_REWRITE_LDAP_PVT_THREADS */
  };
  
  /*
   * Submatch substitution
   */
  struct rewrite_submatch
  {
  #define REWRITE_SUBMATCH_ASIS 0x0000
  #define REWRITE_SUBMATCH_XMAP 0x0001
  #define REWRITE_SUBMATCH_MAP_W_ARG 0x0002
      int ls_type;
      struct rewrite_map* ls_map;
      int ls_submatch;
      /*
       * The first one represents the index of the submatch in case
       * the map has single submatch as argument;
       * the latter represents the map argument scheme in case
       * the map has substitution string argument form
       */
  };
  
  /*
   * Pattern substitution
   */
  struct rewrite_subst
  {
      size_t lt_subs_len;
      struct berval* lt_subs;
  
      int lt_num_submatch;
      struct rewrite_submatch* lt_submatch;
  };
  
  /*
   * Rule
   */
  struct rewrite_rule
  {
      struct rewrite_rule* lr_next;
      struct rewrite_rule* lr_prev;
  
      char* lr_pattern;
      char* lr_subststring;
      char* lr_flagstring;
      regex_t lr_regex;
  
      /*
       * I was thinking about some kind of per-rule mutex, but there's
       * probably no need, because rules after compilation are only read;
       * however, I need to check whether regexec is reentrant ...
       */
  
      struct rewrite_subst* lr_subst;
  
  #define REWRITE_REGEX_ICASE REG_ICASE
  #define REWRITE_REGEX_EXTENDED REG_EXTENDED
      int lr_flags;
  
  #define REWRITE_RECURSE 0x0001
  #define REWRITE_EXEC_ONCE 0x0002
      int lr_mode;
      int lr_max_passes;
  
      struct rewrite_action* lr_action;
  };
  
  /*
   * Rewrite Context (set of rules)
   */
  struct rewrite_context
  {
      char* lc_name;
      struct rewrite_context* lc_alias;
      struct rewrite_rule* lc_rule;
  };
  
  /*
   * Session
   */
  struct rewrite_session
  {
      void* ls_cookie;
      Avlnode* ls_vars;
  #ifdef USE_REWRITE_LDAP_PVT_THREADS
      ldap_pvt_thread_rdwr_t ls_vars_mutex;
      ldap_pvt_thread_mutex_t ls_mutex;
  #endif /* USE_REWRITE_LDAP_PVT_THREADS */
      int ls_count;
  };
  
  /*
   * Variable
   */
  struct rewrite_var
  {
      char* lv_name;
      int lv_flags;
      struct berval lv_value;
  };
  
  /*
   * Operation
   */
  struct rewrite_op
  {
      int lo_num_passes;
      int lo_depth;
  #if 0 /* FIXME: not used anywhere! (debug? then, why strdup?) */
  	char                           *lo_string;
  #endif
      char* lo_result;
      Avlnode* lo_vars;
      const void* lo_cookie;
  };
  
  /**********
   * PUBLIC *
   **********/
  
  /*
   * Rewrite info
   */
  struct rewrite_info
  {
      Avlnode* li_context;
      Avlnode* li_maps;
      /*
       * No global mutex because maps are read only at
       * config time
       */
      Avlnode* li_params;
      Avlnode* li_cookies;
      int li_num_cookies;
  
  #ifdef USE_REWRITE_LDAP_PVT_THREADS
      ldap_pvt_thread_rdwr_t li_params_mutex;
      ldap_pvt_thread_rdwr_t li_cookies_mutex;
  #endif /* USE_REWRITE_LDAP_PVT_THREADS */
  
      /*
       * Default to `off';
       * use `rewriteEngine {on|off}' directive to alter
       */
      int li_state;
  
      /*
       * Defaults to REWRITE_MAXPASSES;
       * use `rewriteMaxPasses numPasses' directive to alter
       */
  #define REWRITE_MAXPASSES 100
      int li_max_passes;
      int li_max_passes_per_rule;
  
      /*
       * Behavior in case a NULL or non-existent context is required
       */
      int li_rewrite_mode;
  };
  
  /***********
   * PRIVATE *
   ***********/
  
  LDAP_REWRITE_V(struct rewrite_context*) rewrite_int_curr_context;
  
  /*
   * Maps
   */
  
  /*
   * Parses a map (also in legacy 'x' version)
   */
  LDAP_REWRITE_F(struct rewrite_map*)
  rewrite_map_parse(struct rewrite_info* info, const char* s, const char** end);
  
  LDAP_REWRITE_F(struct rewrite_map*)
  rewrite_xmap_parse(struct rewrite_info* info, const char* s, const char** end);
  
  /*
   * Resolves key in val by means of map (also in legacy 'x' version)
   */
  LDAP_REWRITE_F(int)
  rewrite_map_apply(struct rewrite_info* info, struct rewrite_op* op,
                    struct rewrite_map* map, struct berval* key,
                    struct berval* val);
  
  LDAP_REWRITE_F(int)
  rewrite_xmap_apply(struct rewrite_info* info, struct rewrite_op* op,
                     struct rewrite_map* map, struct berval* key,
                     struct berval* val);
  
  LDAP_REWRITE_F(int)
  rewrite_map_destroy(struct rewrite_map** map);
  
  LDAP_REWRITE_F(int)
  rewrite_xmap_destroy(struct rewrite_map** map);
  
  LDAP_REWRITE_F(void)
  rewrite_builtin_map_free(void* map);
  /*
   * Submatch substitution
   */
  
  /*
   * Compiles a substitution pattern
   */
  LDAP_REWRITE_F(struct rewrite_subst*)
  rewrite_subst_compile(struct rewrite_info* info, const char* result);
  
  /*
   * Substitutes a portion of rewritten string according to substitution
   * pattern using submatches
   */
  LDAP_REWRITE_F(int)
  rewrite_subst_apply(struct rewrite_info* info, struct rewrite_op* op,
                      struct rewrite_subst* subst, const char* string,
                      const regmatch_t* match, struct berval* val);
  
  LDAP_REWRITE_F(int)
  rewrite_subst_destroy(struct rewrite_subst** subst);
  
  /*
   * Rules
   */
  
  /*
   * Compiles the rule and appends it at the running context
   */
  LDAP_REWRITE_F(int)
  rewrite_rule_compile(struct rewrite_info* info, struct rewrite_context* context,
                       const char* pattern, const char* result,
                       const char* flagstring);
  
  /*
   * Rewrites string according to rule; may return:
   *      REWRITE_REGEXEC_OK:	fine; if *result != NULL rule matched
   *      			and rewrite succeeded.
   *      REWRITE_REGEXEC_STOP:   fine, rule matched; stop processing
   *      			following rules
   *      REWRITE_REGEXEC_UNWILL: rule matched; force 'unwilling to perform'
   *      REWRITE_REGEXEC_ERR:	an error occurred
   */
  LDAP_REWRITE_F(int)
  rewrite_rule_apply(struct rewrite_info* info, struct rewrite_op* op,
                     struct rewrite_rule* rule, const char* string,
                     char** result);
  
  LDAP_REWRITE_F(int)
  rewrite_rule_destroy(struct rewrite_rule** rule);
  
  /*
   * Sessions
   */
  
  /*
   * Fetches a struct rewrite_session
   */
  LDAP_REWRITE_F(struct rewrite_session*)
  rewrite_session_find(struct rewrite_info* info, const void* cookie);
  
  /*
   * Defines and inits a variable with session scope
   */
  LDAP_REWRITE_F(int)
  rewrite_session_var_set_f(struct rewrite_info* info, const void* cookie,
                            const char* name, const char* value, int flags);
  
  /*
   * Gets a var with session scope
   */
  LDAP_REWRITE_F(int)
  rewrite_session_var_get(struct rewrite_info* info, const void* cookie,
                          const char* name, struct berval* val);
  
  /*
   * Deletes a session
   */
  LDAP_REWRITE_F(int)
  rewrite_session_delete(struct rewrite_info* info, const void* cookie);
  
  /*
   * Destroys the cookie tree
   */
  LDAP_REWRITE_F(int)
  rewrite_session_destroy(struct rewrite_info* info);
  
  /*
   * Vars
   */
  
  /*
   * Finds a var
   */
  LDAP_REWRITE_F(struct rewrite_var*)
  rewrite_var_find(Avlnode* tree, const char* name);
  
  /*
   * Replaces the value of a variable
   */
  LDAP_REWRITE_F(int)
  rewrite_var_replace(struct rewrite_var* var, const char* value, int flags);
  
  /*
   * Inserts a newly created var
   */
  LDAP_REWRITE_F(struct rewrite_var*)
  rewrite_var_insert_f(Avlnode** tree, const char* name, const char* value,
                       int flags);
  
  #define rewrite_var_insert(tree, name, value)                                  \
      rewrite_var_insert_f((tree), (name), (value),                              \
                           REWRITE_VAR_UPDATE | REWRITE_VAR_COPY_NAME |          \
                               REWRITE_VAR_COPY_VALUE)
  
  /*
   * Sets/inserts a var
   */
  LDAP_REWRITE_F(struct rewrite_var*)
  rewrite_var_set_f(Avlnode** tree, const char* name, const char* value,
                    int flags);
  
  #define rewrite_var_set(tree, name, value, insert)                             \
      rewrite_var_set_f((tree), (name), (value),                                 \
                        REWRITE_VAR_UPDATE | REWRITE_VAR_COPY_NAME |             \
                            REWRITE_VAR_COPY_VALUE |                             \
                            ((insert) ? REWRITE_VAR_INSERT : 0))
  
  /*
   * Deletes a var tree
   */
  LDAP_REWRITE_F(int)
  rewrite_var_delete(Avlnode* tree);
  
  /*
   * Contexts
   */
  
  /*
   * Finds the context named rewriteContext in the context tree
   */
  LDAP_REWRITE_F(struct rewrite_context*)
  rewrite_context_find(struct rewrite_info* info, const char* rewriteContext);
  
  /*
   * Creates a new context called rewriteContext and stores in into the tree
   */
  LDAP_REWRITE_F(struct rewrite_context*)
  rewrite_context_create(struct rewrite_info* info, const char* rewriteContext);
  
  /*
   * Rewrites string according to context; may return:
   *      OK:     fine; if *result != NULL rule matched and rewrite succeeded.
   *      STOP:   fine, rule matched; stop processing following rules
   *      UNWILL: rule matched; force 'unwilling to perform'
   */
  LDAP_REWRITE_F(int)
  rewrite_context_apply(struct rewrite_info* info, struct rewrite_op* op,
                        struct rewrite_context* context, const char* string,
                        char** result);
  
  LDAP_REWRITE_F(int)
  rewrite_context_destroy(struct rewrite_context** context);
  
  LDAP_REWRITE_F(void)
  rewrite_context_free(void* tmp);
  
  #endif /* REWRITE_INT_H */
  