/* ====================================================================
* The Kannel Software License, Version 1.0
*
* Copyright (c) 2001-2005 Kannel Group
* Copyright (c) 1998-2001 WapIT Ltd.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Kannel Group (http://www.kannel.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Kannel" and "Kannel Group" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For written permission, please
* contact org@kannel.org.
*
* 5. Products derived from this software may not be called "Kannel",
* nor may "Kannel" appear in their name, without prior written
* permission of the Kannel Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE KANNEL GROUP OR ITS CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Kannel Group. For more information on
* the Kannel Group, please see .
*
* Portions of this software are based upon software originally written at
* WapIT Ltd., Helsinki, Finland for the Kannel project.
*/
/*
* urltrans.c - URL translations
*
* Lars Wirzenius
*/
#include
#include
#include
#include
#include
#include
#include "urltrans.h"
#include "gwlib/gwlib.h"
#include "gw/sms.h"
#include "gwlib/regex.h"
/***********************************************************************
* Definitions of data structures. These are not visible to the external
* world -- they may be accessed only via the functions declared in
* urltrans.h.
*/
/*
* Hold one keyword/options entity
*/
struct URLTranslation {
Octstr *keyword; /* keyword in SMS (similar) query */
List *aliases; /* keyword aliases, List of Octstr */
int type; /* see enumeration in header file */
Octstr *pattern; /* url, text or file-name pattern */
Octstr *prefix; /* for prefix-cut */
Octstr *suffix; /* for suffix-cut */
Octstr *faked_sender;/* works only with certain services */
Octstr *default_sender;/* Default sender to sendsms-user */
long max_messages; /* absolute limit of reply messages */
int concatenation; /* send long messages as concatenated SMS's if true */
Octstr *split_chars;/* allowed chars to be used to split message */
Octstr *split_suffix;/* chars added to end after each split (not last) */
int omit_empty; /* if the reply is empty, is notification send */
Octstr *header; /* string to be inserted to each SMS */
Octstr *footer; /* string to be appended to each SMS */
List *accepted_smsc; /* smsc id's allowed to use this service. If not set,
all messages can use this service */
Octstr *name; /* Translation name */
Octstr *username; /* send sms username */
Octstr *password; /* password associated */
Octstr *forced_smsc;/* if smsc id is forcet to certain for this user */
Octstr *default_smsc; /* smsc id if none given in http send-sms request */
Octstr *allow_ip; /* allowed IPs to request send-sms with this
account */
Octstr *deny_ip; /* denied IPs to request send-sms with this account */
Octstr *allowed_prefix; /* Prefixes (of sender) allowed in this translation, or... */
Octstr *denied_prefix; /* ...denied prefixes */
Octstr *allowed_recv_prefix; /* Prefixes (of receiver) allowed in this translation, or... */
Octstr *denied_recv_prefix; /* ...denied prefixes */
Numhash *white_list; /* To numbers allowed, or ... */
Numhash *black_list; /* ...denied numbers */
int assume_plain_text; /* for type: octet-stream */
int accept_x_kannel_headers; /* do we accept special headers in reply */
int strip_keyword; /* POST body */
int send_sender; /* POST headers */
int args;
int has_catchall_arg;
int catch_all;
Octstr *dlr_url; /* Url to call for delivery reports */
regex_t *keyword_regex; /* the compiled regular expression for the keyword*/
regex_t *accepted_smsc_regex;
regex_t *allowed_prefix_regex;
regex_t *denied_prefix_regex;
regex_t *allowed_receiver_prefix_regex;
regex_t *denied_receiver_prefix_regex;
regex_t *white_list_regex;
regex_t *black_list_regex;
};
/*
* Hold the list of all translations.
*/
struct URLTranslationList {
List *list;
Dict *dict; /* Dict of lowercase Octstr keywords*/
Dict *names; /* Dict of lowercase Octstr names */
};
/***********************************************************************
* Declarations of internal functions. These are defined at the end of
* the file.
*/
static long count_occurences(Octstr *str, Octstr *pat);
static URLTranslation *create_onetrans(CfgGroup *grp);
static void destroy_onetrans(void *ot);
static URLTranslation *find_translation(URLTranslationList *trans,
List *words, Octstr *smsc,
Octstr *sender, Octstr *receiver, int *reject);
static URLTranslation *find_default_translation(URLTranslationList *trans,
Octstr *smsc, Octstr *sender, Octstr *receiver,
int *reject);
static URLTranslation *find_black_list_translation(URLTranslationList *trans,
Octstr *smsc);
/***********************************************************************
* Implementations of the functions declared in urltrans.h. See the
* header for explanations of what they should do.
*/
static void destroy_keyword_list(void *list)
{
gwlist_destroy(list, NULL);
}
URLTranslationList *urltrans_create(void)
{
URLTranslationList *trans;
trans = gw_malloc(sizeof(URLTranslationList));
trans->list = gwlist_create();
trans->dict = dict_create(1024, destroy_keyword_list);
trans->names = dict_create(1024, destroy_keyword_list);
return trans;
}
void urltrans_destroy(URLTranslationList *trans)
{
gwlist_destroy(trans->list, destroy_onetrans);
dict_destroy(trans->names);
dict_destroy(trans->dict);
gw_free(trans);
}
int urltrans_add_one(URLTranslationList *trans, CfgGroup *grp)
{
URLTranslation *ot;
long i;
List *list, *list2;
Octstr *alias;
ot = create_onetrans(grp);
if (ot == NULL)
return -1;
gwlist_append(trans->list, ot);
list2 = dict_get(trans->names, ot->name);
if (list2 == NULL) {
list2 = gwlist_create();
dict_put(trans->names, ot->name, list2);
}
gwlist_append(list2, ot);
if (ot->keyword == NULL || ot->type == TRANSTYPE_SENDSMS)
return 0;
list = dict_get(trans->dict, ot->keyword);
if (list == NULL) {
list = gwlist_create();
dict_put(trans->dict, ot->keyword, list);
}
gwlist_append(list, ot);
for (i = 0; i < gwlist_len(ot->aliases); ++i) {
alias = gwlist_get(ot->aliases, i);
list = dict_get(trans->dict, alias);
if (list == NULL) {
list = gwlist_create();
dict_put(trans->dict, alias, list);
}
gwlist_append(list, ot);
}
return 0;
}
int urltrans_add_cfg(URLTranslationList *trans, Cfg *cfg)
{
CfgGroup *grp;
List *list;
list = cfg_get_multi_group(cfg, octstr_imm("sms-service"));
while (list && (grp = gwlist_extract_first(list)) != NULL) {
if (urltrans_add_one(trans, grp) == -1) {
gwlist_destroy(list, NULL);
return -1;
}
}
gwlist_destroy(list, NULL);
list = cfg_get_multi_group(cfg, octstr_imm("sendsms-user"));
while (list && (grp = gwlist_extract_first(list)) != NULL) {
if (urltrans_add_one(trans, grp) == -1) {
gwlist_destroy(list, NULL);
return -1;
}
}
gwlist_destroy(list, NULL);
return 0;
}
URLTranslation *urltrans_find(URLTranslationList *trans, Octstr *text,
Octstr *smsc, Octstr *sender, Octstr *receiver)
{
List *words;
URLTranslation *t = NULL;
int reject = 0;
/* do not panic if text == NULL */
if (text != NULL) {
words = octstr_split_words(text);
t = find_translation(trans, words, smsc, sender, receiver, &reject);
gwlist_destroy(words, octstr_destroy_item);
}
if (reject)
t = find_black_list_translation(trans, smsc);
if (t == NULL) {
t = find_default_translation(trans, smsc, sender, receiver, &reject);
if (reject)
t = find_black_list_translation(trans, smsc);
}
return t;
}
URLTranslation *urltrans_find_service(URLTranslationList *trans, Msg *msg)
{
URLTranslation *t;
List *list;
list = dict_get(trans->names, msg->sms.service);
if (list != NULL) {
t = gwlist_get(list, 0);
} else {
t = NULL;
}
return t;
}
URLTranslation *urltrans_find_username(URLTranslationList *trans,
Octstr *name)
{
URLTranslation *t;
int i;
gw_assert(name != NULL);
for (i = 0; i < gwlist_len(trans->list); ++i) {
t = gwlist_get(trans->list, i);
if (t->type == TRANSTYPE_SENDSMS) {
if (octstr_compare(name, t->username) == 0)
return t;
}
}
return NULL;
}
/*
* Remove the first word and the whitespace that follows it from
* the start of the message data.
*/
static void strip_keyword(Msg *request)
{
int ch;
long pos;
pos = 0;
for (; (ch = octstr_get_char(request->sms.msgdata, pos)) >= 0; pos++)
if (isspace(ch))
break;
for (; (ch = octstr_get_char(request->sms.msgdata, pos)) >= 0; pos++)
if (!isspace(ch))
break;
octstr_delete(request->sms.msgdata, 0, pos);
}
/*
* Trans being NULL means that we are servicing ppg (doing dlr, but this does not
* concern us here).
*/
Octstr *urltrans_get_pattern(URLTranslation *t, Msg *request)
{
Octstr *enc;
int nextarg, j;
struct tm tm;
int num_words;
List *word_list;
Octstr *result, *pattern;
long pattern_len;
long pos;
int c;
long i;
Octstr *temp;
Octstr *url, *reply; /* For and If delivery report */
url = reply = NULL;
if (request->sms.sms_type != report_mo && t->type == TRANSTYPE_SENDSMS)
return octstr_create("");
if (request->sms.msgdata) {
word_list = octstr_split_words(request->sms.msgdata);
num_words = gwlist_len(word_list);
} else {
word_list = gwlist_create();
num_words = 0;
}
result = octstr_create("");
/* check if this is a delivery report message or not */
if (request->sms.sms_type != report_mo) {
pattern = t->pattern;
} else {
/* this is a DLR message */
reply = octstr_duplicate(request->sms.msgdata);
url = octstr_duplicate(request->sms.dlr_url);
pattern = url;
if (octstr_len(pattern) == 0) {
if (t && octstr_len(t->dlr_url)) {
pattern = t->dlr_url;
} else {
gwlist_destroy(word_list, octstr_destroy_item);
return octstr_create("");
}
}
}
pattern_len = octstr_len(pattern);
nextarg = 1;
pos = 0;
for (;;) {
while (pos < pattern_len) {
c = octstr_get_char(pattern, pos);
if (c == '%' && pos + 1 < pattern_len)
break;
octstr_append_char(result, c);
++pos;
}
if (pos == pattern_len)
break;
switch (octstr_get_char(pattern, pos + 1)) {
case 'k':
if (num_words <= 0)
break;
enc = octstr_duplicate(gwlist_get(word_list, 0));
octstr_url_encode(enc);
octstr_append(result, enc);
octstr_destroy(enc);
break;
case 's':
if (nextarg >= num_words)
break;
enc = octstr_duplicate(gwlist_get(word_list, nextarg));
octstr_url_encode(enc);
octstr_append(result, enc);
octstr_destroy(enc);
++nextarg;
break;
case 'S':
if (nextarg >= num_words)
break;
temp = gwlist_get(word_list, nextarg);
for (i = 0; i < octstr_len(temp); ++i) {
if (octstr_get_char(temp, i) == '*')
octstr_append_char(result, '~');
else
octstr_append_char(result, octstr_get_char(temp, i));
}
++nextarg;
break;
case 'r':
for (j = nextarg; j < num_words; ++j) {
enc = octstr_duplicate(gwlist_get(word_list, j));
octstr_url_encode(enc);
if (j != nextarg)
octstr_append_char(result, '+');
octstr_append(result, enc);
octstr_destroy(enc);
}
break;
/* NOTE: the sender and receiver is already switched in
* message, so that's why we must use 'sender' when
* we want original receiver and vice versa
*/
case 'P':
enc = octstr_duplicate(request->sms.sender);
octstr_url_encode(enc);
octstr_append(result, enc);
octstr_destroy(enc);
break;
case 'p':
enc = octstr_duplicate(request->sms.receiver);
octstr_url_encode(enc);
octstr_append(result, enc);
octstr_destroy(enc);
break;
case 'Q':
if (strncmp(octstr_get_cstr(request->sms.sender), "00", 2) == 0) {
enc = octstr_copy(request->sms.sender, 2,
octstr_len(request->sms.sender));
octstr_url_encode(enc);
octstr_format_append(result, "%%2B%S", enc);
octstr_destroy(enc);
} else {
enc = octstr_duplicate(request->sms.sender);
octstr_url_encode(enc);
octstr_append(result, enc);
octstr_destroy(enc);
}
break;
case 'q':
if (strncmp(octstr_get_cstr(request->sms.receiver),"00",2)==0) {
enc = octstr_copy(request->sms.receiver, 2,
octstr_len(request->sms.receiver));
octstr_url_encode(enc);
octstr_format_append(result, "%%2B%S", enc);
octstr_destroy(enc);
} else {
enc = octstr_duplicate(request->sms.receiver);
octstr_url_encode(enc);
octstr_append(result, enc);
octstr_destroy(enc);
}
break;
case 'a':
for (j = 0; j < num_words; ++j) {
enc = octstr_duplicate(gwlist_get(word_list, j));
octstr_url_encode(enc);
if (j > 0)
octstr_append_char(result, '+');
octstr_append(result, enc);
octstr_destroy(enc);
}
break;
case 'b':
enc = octstr_duplicate(request->sms.msgdata);
octstr_url_encode(enc);
octstr_append(result, enc);
octstr_destroy(enc);
break;
case 't':
tm = gw_gmtime(request->sms.time);
octstr_format_append(result, "%04d-%02d-%02d+%02d:%02d:%02d",
tm.tm_year + 1900,
tm.tm_mon + 1,
tm.tm_mday,
tm.tm_hour,
tm.tm_min,
tm.tm_sec);
break;
case 'T':
if (request->sms.time == MSG_PARAM_UNDEFINED)
break;
octstr_format_append(result, "%ld", request->sms.time);
break;
case 'i':
if (request->sms.smsc_id == NULL)
break;
enc = octstr_duplicate(request->sms.smsc_id);
octstr_url_encode(enc);
octstr_append(result, enc);
octstr_destroy(enc);
break;
case 'I':
if (!uuid_is_null(request->sms.id)) {
char id[UUID_STR_LEN + 1];
uuid_unparse(request->sms.id, id);
octstr_append_cstr(result, id);
}
break;
case 'n':
if (request->sms.service == NULL)
break;
enc = octstr_duplicate(request->sms.service);
octstr_url_encode(enc);
octstr_append(result, enc);
octstr_destroy(enc);
break;
case 'd':
enc = octstr_create("");
octstr_append_decimal(enc, request->sms.dlr_mask);
octstr_url_encode(enc);
octstr_append(result, enc);
octstr_destroy(enc);
break;
case 'A':
if (reply) {
enc = octstr_duplicate(reply);
octstr_url_encode(enc);
octstr_append(result, enc);
octstr_destroy(enc);
}
break;
case 'c':
octstr_append_decimal(result, request->sms.coding);
break;
case 'C':
if(octstr_len(request->sms.charset)) {
octstr_append(result, request->sms.charset);
} else {
switch (request->sms.coding) {
case DC_UNDEF:
case DC_7BIT:
octstr_append(result, octstr_imm("ISO-8859-1"));
break;
case DC_8BIT:
octstr_append(result, octstr_imm("8-BIT"));
break;
case DC_UCS2:
octstr_append(result, octstr_imm("UTF-16BE"));
break;
}
}
break;
case 'u':
if(octstr_len(request->sms.udhdata)) {
enc = octstr_duplicate(request->sms.udhdata);
octstr_url_encode(enc);
octstr_append(result, enc);
octstr_destroy(enc);
}
break;
case 'B': /* billing identifier/information */
if (octstr_len(request->sms.binfo)) {
enc = octstr_duplicate(request->sms.binfo);
octstr_url_encode(enc);
octstr_append(result, enc);
octstr_destroy(enc);
}
break;
case 'o': /* account information (may be operator id for aggregators */
if (octstr_len(request->sms.account)) {
enc = octstr_duplicate(request->sms.account);
octstr_url_encode(enc);
octstr_append(result, enc);
octstr_destroy(enc);
}
break;
case 'm': /* mclass - message class */
enc = octstr_create("");
octstr_append_decimal(enc, request->sms.mclass);
octstr_url_encode(enc);
octstr_append(result, enc);
octstr_destroy(enc);
break;
case 'M': /* mwi - message waiting indicator */
enc = octstr_create("");
octstr_append_decimal(enc, request->sms.mwi);
octstr_url_encode(enc);
octstr_append(result, enc);
octstr_destroy(enc);
break;
case 'f': /* smsc number*/
if (octstr_len(request->sms.smsc_number)) {
enc = octstr_duplicate(request->sms.smsc_number);
octstr_url_encode(enc);
octstr_append(result, enc);
octstr_destroy(enc);
}
break;
/* XXX sms.parameters not present in here:
* * pid - will we receive this ?
* * alt-dcs - shouldn't be required unless we want to inform
* which alt-dcs external server should use back
* * compress - if we use compression, probably kannel would
* decompress and reset this to 0. not required
* * validity, deferred, rpi - we don't receive these from smsc
* * username, password, dlr-url, account - nonsense to send
*/
case '%':
octstr_format_append(result, "%%");
break;
default:
octstr_format_append(result, "%%%c",
octstr_get_char(pattern, pos + 1));
break;
}
pos += 2;
}
/*
* this SHOULD be done in smsbox, not here, but well,
* much easier to do here
*/
if (t && (t->type == TRANSTYPE_POST_URL || t->type == TRANSTYPE_POST_XML)
&& t->strip_keyword)
strip_keyword(request);
octstr_destroy(url);
octstr_destroy(reply);
gwlist_destroy(word_list, octstr_destroy_item);
return result;
}
int urltrans_type(URLTranslation *t)
{
return t->type;
}
Octstr *urltrans_prefix(URLTranslation *t)
{
return t->prefix;
}
Octstr *urltrans_suffix(URLTranslation *t)
{
return t->suffix;
}
Octstr *urltrans_default_sender(URLTranslation *t)
{
return t->default_sender;
}
Octstr *urltrans_faked_sender(URLTranslation *t)
{
return t->faked_sender;
}
int urltrans_max_messages(URLTranslation *t)
{
return t->max_messages;
}
int urltrans_concatenation(URLTranslation *t)
{
return t->concatenation;
}
Octstr *urltrans_split_chars(URLTranslation *t)
{
return t->split_chars;
}
Octstr *urltrans_split_suffix(URLTranslation *t)
{
return t->split_suffix;
}
int urltrans_omit_empty(URLTranslation *t)
{
return t->omit_empty;
}
Octstr *urltrans_header(URLTranslation *t)
{
return t->header;
}
Octstr *urltrans_footer(URLTranslation *t)
{
return t->footer;
}
Octstr *urltrans_name(URLTranslation *t)
{
return t->name;
}
Octstr *urltrans_username(URLTranslation *t)
{
return t->username;
}
Octstr *urltrans_password(URLTranslation *t)
{
return t->password;
}
Octstr *urltrans_forced_smsc(URLTranslation *t)
{
return t->forced_smsc;
}
Octstr *urltrans_default_smsc(URLTranslation *t)
{
return t->default_smsc;
}
Octstr *urltrans_allow_ip(URLTranslation *t)
{
return t->allow_ip;
}
Octstr *urltrans_deny_ip(URLTranslation *t)
{
return t->deny_ip;
}
Octstr *urltrans_allowed_prefix(URLTranslation *t)
{
return t->allowed_prefix;
}
Octstr *urltrans_denied_prefix(URLTranslation *t)
{
return t->denied_prefix;
}
Octstr *urltrans_allowed_recv_prefix(URLTranslation *t)
{
return t->allowed_recv_prefix;
}
Octstr *urltrans_denied_recv_prefix(URLTranslation *t)
{
return t->denied_recv_prefix;
}
Numhash *urltrans_white_list(URLTranslation *t)
{
return t->white_list;
}
regex_t *urltrans_white_list_regex(URLTranslation *t)
{
return t->white_list_regex;
}
Numhash *urltrans_black_list(URLTranslation *t)
{
return t->black_list;
}
regex_t *urltrans_black_list_regex(URLTranslation *t)
{
return t->black_list_regex;
}
int urltrans_assume_plain_text(URLTranslation *t)
{
return t->assume_plain_text;
}
int urltrans_accept_x_kannel_headers(URLTranslation *t)
{
return t->accept_x_kannel_headers;
}
int urltrans_strip_keyword(URLTranslation *t)
{
return t->strip_keyword;
}
int urltrans_send_sender(URLTranslation *t)
{
return t->send_sender;
}
/***********************************************************************
* Internal functions.
*/
/*
* Create one URLTranslation. Return NULL for failure, pointer to it for OK.
*/
static URLTranslation *create_onetrans(CfgGroup *grp)
{
URLTranslation *ot;
Octstr *aliases, *url, *post_url, *post_xml, *text, *file, *exec;
Octstr *accepted_smsc, *forced_smsc, *default_smsc;
Octstr *grpname, *sendsms_user, *sms_service;
int is_sms_service;
Octstr *accepted_smsc_regex;
Octstr *allowed_prefix_regex;
Octstr *denied_prefix_regex;
Octstr *allowed_receiver_prefix_regex;
Octstr *denied_receiver_prefix_regex;
Octstr *white_list_regex;
Octstr *black_list_regex;
Octstr *keyword_regex;
Octstr *os;
grpname = cfg_get_group_name(grp);
if (grpname == NULL)
return NULL;
sms_service = octstr_imm("sms-service");
sendsms_user = octstr_imm("sendsms-user");
if (octstr_compare(grpname, sms_service) == 0)
is_sms_service = 1;
else if (octstr_compare(grpname, sendsms_user) == 0)
is_sms_service = 0;
else {
octstr_destroy(grpname);
return NULL;
}
octstr_destroy(grpname);
ot = gw_malloc(sizeof(URLTranslation));
ot->keyword = NULL;
ot->aliases = NULL;
ot->pattern = NULL;
ot->prefix = NULL;
ot->suffix = NULL;
ot->faked_sender = NULL;
ot->default_sender = NULL;
ot->split_chars = NULL;
ot->split_suffix = NULL;
ot->footer = NULL;
ot->header = NULL;
ot->name = NULL;
ot->username = NULL;
ot->password = NULL;
ot->omit_empty = 0;
ot->accepted_smsc = NULL;
ot->forced_smsc = NULL;
ot->default_smsc = NULL;
ot->allow_ip = NULL;
ot->deny_ip = NULL;
ot->allowed_prefix = NULL;
ot->denied_prefix = NULL;
ot->allowed_recv_prefix = NULL;
ot->denied_recv_prefix = NULL;
ot->white_list = NULL;
ot->black_list = NULL;
ot->keyword_regex = NULL;
ot->accepted_smsc_regex = NULL;
ot->allowed_prefix_regex = NULL;
ot->denied_prefix_regex = NULL;
ot->allowed_receiver_prefix_regex = NULL;
ot->denied_receiver_prefix_regex = NULL;
ot->white_list_regex = NULL;
ot->black_list_regex = NULL;
if (is_sms_service) {
cfg_get_bool(&ot->catch_all, grp, octstr_imm("catch-all"));
ot->dlr_url = cfg_get(grp, octstr_imm("dlr-url"));
url = cfg_get(grp, octstr_imm("get-url"));
if (url == NULL)
url = cfg_get(grp, octstr_imm("url"));
post_url = cfg_get(grp, octstr_imm("post-url"));
post_xml = cfg_get(grp, octstr_imm("post-xml"));
file = cfg_get(grp, octstr_imm("file"));
text = cfg_get(grp, octstr_imm("text"));
exec = cfg_get(grp, octstr_imm("exec"));
if (url != NULL) {
ot->type = TRANSTYPE_GET_URL;
ot->pattern = url;
} else if (post_url != NULL) {
ot->type = TRANSTYPE_POST_URL;
ot->pattern = post_url;
ot->catch_all = 1;
} else if (post_xml != NULL) {
ot->type = TRANSTYPE_POST_XML;
ot->pattern = post_xml;
ot->catch_all = 1;
} else if (file != NULL) {
ot->type = TRANSTYPE_FILE;
ot->pattern = file;
} else if (text != NULL) {
ot->type = TRANSTYPE_TEXT;
ot->pattern = text;
} else if (exec != NULL) {
ot->type = TRANSTYPE_EXECUTE;
ot->pattern = exec;
} else {
error(0, "Configuration group `sms-service' "
"did not specify get-url, post-url, post-xml, file or text.");
goto error;
}
ot->keyword = cfg_get(grp, octstr_imm("keyword"));
if (ot->keyword == NULL) {
error(0, "Group 'sms-service' must include 'keyword'.");
goto error;
}
octstr_convert_range(ot->keyword, 0, octstr_len(ot->keyword),
tolower);
keyword_regex = cfg_get(grp, octstr_imm("keyword-regex"));
if (keyword_regex != NULL) {
if ((ot->keyword_regex = gw_regex_comp(keyword_regex, REG_EXTENDED)) == NULL)
panic(0, "Could not compile pattern '%s'", octstr_get_cstr(keyword_regex));
octstr_destroy(keyword_regex);
}
ot->name = cfg_get(grp, octstr_imm("name"));
if (ot->name == NULL)
ot->name = octstr_duplicate(ot->keyword);
aliases = cfg_get(grp, octstr_imm("aliases"));
if (aliases == NULL)
ot->aliases = gwlist_create();
else {
long i;
Octstr *os;
ot->aliases = octstr_split(aliases, octstr_imm(";"));
octstr_destroy(aliases);
for (i = 0; i < gwlist_len(ot->aliases); ++i) {
os = gwlist_get(ot->aliases, i);
octstr_convert_range(os, 0, octstr_len(os), tolower);
}
}
accepted_smsc = cfg_get(grp, octstr_imm("accepted-smsc"));
if (accepted_smsc != NULL) {
ot->accepted_smsc = octstr_split(accepted_smsc, octstr_imm(";"));
octstr_destroy(accepted_smsc);
}
accepted_smsc_regex = cfg_get(grp, octstr_imm("accepted-smsc-regex"));
if (accepted_smsc_regex != NULL) {
if ( (ot->accepted_smsc_regex = gw_regex_comp(accepted_smsc_regex, REG_EXTENDED)) == NULL)
panic(0, "Could not compile pattern '%s'", octstr_get_cstr(accepted_smsc_regex));
octstr_destroy(accepted_smsc_regex);
}
cfg_get_bool(&ot->assume_plain_text, grp,
octstr_imm("assume-plain-text"));
cfg_get_bool(&ot->accept_x_kannel_headers, grp,
octstr_imm("accept-x-kannel-headers"));
cfg_get_bool(&ot->strip_keyword, grp, octstr_imm("strip-keyword"));
cfg_get_bool(&ot->send_sender, grp, octstr_imm("send-sender"));
ot->prefix = cfg_get(grp, octstr_imm("prefix"));
ot->suffix = cfg_get(grp, octstr_imm("suffix"));
ot->allowed_recv_prefix = cfg_get(grp, octstr_imm("allowed-receiver-prefix"));
allowed_receiver_prefix_regex = cfg_get(grp, octstr_imm("allowed-receiver-prefix-regex"));
if (allowed_receiver_prefix_regex != NULL) {
if ((ot->allowed_receiver_prefix_regex = gw_regex_comp(allowed_receiver_prefix_regex, REG_EXTENDED)) == NULL)
panic(0, "Could not compile pattern '%s'", octstr_get_cstr(allowed_receiver_prefix_regex));
octstr_destroy(allowed_receiver_prefix_regex);
}
ot->allowed_recv_prefix = cfg_get(grp, octstr_imm("allowed-receiver-prefix"));
ot->denied_recv_prefix = cfg_get(grp, octstr_imm("denied-receiver-prefix"));
denied_receiver_prefix_regex = cfg_get(grp, octstr_imm("denied-receiver-prefix-regex"));
if (denied_receiver_prefix_regex != NULL) {
if ((ot->denied_receiver_prefix_regex = gw_regex_comp(denied_receiver_prefix_regex, REG_EXTENDED)) == NULL)
panic(0, "Could not compile pattern '%s'",octstr_get_cstr(denied_receiver_prefix_regex));
octstr_destroy(denied_receiver_prefix_regex);
}
ot->args = count_occurences(ot->pattern, octstr_imm("%s"));
ot->args += count_occurences(ot->pattern, octstr_imm("%S"));
ot->has_catchall_arg =
(count_occurences(ot->pattern, octstr_imm("%r")) > 0) ||
(count_occurences(ot->pattern, octstr_imm("%a")) > 0);
} else {
ot->type = TRANSTYPE_SENDSMS;
ot->pattern = octstr_create("");
ot->args = 0;
ot->has_catchall_arg = 0;
ot->catch_all = 1;
ot->username = cfg_get(grp, octstr_imm("username"));
ot->password = cfg_get(grp, octstr_imm("password"));
ot->dlr_url = cfg_get(grp, octstr_imm("dlr-url"));
if (ot->password == NULL) {
error(0, "Password required for send-sms user");
goto error;
}
ot->name = cfg_get(grp, octstr_imm("name"));
if (ot->name == NULL)
ot->name = octstr_duplicate(ot->username);
forced_smsc = cfg_get(grp, octstr_imm("forced-smsc"));
default_smsc = cfg_get(grp, octstr_imm("default-smsc"));
if (forced_smsc != NULL) {
if (default_smsc != NULL) {
info(0, "Redundant default-smsc for send-sms user %s",
octstr_get_cstr(ot->username));
}
ot->forced_smsc = forced_smsc;
octstr_destroy(default_smsc);
} else if (default_smsc != NULL)
ot->default_smsc = default_smsc;
ot->deny_ip = cfg_get(grp, octstr_imm("user-deny-ip"));
ot->allow_ip = cfg_get(grp, octstr_imm("user-allow-ip"));
ot->default_sender = cfg_get(grp, octstr_imm("default-sender"));
}
ot->allowed_prefix = cfg_get(grp, octstr_imm("allowed-prefix"));
allowed_prefix_regex = cfg_get(grp, octstr_imm("allowed-prefix-regex"));
if (allowed_prefix_regex != NULL) {
if ((ot->allowed_prefix_regex = gw_regex_comp(allowed_prefix_regex, REG_EXTENDED)) == NULL)
panic(0, "Could not compile pattern '%s'", octstr_get_cstr(allowed_prefix_regex));
octstr_destroy(allowed_prefix_regex);
}
ot->denied_prefix = cfg_get(grp, octstr_imm("denied-prefix"));
denied_prefix_regex = cfg_get(grp, octstr_imm("denied-prefix-regex"));
if (denied_prefix_regex != NULL) {
if ((ot->denied_prefix_regex = gw_regex_comp(denied_prefix_regex, REG_EXTENDED)) == NULL)
panic(0, "Could not compile pattern '%s'", octstr_get_cstr(denied_prefix_regex));
octstr_destroy(denied_prefix_regex);
}
os = cfg_get(grp, octstr_imm("white-list"));
if (os != NULL) {
ot->white_list = numhash_create(octstr_get_cstr(os));
octstr_destroy(os);
}
white_list_regex = cfg_get(grp, octstr_imm("white-list-regex"));
if (white_list_regex != NULL) {
if ((ot->white_list_regex = gw_regex_comp(white_list_regex, REG_EXTENDED)) == NULL)
panic(0, "Could not compile pattern '%s'", octstr_get_cstr(white_list_regex));
octstr_destroy(white_list_regex);
}
os = cfg_get(grp, octstr_imm("black-list"));
if (os != NULL) {
ot->black_list = numhash_create(octstr_get_cstr(os));
octstr_destroy(os);
}
black_list_regex = cfg_get(grp, octstr_imm("black-list-regex"));
if (black_list_regex != NULL) {
if ((ot->black_list_regex = gw_regex_comp(black_list_regex, REG_EXTENDED)) == NULL)
panic(0, "Could not compile pattern '%s'", octstr_get_cstr(black_list_regex));
octstr_destroy(black_list_regex);
}
if (cfg_get_integer(&ot->max_messages, grp,
octstr_imm("max-messages")) == -1)
ot->max_messages = 1;
cfg_get_bool(&ot->concatenation, grp,
octstr_imm("concatenation"));
cfg_get_bool(&ot->omit_empty, grp,
octstr_imm("omit-empty"));
ot->header = cfg_get(grp, octstr_imm("header"));
ot->footer = cfg_get(grp, octstr_imm("footer"));
ot->faked_sender = cfg_get(grp, octstr_imm("faked-sender"));
ot->split_chars = cfg_get(grp, octstr_imm("split-chars"));
ot->split_suffix = cfg_get(grp, octstr_imm("split-suffix"));
if ( (ot->prefix == NULL && ot->suffix != NULL) ||
(ot->prefix != NULL && ot->suffix == NULL) ) {
warning(0, "Service <%s>: suffix and prefix are only used"
" if both are set.", octstr_get_cstr(ot->keyword));
}
if ((ot->prefix != NULL || ot->suffix != NULL) &&
ot->type != TRANSTYPE_GET_URL) {
warning(0, "Service <%s>: suffix and prefix are only used"
" if type is 'get-url'.", octstr_get_cstr(ot->keyword));
}
return ot;
error:
error(0, "Couldn't create a URLTranslation.");
destroy_onetrans(ot);
return NULL;
}
/*
* Free one URLTranslation.
*/
static void destroy_onetrans(void *p)
{
URLTranslation *ot;
ot = p;
if (ot != NULL) {
octstr_destroy(ot->keyword);
gwlist_destroy(ot->aliases, octstr_destroy_item);
octstr_destroy(ot->dlr_url);
octstr_destroy(ot->pattern);
octstr_destroy(ot->prefix);
octstr_destroy(ot->suffix);
octstr_destroy(ot->default_sender);
octstr_destroy(ot->faked_sender);
octstr_destroy(ot->split_chars);
octstr_destroy(ot->split_suffix);
octstr_destroy(ot->header);
octstr_destroy(ot->footer);
gwlist_destroy(ot->accepted_smsc, octstr_destroy_item);
octstr_destroy(ot->name);
octstr_destroy(ot->username);
octstr_destroy(ot->password);
octstr_destroy(ot->forced_smsc);
octstr_destroy(ot->default_smsc);
octstr_destroy(ot->allow_ip);
octstr_destroy(ot->deny_ip);
octstr_destroy(ot->allowed_prefix);
octstr_destroy(ot->denied_prefix);
octstr_destroy(ot->allowed_recv_prefix);
octstr_destroy(ot->denied_recv_prefix);
numhash_destroy(ot->white_list);
numhash_destroy(ot->black_list);
if (ot->keyword_regex != NULL) gw_regex_destroy(ot->keyword_regex);
if (ot->accepted_smsc_regex != NULL) gw_regex_destroy(ot->accepted_smsc_regex);
if (ot->allowed_prefix_regex != NULL) gw_regex_destroy(ot->allowed_prefix_regex);
if (ot->denied_prefix_regex != NULL) gw_regex_destroy(ot->denied_prefix_regex);
if (ot->allowed_receiver_prefix_regex != NULL) gw_regex_destroy(ot->allowed_receiver_prefix_regex);
if (ot->denied_receiver_prefix_regex != NULL) gw_regex_destroy(ot->denied_receiver_prefix_regex);
if (ot->white_list_regex != NULL) gw_regex_destroy(ot->white_list_regex);
if (ot->black_list_regex != NULL) gw_regex_destroy(ot->black_list_regex);
gw_free(ot);
}
}
/*
* checks if the number of passed words matches the service-pattern defined in the
* translation. returns 0 if arguments are okay, -1 otherwise.
*/
static int check_num_args(URLTranslation *t, List *words)
{
const int IS_OKAY = 0;
const int NOT_OKAY = -1;
int n;
n = gwlist_len(words);
/* check number of arguments */
if (t->catch_all)
return IS_OKAY;
if (n - 1 == t->args)
return IS_OKAY;
if (t->has_catchall_arg && n - 1 >= t->args)
return IS_OKAY;
return NOT_OKAY;
}
/*
* checks if a request matches the parameters of a URL-Translation, e.g. whether or not
* a user is allowed to use certain services. returns 0 if allowed, -1 if not.
* reject will be set to 1 is a number is rejected due to white/black-lists.
*/
static int check_allowed_translation(URLTranslation *t,
Octstr *smsc, Octstr *sender, Octstr *receiver, int *reject)
{
const int IS_ALLOWED = 0;
const int NOT_ALLOWED = -1;
/* if smsc_id set and accepted_smsc exist, accept
* translation only if smsc id is in accept string
*/
if (smsc && t->accepted_smsc && !gwlist_search(t->accepted_smsc, smsc, octstr_item_match))
return NOT_ALLOWED;
if (smsc && t->accepted_smsc_regex && gw_regex_match_pre( t->accepted_smsc_regex, smsc) == 0)
return NOT_ALLOWED;
/* Have allowed for sender */
if (t->allowed_prefix && !t->denied_prefix && does_prefix_match(t->allowed_prefix, sender) != 1)
return NOT_ALLOWED;
if (t->allowed_prefix_regex && !t->denied_prefix_regex && gw_regex_match_pre(t->allowed_prefix_regex, sender) == 0)
return NOT_ALLOWED;
/* Have denied for sender */
if (t->denied_prefix && !t->allowed_prefix && does_prefix_match(t->denied_prefix, sender) == 1)
return NOT_ALLOWED;
if (t->denied_prefix_regex && !t->allowed_prefix_regex && gw_regex_match_pre(t->denied_prefix_regex, sender) == 1)
return NOT_ALLOWED;
/* Have allowed for receiver */
if (t->allowed_recv_prefix && !t->denied_recv_prefix && does_prefix_match(t->allowed_recv_prefix, receiver) != 1)
return NOT_ALLOWED;
if (t->allowed_receiver_prefix_regex && !t->denied_receiver_prefix_regex &&
gw_regex_match_pre(t->allowed_receiver_prefix_regex, receiver) == 0)
return NOT_ALLOWED;
/* Have denied for receiver */
if (t->denied_recv_prefix && !t->allowed_recv_prefix && does_prefix_match(t->denied_recv_prefix, receiver) == 1)
return NOT_ALLOWED;
if (t->denied_receiver_prefix_regex && !t->allowed_receiver_prefix_regex &&
gw_regex_match_pre(t->denied_receiver_prefix_regex, receiver) == 0)
return NOT_ALLOWED;
if (t->white_list && numhash_find_number(t->white_list, sender) < 1) {
*reject = 1;
return NOT_ALLOWED;
}
if (t->white_list_regex && gw_regex_match_pre(t->white_list_regex, sender) == 0) {
*reject = 1;
return NOT_ALLOWED;
}
if (t->black_list && numhash_find_number(t->black_list, sender) == 1) {
*reject = 1;
return NOT_ALLOWED;
}
if (t->black_list_regex && gw_regex_match_pre(t->black_list_regex, sender) == 1) {
*reject = 1;
return NOT_ALLOWED;
}
/* Have allowed and denied */
if (t->denied_prefix && t->allowed_prefix && does_prefix_match(t->allowed_prefix, sender) != 1 &&
does_prefix_match(t->denied_prefix, sender) == 1)
return NOT_ALLOWED;
if (t->denied_prefix_regex && t->allowed_prefix_regex &&
gw_regex_match_pre(t->allowed_prefix_regex, sender) == 0 &&
gw_regex_match_pre(t->denied_prefix_regex, sender) == 1)
return NOT_ALLOWED;
return IS_ALLOWED;
};
/* get_matching_translations - iterate over all translations in trans.
* for each translation check whether
* the translation's keyword has already been interpreted as a regexp.
* if not, compile it now,
* otherwise retrieve compilation result from dictionary.
*
* the translations where the word matches the translation's pattern
* are returned in a list
*
*/
static List* get_matching_translations(URLTranslationList *trans, Octstr *word)
{
List *list;
/*char *tmp_word;*/
int i;
size_t n_match = 1;
regmatch_t p_match[10];
URLTranslation *t;
gw_assert(trans != NULL && word != NULL);
list = gwlist_create();
for (i = 0; i < gwlist_len(trans->list); ++i) {
t = gwlist_get(trans->list, i);
if (t->keyword == NULL)
continue;
/* if regex feature is used try to match */
if ((t->keyword_regex != NULL) && (gw_regex_exec(t->keyword_regex, word, n_match, p_match, 0) == 0))
gwlist_append(list, t);
/* otherwise look for exact match */
if (octstr_compare(t->keyword, word) == 0)
gwlist_append(list, t);
}
return list;
}
/*
* Find the appropriate translation
*/
static URLTranslation *find_translation(URLTranslationList *trans,
List *words, Octstr *smsc, Octstr *sender, Octstr *receiver, int *reject)
{
Octstr *keyword;
int i, n;
URLTranslation *t;
List *list;
n = gwlist_len(words);
if (n == 0)
return NULL;
n = 1;
keyword = gwlist_get(words, 0);
keyword = octstr_duplicate(keyword);
octstr_convert_range(keyword, 0, octstr_len(keyword), tolower);
list = get_matching_translations(trans, keyword);
/*
list now contains all translations where the keyword of the sms matches the
pattern defined by the tranlsation's keyword
*/
t = NULL;
for (i = 0; i < gwlist_len(list); ++i) {
t = gwlist_get(list, i);
if (check_allowed_translation(t, smsc, sender, receiver, reject) == 0
&& check_num_args(t, words) == 0)
break;
t = NULL;
}
/* Only return reject if there's only blacklisted smsc's */
if(t != NULL)
*reject = 0;
octstr_destroy(keyword);
gwlist_destroy(list, NULL);
return t;
}
static URLTranslation *find_default_translation(URLTranslationList *trans,
Octstr *smsc, Octstr *sender, Octstr *receiver,
int *reject)
{
URLTranslation *t;
int i;
List *list;
*reject = 0;
list = dict_get(trans->dict, octstr_imm("default"));
t = NULL;
for (i = 0; i < gwlist_len(list); ++i) {
t = gwlist_get(list, i);
if (check_allowed_translation(t, smsc, sender, receiver, reject) == 0)
break;
t = NULL;
}
/* Only return reject if there's only blacklisted smsc's */
if(t != NULL)
*reject = 0;
return t;
}
static URLTranslation *find_black_list_translation(URLTranslationList *trans,
Octstr *smsc)
{
URLTranslation *t;
int i;
List *list;
list = dict_get(trans->dict, octstr_imm("black-list"));
t = NULL;
for (i = 0; i < gwlist_len(list); ++i) {
t = gwlist_get(list, i);
if (smsc && t->accepted_smsc) {
if (!gwlist_search(t->accepted_smsc, smsc, octstr_item_match)) {
t = NULL;
continue;
}
}
break;
}
return t;
}
/*
* Count the number of times `pat' occurs in `str'.
*/
static long count_occurences(Octstr *str, Octstr *pat)
{
long count;
long pos;
long len;
count = 0;
pos = 0;
len = octstr_len(pat);
while ((pos = octstr_search(str, pat, pos)) != -1) {
++count;
pos += len;
}
return count;
}