From: Sipwise Development Team Date: Mon, 21 Feb 2022 14:17:23 +0100 Subject: sipwise_vm_add_pcre_support --- Makefile | 4 +- apps/app_voicemail.c | 175 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 178 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 9ee80e1..69eccd0 100644 --- a/Makefile +++ b/Makefile @@ -12,10 +12,11 @@ MODULES_DIR=$(INSTALL_PREFIX)$(ASTLIBDIR) ASTETCDIR=$(INSTALL_PREFIX)/etc/asterisk CC?=gcc +PKG_CONFIG?=pkg-config OPTIMIZE=-O2 DEBUG=-g -LIBS+= +LIBS+=$(shell $(PKG_CONFIG) --libs libpcre2-8) CFLAGS+=-pipe -fPIC CFLAGS+=-Wall -Wextra -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations CFLAGS+=-Wno-format-truncation @@ -24,6 +25,7 @@ CFLAGS+=-Wno-sign-compare CFLAGS+=-Wno-unused-parameter CFLAGS+=-Wno-unused-variable CFLAGS+=-D_REENTRANT -D_GNU_SOURCE -DODBC_STORAGE +CFLAGS+=$(shell $(PKG_CONFIG) --cflags libpcre2-8) -DPCRE2_CODE_UNIT_WIDTH=8 all: _all @echo " +-------- app_voicemail Build Complete --------+" diff --git a/apps/app_voicemail.c b/apps/app_voicemail.c index 7667ae1..e9d5ec3 100644 --- a/apps/app_voicemail.c +++ b/apps/app_voicemail.c @@ -112,6 +112,9 @@ #include "asterisk/threadstorage.h" #endif +#include +#include + /*** DOCUMENTATION @@ -1081,6 +1084,8 @@ static int maxlogins = 3; static int minpassword = MINPASSWORD; static int passwordlocation; static char aliasescontext[MAX_VM_CONTEXT_LEN]; +static char sw_normalize_user_match[256]; +static char sw_normalize_user_replace[256]; /*! Poll mailboxes for changes since there is something external to * app_voicemail that may change them. */ @@ -1317,6 +1322,134 @@ static int inprocess_count(const char *context, const char *mailbox, int delta) static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit); #endif +/* Sipwise PCRE2 helper functions adapted from contrib of pcre: + + Written by: Bert Driehuis + Copyright (c) 2000 Bert Driehuis + + Permission is granted to anyone to use this software for any purpose on any + computer system, and to redistribute it freely, subject to the following + restrictions: + + 1. This software is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + 2. The origin of this software must not be misrepresented, either by + explicit claim or by omission. + + 3. Altered versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 4. If PCRE is embedded in any software that is released under the GNU + General Purpose Licence (GPL), then the terms of that licence shall + supersede any condition above with which it is incompatible. +*/ +#define MAXCAPTURE 50 + +static int findreplen(const char *rep, int nmat, const int *replen) +{ + int len = 0; + int val; + char *cp = (char *)rep; + + while (*cp) { + if (*cp == '$' && isdigit(cp[1])) { + val = strtoul(&cp[1], &cp, 10); + if (val && val <= nmat) + len += replen[val -1]; + else + fprintf(stderr, "repl %d out of range\n", val); + } else { + cp++; + len++; + } + } + + return len; +} + +static void doreplace(char *out, const char *rep, + int nmat, int *replen, const char **repstr) +{ + int val; + char *cp = (char *)rep; + + while (*cp) { + if (*cp == '$' && isdigit(cp[1])) { + val = strtoul(&cp[1], &cp, 10); + if (val && val <= nmat) { + strncpy(out, repstr[val - 1], replen[val - 1]); + out += replen[val -1]; + } + } else { + *out++ = *cp++; + } + } +} + +static char *edit(const char *str, int len, const char *rep, + int nmat, const size_t *ovec) +{ + int i, slen, rlen; + const size_t *mvec = ovec; + char *res, *cp; + int replen[MAXCAPTURE]; + const char *repstr[MAXCAPTURE]; + + nmat--; + ovec += 2; + for (i = 0; i < nmat; i++) { + replen[i] = ovec[i * 2 + 1] - ovec[i * 2]; + repstr[i] = &str[ovec[i * 2]]; + } + slen = len; + len -= mvec[1] - mvec[0]; + len += rlen = findreplen(rep, nmat, replen); + cp = res = ast_malloc(len + 1); + if (mvec[0] > 0) { + strncpy(cp, str, mvec[0]); + cp += mvec[0]; + } + doreplace(cp, rep, nmat, replen, repstr); + cp += rlen; + if (mvec[1] < slen) + strcpy(cp, &str[mvec[1]]); + res[len] = 0; + + return res; +} + +static char *pcre_subst(const pcre2_code *ppat, + const char *str, int len, + int offset, int options, const char *rep) +{ + int nmat; + pcre2_match_data *md; + size_t *ovec; + + md = pcre2_match_data_create(MAXCAPTURE, NULL); + if (!md) + return NULL; + + nmat = pcre2_match(ppat, (PCRE2_SPTR8)str, len, offset, options, + md, NULL); + if (nmat <= 0) { + pcre2_match_data_free(md); + return NULL; + } + + ovec = pcre2_get_ovector_pointer(md); + pcre2_match_data_free(md); + + if (!ovec) + return NULL; + + return edit(str, len, rep, nmat, ovec); +} + +/* end of pcre helper functions */ + /*! * \brief Strips control and non 7-bit clean characters from input string. * @@ -11317,6 +11450,10 @@ static int vm_authenticate(struct ast_channel *chan, char *mailbox, int mailbox_ int useadsi = 0, valid = 0, logretries = 0; char password[AST_MAX_EXTENSION], *passptr = NULL; struct ast_vm_user vmus, *vmu = NULL; + int errcode; + size_t erroffset; + pcre2_code *ppat = NULL; + char *normalized_mailbox; /* If ADSI is supported, setup login screen */ adsi_begin(chan, &useadsi); @@ -11327,6 +11464,17 @@ static int vm_authenticate(struct ast_channel *chan, char *mailbox, int mailbox_ return -1; } + if (sw_normalize_user_match[0] != '\0' && + sw_normalize_user_replace[0] != '\0') { + // TODO: could be done once on start + ppat = pcre2_compile((PCRE2_SPTR8)sw_normalize_user_match, PCRE2_ZERO_TERMINATED, 0, &errcode, &erroffset, NULL); + if (ppat == NULL) { + ast_log(LOG_WARNING, "Couldn't compile user match regex '%s': %d at offset %zd\n", + sw_normalize_user_match, errcode, erroffset); + return -1; + } + } + /* Authenticate them and get their mailbox/password */ while (!valid && (logretries < max_logins)) { @@ -11356,6 +11504,18 @@ static int vm_authenticate(struct ast_channel *chan, char *mailbox, int mailbox_ if (useadsi) adsi_password(chan); + if (ppat) { + ast_log(LOG_NOTICE, "Trying to rewrite user input '%s' using s/%s/%s/\n", + mailbox, sw_normalize_user_match, sw_normalize_user_replace); + normalized_mailbox = pcre_subst(ppat, mailbox, strlen(mailbox), 0, 0, sw_normalize_user_replace); + if(normalized_mailbox) { + ast_log(LOG_NOTICE, "Rewrote mailbox user input '%s' to %s\n", + mailbox, normalized_mailbox); + ast_copy_string(mailbox, normalized_mailbox, mailbox_size); + ast_free(normalized_mailbox); + } + } + if (!ast_strlen_zero(prefix)) { char fullusername[80]; @@ -14349,6 +14509,8 @@ static int actual_load_config(int reload, struct ast_config *cfg, struct ast_con char secretfn[PATH_MAX] = ""; long tps_queue_low; long tps_queue_high; + const char *ast_sw_normalize_user_match = NULL; + const char *ast_sw_normalize_user_replace = NULL; #ifdef IMAP_STORAGE ast_copy_string(imapparentfolder, "\0", sizeof(imapparentfolder)); @@ -14421,6 +14583,19 @@ static int actual_load_config(int reload, struct ast_config *cfg, struct ast_con ast_set2_flag((&globalflags), ast_true(val), VM_ODBC_AUDIO_ON_DISK); #endif + + /* sipwise sw_normalize_user_match/replace */ + if ((ast_sw_normalize_user_match = ast_variable_retrieve(cfg, "general", "sw_normalize_user_match"))) { + ast_copy_string(sw_normalize_user_match, ast_sw_normalize_user_match, sizeof(sw_normalize_user_match)); + } else { + sw_normalize_user_match[0] = '\0'; + } + if ((ast_sw_normalize_user_replace = ast_variable_retrieve(cfg, "general", "sw_normalize_user_replace"))) { + ast_copy_string(sw_normalize_user_replace, ast_sw_normalize_user_replace, sizeof(sw_normalize_user_replace)); + } else { + sw_normalize_user_replace[0] = '\0'; + } + /* Mail command */ strcpy(mailcmd, SENDMAIL); if ((val = ast_variable_retrieve(cfg, "general", "mailcmd")))