From 374227b15ff7fbed8660beb93d52da15dcb4ba9e Mon Sep 17 00:00:00 2001
From: Victor Seva <linuxmaniac@torreviejawireless.org>
Date: Mon, 21 Aug 2023 12:27:43 +0200
Subject: [PATCH] dialplan: migrate to pcre2

---
 src/modules/dialplan/Makefile   |  11 +---
 src/modules/dialplan/dialplan.c |   5 ++
 src/modules/dialplan/dialplan.h |  20 ++++---
 src/modules/dialplan/dp_db.c    | 103 +++++++++++++++++++-------------
 src/modules/dialplan/dp_repl.c  |  56 +++++++++++------
 5 files changed, 121 insertions(+), 74 deletions(-)

--- a/src/modules/dialplan/Makefile
+++ b/src/modules/dialplan/Makefile
@@ -6,20 +6,15 @@ auto_gen=
 NAME=dialplan.so
 
 ifeq ($(CROSS_COMPILE),)
-PCRE_BUILDER = $(shell \
-	if pkg-config --exists libcre; then \
-		echo 'pkg-config libpcre'; \
-	else \
-		which pcre-config; \
-	fi)
+PCRE_BUILDER = $(shell command -v pcre2-config)
 endif
 
 ifeq ($(PCRE_BUILDER),)
 	PCREDEFS=-I$(LOCALBASE)/include
-	PCRELIBS=-L$(LOCALBASE)/lib -lpcre
+	PCRELIBS=-L$(LOCALBASE)/lib -lpcre2-8
 else
 	PCREDEFS = $(shell $(PCRE_BUILDER) --cflags)
-	PCRELIBS = $(shell $(PCRE_BUILDER) --libs)
+	PCRELIBS = $(shell $(PCRE_BUILDER) --libs8)
 endif
 DEFS+=$(PCREDEFS)
 LIBS=$(PCRELIBS)
--- a/src/modules/dialplan/dialplan.c
+++ b/src/modules/dialplan/dialplan.c
@@ -5,6 +5,8 @@
  *
  * Copyright (C)  2014 Olle E. Johansson, Edvina AB
  *
+ * Copyright (C)  2023 Victor Seva
+ *
  * This file is part of Kamailio, a free SIP server.
  *
  * Kamailio is free software; you can redistribute it and/or modify
@@ -79,6 +81,9 @@ static int ki_dp_translate_vars(
 int dp_replace_fixup(void **param, int param_no);
 int dp_replace_fixup_free(void **param, int param_no);
 
+pcre2_general_context *dpl_gctx = NULL;
+pcre2_compile_context *dpl_ctx = NULL;
+
 str dp_attr_pvar_s = STR_NULL;
 pv_spec_t *dp_attr_pvar = NULL;
 
--- a/src/modules/dialplan/dialplan.h
+++ b/src/modules/dialplan/dialplan.h
@@ -13,8 +13,8 @@
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
  *
- * You should have received a copy of the GNU General Public License 
- * along with this program; if not, write to the Free Software 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
  *
  */
@@ -30,7 +30,8 @@
 #ifndef _DP_DIALPLAN_H
 #define _DP_DIALPLAN_H
 
-#include <pcre.h>
+#define PCRE2_CODE_UNIT_WIDTH 8
+#include <pcre2.h>
 #include "../../core/pvar.h"
 #include "../../core/parser/msg_parser.h"
 
@@ -43,6 +44,9 @@
 #define DP_TFLAGS_PV_MATCH (1 << 0)
 #define DP_TFLAGS_PV_SUBST (1 << 1)
 
+extern pcre2_general_context *dpl_gctx;
+extern pcre2_compile_context *dpl_ctx;
+
 typedef struct dpl_node
 {
 	int dpid;					  /* dialplan id */
@@ -52,8 +56,8 @@ typedef struct dpl_node
 	str match_exp;				  /* match-first string */
 	str subst_exp;				  /* match string with subtitution groupping */
 	str repl_exp;				  /* replacement expression string */
-	pcre *match_comp;			  /* compiled matching expression */
-	pcre *subst_comp;			  /* compiled substitution expression */
+	pcre2_code *match_comp;		  /* compiled matching expression */
+	pcre2_code *subst_comp;		  /* compiled substitution expression */
 	struct subst_expr *repl_comp; /* compiled replacement */
 	str attrs;					  /* attributes string */
 	unsigned int tflags;		  /* flags for type of values for matching */
@@ -103,8 +107,8 @@ struct subst_expr *repl_exp_parse(str su
 void repl_expr_free(struct subst_expr *se);
 int dp_translate_helper(
 		sip_msg_t *msg, str *user_name, str *repl_user, dpl_id_p idp, str *);
-int rule_translate(
-		sip_msg_t *msg, str *instr, dpl_node_t *rule, pcre *subst_comp, str *);
+int rule_translate(sip_msg_t *msg, str *instr, dpl_node_t *rule,
+		pcre2_code *subst_comp, str *);
 
-pcre *reg_ex_comp(const char *pattern, int *cap_cnt, int mtype);
+pcre2_code *reg_ex_comp(const char *pattern, int *cap_cnt, int mtype);
 #endif
--- a/src/modules/dialplan/dp_db.c
+++ b/src/modules/dialplan/dp_db.c
@@ -196,11 +196,31 @@ void dp_disconnect_db(void)
 	}
 }
 
+static void *pcre2_malloc(size_t size, void *ext)
+{
+	return shm_malloc(size);
+}
+
+static void pcre2_free(void *ptr, void *ext)
+{
+	shm_free(ptr);
+	ptr = NULL;
+}
 
 int init_data(void)
 {
 	int *p;
 
+	if((dpl_gctx = pcre2_general_context_create(pcre2_malloc, pcre2_free, NULL))
+			== NULL) {
+		LM_ERR("pcre2 general context creation failed\n");
+		return -1;
+	}
+	if((dpl_ctx = pcre2_compile_context_create(dpl_gctx)) == NULL) {
+		LM_ERR("pcre2 compile context creation failed\n");
+		return -1;
+	}
+
 	dp_rules_hash = (dpl_id_p *)shm_malloc(2 * sizeof(dpl_id_p));
 	if(!dp_rules_hash) {
 		LM_ERR("out of shm memory\n");
@@ -227,6 +247,14 @@ int init_data(void)
 
 void destroy_data(void)
 {
+	if(dpl_ctx) {
+		pcre2_compile_context_free(dpl_ctx);
+	}
+
+	if(dpl_gctx) {
+		pcre2_general_context_free(dpl_gctx);
+	}
+
 	if(dp_rules_hash) {
 		destroy_hash(0);
 		destroy_hash(1);
@@ -373,55 +401,50 @@ int dpl_str_to_shm(str src, str *dest, i
 
 
 /* Compile pcre pattern
- * if mtype==0 - return pointer to shm copy of result
- * if mtype==1 - return pcre pointer that has to be pcre_free() */
-pcre *reg_ex_comp(const char *pattern, int *cap_cnt, int mtype)
-{
-	pcre *re, *result;
-	const char *error;
-	int rc, err_offset;
-	size_t size;
+ * if mtype==0 - return pointer using shm
+ * if mtype==1 - return pcre2_code pointer that has to be pcre2_code_free() */
+pcre2_code *reg_ex_comp(const char *pattern, int *cap_cnt, int mtype)
+{
+	pcre2_code *re;
+	int pcre_error_num = 0;
+	char pcre_error[128];
+	size_t pcre_erroffset;
+	int rc;
 
-	re = pcre_compile(pattern, 0, &error, &err_offset, NULL);
+	re = pcre2_compile((PCRE2_SPTR)pattern, PCRE2_ZERO_TERMINATED, 0,
+			&pcre_error_num, &pcre_erroffset, mtype == 0 ? dpl_ctx : NULL);
 	if(re == NULL) {
-		LM_ERR("PCRE compilation of '%s' failed at offset %d: %s\n", pattern,
-				err_offset, error);
-		return (pcre *)0;
-	}
-	rc = pcre_fullinfo(re, NULL, PCRE_INFO_SIZE, &size);
-	if(rc != 0) {
-		pcre_free(re);
-		LM_ERR("pcre_fullinfo on compiled pattern '%s' yielded error: %d\n",
-				pattern, rc);
-		return (pcre *)0;
+		switch(pcre2_get_error_message(
+				pcre_error_num, (PCRE2_UCHAR *)pcre_error, 128)) {
+			case PCRE2_ERROR_NOMEMORY:
+				snprintf(pcre_error, 128,
+						"unknown error[%d]: pcre2 error buffer too small",
+						pcre_error_num);
+				break;
+			case PCRE2_ERROR_BADDATA:
+				snprintf(pcre_error, 128, "unknown pcre2 error[%d]",
+						pcre_error_num);
+				break;
+		}
+		LM_ERR("PCRE compilation of '%s' failed at offset %zu: %s\n", pattern,
+				pcre_erroffset, pcre_error);
+		return NULL;
 	}
-	rc = pcre_fullinfo(re, NULL, PCRE_INFO_CAPTURECOUNT, cap_cnt);
+	rc = pcre2_pattern_info(re, PCRE2_INFO_CAPTURECOUNT, cap_cnt);
 	if(rc != 0) {
-		pcre_free(re);
+		pcre2_code_free(re);
 		LM_ERR("pcre_fullinfo on compiled pattern '%s' yielded error: %d\n",
 				pattern, rc);
-		return (pcre *)0;
-	}
-	if(mtype == 0) {
-		result = (pcre *)shm_malloc(size);
-		if(result == NULL) {
-			pcre_free(re);
-			LM_ERR("not enough shared memory for compiled PCRE pattern\n");
-			return (pcre *)0;
-		}
-		memcpy(result, re, size);
-		pcre_free(re);
-		return result;
-	} else {
-		return re;
+		return NULL;
 	}
+	return re;
 }
 
 
 /*compile the expressions, and if ok, build the rule */
 dpl_node_t *build_rule(db_val_t *values)
 {
-	pcre *match_comp, *subst_comp;
+	pcre2_code *match_comp, *subst_comp;
 	struct subst_expr *repl_comp;
 	dpl_node_t *new_rule;
 	str match_exp, subst_exp, repl_exp, attrs;
@@ -544,9 +567,9 @@ dpl_node_t *build_rule(db_val_t *values)
 
 err:
 	if(match_comp)
-		shm_free(match_comp);
+		pcre2_code_free(match_comp);
 	if(subst_comp)
-		shm_free(subst_comp);
+		pcre2_code_free(subst_comp);
 	if(repl_comp)
 		repl_expr_free(repl_comp);
 	if(new_rule)
@@ -692,10 +715,10 @@ void destroy_rule(dpl_node_t *rule)
 	LM_DBG("destroying rule with priority %i\n", rule->pr);
 
 	if(rule->match_comp)
-		shm_free(rule->match_comp);
+		pcre2_code_free(rule->match_comp);
 
 	if(rule->subst_comp)
-		shm_free(rule->subst_comp);
+		pcre2_code_free(rule->subst_comp);
 
 	/*destroy repl_exp*/
 	if(rule->repl_comp)
--- a/src/modules/dialplan/dp_repl.c
+++ b/src/modules/dialplan/dp_repl.c
@@ -15,8 +15,8 @@
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
  *
- * You should have received a copy of the GNU General Public License 
- * along with this program; if not, write to the Free Software 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
  *
  */
@@ -38,7 +38,7 @@
 
 typedef struct dpl_dyn_pcre
 {
-	pcre *re;
+	pcre2_code *re;
 	int cnt;
 	str expr;
 
@@ -186,9 +186,10 @@ int dpl_detect_avp_indx(const pv_elem_p
 	return 0;
 }
 
-pcre *dpl_dyn_pcre_comp(sip_msg_t *msg, str *expr, str *vexpr, int *cap_cnt)
+pcre2_code *dpl_dyn_pcre_comp(
+		sip_msg_t *msg, str *expr, str *vexpr, int *cap_cnt)
 {
-	pcre *re = NULL;
+	pcre2_code *re = NULL;
 	int ccnt = 0;
 
 	if(expr == NULL || expr->s == NULL || expr->len <= 0 || vexpr == NULL
@@ -225,7 +226,7 @@ dpl_dyn_pcre_p dpl_dynamic_pcre_list(sip
 	dpl_dyn_pcre_p rt = NULL;
 	struct str_list *l = NULL;
 	struct str_list *t = NULL;
-	pcre *re = NULL;
+	pcre2_code *re = NULL;
 	int cnt = 0;
 	str vexpr = STR_NULL;
 
@@ -294,7 +295,7 @@ error:
 	while(re_list) {
 		rt = re_list->next;
 		if(re_list->re)
-			pcre_free(re_list->re);
+			pcre2_code_free(re_list->re);
 		pkg_free(re_list);
 		re_list = rt;
 	}
@@ -400,15 +401,16 @@ error:
 #define MAX_PHONE_NB_DIGITS 127
 static char dp_output_buf[MAX_PHONE_NB_DIGITS + 1];
 int rule_translate(sip_msg_t *msg, str *instr, dpl_node_t *rule,
-		pcre *subst_comp, str *result)
+		pcre2_code *subst_comp, str *result)
 {
 	int repl_nb, offset, match_nb, rc, cap_cnt;
 	struct replace_with token;
 	struct subst_expr *repl_comp;
+	pcre2_match_data *pcre_md = NULL;
 	str match;
 	pv_value_t sv;
 	str *uri;
-	int ovector[3 * (MAX_REPLACE_WITH + 1)];
+	PCRE2_SIZE *ovector = NULL;
 	char *p;
 	int size;
 
@@ -424,7 +426,7 @@ int rule_translate(sip_msg_t *msg, str *
 
 	if(subst_comp) {
 		/*just in case something went wrong at load time*/
-		rc = pcre_fullinfo(subst_comp, NULL, PCRE_INFO_CAPTURECOUNT, &cap_cnt);
+		rc = pcre2_pattern_info(subst_comp, PCRE2_INFO_CAPTURECOUNT, &cap_cnt);
 		if(rc != 0) {
 			LM_ERR("pcre_fullinfo on compiled pattern yielded error: %d\n", rc);
 			return -1;
@@ -441,15 +443,19 @@ int rule_translate(sip_msg_t *msg, str *
 		}
 
 		/*search for the pattern from the compiled subst_exp*/
-		if(pcre_exec(subst_comp, NULL, instr->s, instr->len, 0, 0, ovector,
-				   3 * (MAX_REPLACE_WITH + 1))
+		pcre_md = pcre2_match_data_create_from_pattern(subst_comp, NULL);
+		if(pcre2_match(subst_comp, (PCRE2_SPTR)instr->s, (PCRE2_SIZE)instr->len,
+				   0, 0, pcre_md, NULL)
 				<= 0) {
 			LM_DBG("the string %.*s matched "
 				   "the match_exp %.*s but not the subst_exp %.*s!\n",
 					instr->len, instr->s, rule->match_exp.len,
 					rule->match_exp.s, rule->subst_exp.len, rule->subst_exp.s);
+			if(pcre_md)
+				pcre2_match_data_free(pcre_md);
 			return -1;
 		}
+		ovector = pcre2_get_ovector_pointer(pcre_md);
 	}
 
 	/*simply copy from the replacing string*/
@@ -463,6 +469,8 @@ int rule_translate(sip_msg_t *msg, str *
 		memcpy(result->s, repl_comp->replacement.s, repl_comp->replacement.len);
 		result->len = repl_comp->replacement.len;
 		result->s[result->len] = '\0';
+		if(pcre_md)
+			pcre2_match_data_free(pcre_md);
 		return 0;
 	}
 
@@ -571,11 +579,15 @@ int rule_translate(sip_msg_t *msg, str *
 	}
 
 	result->s[result->len] = '\0';
+	if(pcre_md)
+		pcre2_match_data_free(pcre_md);
 	return 0;
 
 error:
 	result->s = 0;
 	result->len = 0;
+	if(pcre_md)
+		pcre2_match_data_free(pcre_md);
 	return -1;
 }
 
@@ -584,6 +596,7 @@ static char dp_attrs_buf[DP_MAX_ATTRS_LE
 int dp_translate_helper(
 		sip_msg_t *msg, str *input, str *output, dpl_id_p idp, str *attrs)
 {
+	pcre2_match_data *pcre_md = NULL;
 	dpl_node_p rulep;
 	dpl_index_p indexp;
 	int user_len, rez;
@@ -624,21 +637,28 @@ search_rule:
 					rez = -1;
 					do {
 						if(rez < 0) {
-							rez = pcre_exec(re_list->re, NULL, input->s,
-									input->len, 0, 0, NULL, 0);
+							pcre_md = pcre2_match_data_create_from_pattern(
+									re_list->re, NULL);
+							rez = pcre2_match(re_list->re, (PCRE2_SPTR)input->s,
+									(PCRE2_SIZE)input->len, 0, 0, pcre_md,
+									NULL);
 							LM_DBG("match check: [%.*s] %d\n",
 									re_list->expr.len, re_list->expr.s, rez);
 						} else
 							LM_DBG("match check skipped: [%.*s] %d\n",
 									re_list->expr.len, re_list->expr.s, rez);
 						rt = re_list->next;
-						pcre_free(re_list->re);
+						pcre2_match_data_free(pcre_md);
+						pcre2_code_free(re_list->re);
 						pkg_free(re_list);
 						re_list = rt;
 					} while(re_list);
 				} else {
-					rez = pcre_exec(rulep->match_comp, NULL, input->s,
-							input->len, 0, 0, NULL, 0);
+					pcre_md = pcre2_match_data_create_from_pattern(
+							rulep->match_comp, NULL);
+					rez = pcre2_match(rulep->match_comp, (PCRE2_SPTR)input->s,
+							(PCRE2_SIZE)input->len, 0, 0, pcre_md, 0);
+					pcre2_match_data_free(pcre_md);
 				}
 				break;
 
@@ -728,7 +748,7 @@ repl:
 				LM_DBG("subst check skipped: [%.*s] %d\n", re_list->expr.len,
 						re_list->expr.s, rez);
 			rt = re_list->next;
-			pcre_free(re_list->re);
+			pcre2_code_free(re_list->re);
 			pkg_free(re_list);
 			re_list = rt;
 		} while(re_list);
