/* ====================================================================
* 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.
*/
/*
* smsbox.c - main program of the smsbox
*/
#include
#include
#include
#include
#include "gwlib/gwlib.h"
#include "gwlib/regex.h"
#include "msg.h"
#include "sms.h"
#include "dlr.h"
#include "bb.h"
#include "shared.h"
#include "heartbeat.h"
#include "html.h"
#include "urltrans.h"
#include "ota_prov_attr.h"
#include "ota_prov.h"
#include "ota_compiler.h"
#include "xml_shared.h"
#ifdef HAVE_SECURITY_PAM_APPL_H
#include
#endif
#define SENDSMS_DEFAULT_CHARS "0123456789 +-"
#define O_DESTROY(a) { if(a) octstr_destroy(a); a = NULL; }
/* Defaults for the HTTP request queueing inside http_queue_thread */
#define HTTP_MAX_RETRIES 0
#define HTTP_RETRY_DELAY 10 /* in sec. */
#define HTTP_MAX_PENDING 512 /* max requests handled in parallel */
/* have we received restart cmd from bearerbox? */
volatile sig_atomic_t restart = 0;
static Cfg *cfg;
static long bb_port;
static int bb_ssl = 0;
static long sendsms_port = 0;
static Octstr *sendsms_interface = NULL;
static Octstr *smsbox_id = NULL;
static Octstr *sendsms_url = NULL;
static Octstr *sendota_url = NULL;
static Octstr *xmlrpc_url = NULL;
static Octstr *bb_host;
static Octstr *accepted_chars = NULL;
static int only_try_http = 0;
static URLTranslationList *translations = NULL;
static long sms_max_length = MAX_SMS_OCTETS;
static char *sendsms_number_chars;
static Octstr *global_sender = NULL;
static Octstr *reply_couldnotfetch = NULL;
static Octstr *reply_couldnotrepresent = NULL;
static Octstr *reply_requestfailed = NULL;
static Octstr *reply_emptymessage = NULL;
static int mo_recode = 0;
static Numhash *white_list;
static Numhash *black_list;
static regex_t *white_list_regex = NULL;
static regex_t *black_list_regex = NULL;
static long max_http_retries = HTTP_MAX_RETRIES;
static long http_queue_delay = HTTP_RETRY_DELAY;
static Octstr *ppg_service_name = NULL;
static List *smsbox_requests = NULL; /* the inbound request queue */
static List *smsbox_http_requests = NULL; /* the outbound HTTP request queue */
/* Maximum requests that we handle in parallel */
static Semaphore *max_pending_requests;
int charset_processing (Octstr *charset, Octstr *text, int coding);
static long get_tag(Octstr *body, Octstr *tag, Octstr **value, long pos, int nostrip);
/* for delayed HTTP answers.
* Dict key is uuid, value is HTTPClient pointer
* of open transaction
*/
static int immediate_sendsms_reply = 0;
static Dict *client_dict = NULL;
static List *sendsms_reply_hdrs = NULL;
/***********************************************************************
* Communication with the bearerbox.
*/
/*
* Identify ourself to bearerbox for smsbox-specific routing inside bearerbox.
* Do this even while no smsbox-id is given to unlock the sender thread in
* bearerbox.
*/
static void identify_to_bearerbox(void)
{
Msg *msg;
msg = msg_create(admin);
msg->admin.command = cmd_identify;
msg->admin.boxc_id = octstr_duplicate(smsbox_id);
write_to_bearerbox(msg);
}
/*
* Handle delayed reply to HTTP sendsms client, if any
*/
static void delayed_http_reply(Msg *msg)
{
HTTPClient *client;
Octstr *os, *answer;
char id[UUID_STR_LEN + 1];
int status;
uuid_unparse(msg->ack.id, id);
os = octstr_create(id);
debug("sms.http", 0, "Got ACK (%ld) of %s", msg->ack.nack, octstr_get_cstr(os));
client = dict_remove(client_dict, os);
if (client == NULL) {
debug("sms.http", 0, "No client - multi-send or ACK to pull-reply");
octstr_destroy(os);
return;
}
/* XXX this should be fixed so that we really wait for DLR
* SMSC accept/deny before doing this - but that is far
* more slower, a bit more complex, and is done later on
*/
switch (msg->ack.nack) {
case ack_success:
status = HTTP_ACCEPTED;
answer = octstr_create("0: Accepted for delivery");
break;
case ack_buffered:
status = HTTP_ACCEPTED;
answer = octstr_create("3: Queued for later delivery");
break;
case ack_failed:
status = HTTP_FORBIDDEN;
answer = octstr_create("Not routable. Do not try again.");
break;
case ack_failed_tmp:
status = HTTP_SERVICE_UNAVAILABLE;
answer = octstr_create("Temporal failure, try again later.");
break;
default:
error(0, "Strange reply from bearerbox!");
status = HTTP_SERVICE_UNAVAILABLE;
answer = octstr_create("Temporal failure, try again later.");
break;
}
http_send_reply(client, status, sendsms_reply_hdrs, answer);
octstr_destroy(answer);
octstr_destroy(os);
}
/*
* Read an Msg from the bearerbox and send it to the proper receiver
* via a List. At the moment all messages are sent to the smsbox_requests
* List.
*/
static void read_messages_from_bearerbox(void)
{
time_t start, t;
int secs;
int total = 0;
int ret;
Msg *msg;
start = t = time(NULL);
while (program_status != shutting_down) {
/* block infinite for reading messages */
ret = read_from_bearerbox(&msg, INFINITE_TIME);
if (ret == -1)
break;
else if (ret == 1) /* timeout */
continue;
else if (msg == NULL) /* just to be sure, may not happens */
break;
if (msg_type(msg) == admin) {
if (msg->admin.command == cmd_shutdown) {
info(0, "Bearerbox told us to die");
program_status = shutting_down;
} else if (msg->admin.command == cmd_restart) {
info(0, "Bearerbox told us to restart");
restart = 1;
program_status = shutting_down;
}
/*
* XXXX here should be suspend/resume, add RSN
*/
msg_destroy(msg);
} else if (msg_type(msg) == sms) {
if (total == 0)
start = time(NULL);
total++;
gwlist_produce(smsbox_requests, msg);
} else if (msg_type(msg) == ack) {
if (!immediate_sendsms_reply)
delayed_http_reply(msg);
msg_destroy(msg);
} else {
warning(0, "Received other message than sms/admin, ignoring!");
msg_destroy(msg);
}
}
secs = difftime(time(NULL), start);
info(0, "Received (and handled?) %d requests in %d seconds "
"(%.2f per second)", total, secs, (float)total / secs);
}
/***********************************************************************
* Send Msg to bearerbox for delivery to phone, possibly split it first.
*/
/*
* Counter for catenated SMS messages. The counter that can be put into
* the catenated SMS message's UDH headers is actually the lowest 8 bits.
*/
static Counter *catenated_sms_counter;
/*
* Send a message to the bearerbox for delivery to a phone. Use
* configuration from `trans' to format the message before sending.
* Return >= 0 for success & count of splitted sms messages,
* -1 for failure. Does not destroy the msg.
*/
static int send_message(URLTranslation *trans, Msg *msg)
{
int max_msgs;
Octstr *header, *footer, *suffix, *split_chars;
int catenate;
unsigned long msg_sequence, msg_count;
List *list;
Msg *part;
gw_assert(msg != NULL);
gw_assert(msg_type(msg) == sms);
if (trans != NULL)
max_msgs = urltrans_max_messages(trans);
else
max_msgs = 1;
if (max_msgs == 0) {
info(0, "No reply sent, denied.");
return 0;
}
/*
* Encode our smsbox-id to the msg structure.
* This will allow bearerbox to return specific answers to the
* same smsbox, mainly for DLRs and SMS proxy modes.
*/
if (smsbox_id != NULL) {
msg->sms.boxc_id = octstr_duplicate(smsbox_id);
}
/*
* Empty message? Two alternatives have to be handled:
* a) it's a HTTP sms-service reply: either ignore it or
* substitute the "empty" warning defined
* b) it's a sendsms HTTP interface call: leave the message empty
* if at least a UDH is given.
*
* XXX this still does not cover the case when the sendsms interface is
* used with *no* text and udh. What should we do then?!
*/
if (octstr_len(msg->sms.msgdata) == 0 && octstr_len(msg->sms.udhdata) == 0) {
if (trans != NULL && urltrans_omit_empty(trans))
return 0;
else
msg->sms.msgdata = octstr_duplicate(reply_emptymessage);
}
if (trans == NULL) {
header = NULL;
footer = NULL;
suffix = NULL;
split_chars = NULL;
catenate = 0;
} else {
header = urltrans_header(trans);
footer = urltrans_footer(trans);
suffix = urltrans_split_suffix(trans);
split_chars = urltrans_split_chars(trans);
catenate = urltrans_concatenation(trans);
}
if (catenate)
msg_sequence = counter_increase(catenated_sms_counter) & 0xFF;
else
msg_sequence = 0;
list = sms_split(msg, header, footer, suffix, split_chars, catenate,
msg_sequence, max_msgs, sms_max_length);
msg_count = gwlist_len(list);
debug("sms", 0, "message length %ld, sending %ld messages",
octstr_len(msg->sms.msgdata), msg_count);
/*
* In order to get catenated msgs work properly, we
* have moved catenation to bearerbox.
* So here we just need to put splitted msgs into one again and send
* to bearerbox that will care about catenation.
*/
if (catenate) {
Msg *new_msg = msg_duplicate(msg);
octstr_delete(new_msg->sms.msgdata, 0, octstr_len(new_msg->sms.msgdata));
while((part = gwlist_extract_first(list)) != NULL) {
octstr_append(new_msg->sms.msgdata, part->sms.msgdata);
msg_destroy(part);
}
write_to_bearerbox(new_msg);
} else {
/* msgs are the independed parts so sent those as is */
while ((part = gwlist_extract_first(list)) != NULL)
write_to_bearerbox(part);
}
gwlist_destroy(list, NULL);
return msg_count;
}
/***********************************************************************
* Stuff to remember which receiver belongs to which HTTP query.
* This also includes HTTP request data to queue a failed HTTP request
* into the smsbox_http_request queue which is then handled by the
* http_queue_thread thread on a re-scheduled time basis.
*/
static HTTPCaller *caller;
static Counter *num_outstanding_requests;
struct receiver {
Msg *msg;
URLTranslation *trans;
int method; /* the HTTP method to use */
Octstr *url; /* the after pattern URL */
List *http_headers;
Octstr *body; /* body content of the request */
unsigned long retries; /* number of performed retries */
};
/*
* Again no urltranslation when we got an answer to wap push - it can only be dlr.
*/
static void *remember_receiver(Msg *msg, URLTranslation *trans, int method,
Octstr *url, List *headers, Octstr *body,
unsigned int retries)
{
struct receiver *receiver;
counter_increase(num_outstanding_requests);
receiver = gw_malloc(sizeof(*receiver));
receiver->msg = msg_create(sms);
receiver->msg->sms.sender = octstr_duplicate(msg->sms.sender);
receiver->msg->sms.receiver = octstr_duplicate(msg->sms.receiver);
/* ppg_service_name should always be not NULL here */
if (trans != NULL && (msg->sms.service == NULL || ppg_service_name == NULL ||
octstr_compare(msg->sms.service, ppg_service_name) != 0)) {
receiver->msg->sms.service = octstr_duplicate(urltrans_name(trans));
} else {
receiver->msg->sms.service = octstr_duplicate(msg->sms.service);
}
receiver->msg->sms.smsc_id = octstr_duplicate(msg->sms.smsc_id);
/* to remember if it's a DLR http get */
receiver->msg->sms.sms_type = msg->sms.sms_type;
receiver->trans = trans;
/* remember the HTTP request if we need to queue this */
receiver->method = method;
receiver->url = octstr_duplicate(url);
receiver->http_headers = http_header_duplicate(headers);
receiver->body = octstr_duplicate(body);
receiver->retries = retries;
return receiver;
}
static void get_receiver(void *id, Msg **msg, URLTranslation **trans, int *method,
Octstr **url, List **headers, Octstr **body,
unsigned long *retries)
{
struct receiver *receiver;
receiver = id;
*msg = receiver->msg;
*trans = receiver->trans;
*method = receiver->method;
*url = receiver->url;
*headers = receiver->http_headers;
*body = receiver->body;
*retries = receiver->retries;
gw_free(receiver);
counter_decrease(num_outstanding_requests);
}
static long outstanding_requests(void)
{
return counter_value(num_outstanding_requests);
}
/***********************************************************************
* Thread for receiving reply from HTTP query and sending it to phone.
*/
static void strip_prefix_and_suffix(Octstr *html, Octstr *prefix,
Octstr *suffix)
{
long prefix_end, suffix_start;
if (prefix == NULL || suffix == NULL)
return;
prefix_end = octstr_case_search(html, prefix, 0);
if (prefix_end == -1)
return;
prefix_end += octstr_len(prefix);
suffix_start = octstr_case_search(html, suffix, prefix_end);
if (suffix_start == -1)
return;
octstr_delete(html, 0, prefix_end);
octstr_truncate(html, suffix_start - prefix_end);
}
static void get_x_kannel_from_headers(List *headers, Octstr **from,
Octstr **to, Octstr **udh,
Octstr **user, Octstr **pass,
Octstr **smsc, int *mclass, int *mwi,
int *coding, int *compress,
int *validity, int *deferred,
int *dlr_mask, Octstr **dlr_url,
Octstr **account, int *pid, int *alt_dcs,
int *rpi, Octstr **binfo, int *priority)
{
Octstr *name, *val;
long l;
for(l=0; l"));
taglen = octstr_len(tmp);
start = octstr_search(body, tmp, pos);
octstr_destroy(tmp);
if(start != -1) {
tmp = octstr_create("");
octstr_append(tmp, tag);
octstr_append(tmp, octstr_imm(">"));
end = octstr_search(body, tmp, start);
octstr_destroy(tmp);
if(end != -1) {
octstr_destroy(*value);
*value = octstr_copy(body, start + taglen, end - start - taglen);
if(nostrip == 0) {
octstr_strip_blanks(*value);
debug("sms", 0, "XMLParsing: tag <%s> value <%s>", octstr_get_cstr(tag),
octstr_get_cstr(*value));
}
return end + taglen + 1;
} else {
debug("sms", 0, "XMLParsing: end tag %s> not found", octstr_get_cstr(tag));
return -1;
}
} else {
/* debug("sms", 0, "XMLParsing: tag <%s> not found", octstr_get_cstr(tag)); */
return -1;
}
}
/* requesttype = mt_reply or mt_push. for example, auth is only read on mt_push
* parse body and populate fields, including replacing body for value and
* type to text/plain */
static void get_x_kannel_from_xml(int requesttype , Octstr **type, Octstr **body,
List *headers, Octstr **from,
Octstr **to, Octstr **udh,
Octstr **user, Octstr **pass,
Octstr **smsc, int *mclass, int *mwi,
int *coding, int *compress,
int *validity, int *deferred,
int *dlr_mask, Octstr **dlr_url,
Octstr **account, int *pid, int *alt_dcs,
int *rpi, List **tolist, Octstr **charset,
Octstr **binfo, int *priority)
{
Octstr *text, *tmp, *tmp2;
long tmplong, where;
tmp = tmp2 = text = NULL;
debug("sms", 0, "XMLParsing: XML: <%s>", octstr_get_cstr(*body));
/* auth */
get_tag(*body, octstr_imm("from"), &tmp, 0, 0);
if(tmp) {
if(requesttype == mt_push) {
/* user */
get_tag(tmp, octstr_imm("user"), user, 0, 0);
get_tag(tmp, octstr_imm("username"), user, 0, 0);
/* pass */
get_tag(tmp, octstr_imm("pass"), pass, 0, 0);
get_tag(tmp, octstr_imm("password"), pass, 0, 0);
}
/* account */
get_tag(tmp, octstr_imm("account"), account, 0, 0);
/* binfo */
get_tag(tmp, octstr_imm("binfo"), binfo, 0, 0);
O_DESTROY(tmp);
}
get_tag(*body, octstr_imm("oa"), &tmp, 0, 0);
if(tmp) {
/* sender address */
get_tag(tmp, octstr_imm("number"), from, 0, 0);
O_DESTROY(tmp);
}
if(requesttype == mt_push) {
/* to (da/number) Multiple tags */
*tolist = gwlist_create();
where = get_tag(*body, octstr_imm("da"), &tmp, 0, 0);
if(tmp) {
get_tag(tmp, octstr_imm("number"), to, 0, 0);
gwlist_append(*tolist, octstr_duplicate(*to));
O_DESTROY(*to);
while(tmp && where != -1) {
O_DESTROY(tmp);
where = get_tag(*body, octstr_imm("da"), &tmp, where, 0);
if(tmp) {
get_tag(tmp, octstr_imm("number"), &tmp2, 0, 0);
if(tmp2 != NULL) {
gwlist_append(*tolist, octstr_duplicate(tmp2));
O_DESTROY(tmp2);
}
}
}
}
}
/* udh */
get_tag(*body, octstr_imm("udh"), &tmp, 0, 0);
if(tmp) {
O_DESTROY(*udh);
*udh = octstr_duplicate(tmp);
if(octstr_hex_to_binary(*udh) == -1)
octstr_url_decode(*udh);
O_DESTROY(tmp);
}
/* smsc */
get_tag(*body, octstr_imm("to"), &tmp, 0, 0);
if(tmp) {
get_tag(tmp, octstr_imm("account"), smsc, 0, 0);
O_DESTROY(tmp);
}
/* pid */
get_tag(*body, octstr_imm("pid"), &tmp, 0, 0);
if(tmp) {
if(octstr_parse_long(&tmplong, tmp, 0, 10) != -1)
*pid = tmplong;
O_DESTROY(tmp);
}
/* rpi */
get_tag(*body, octstr_imm("rpi"), &tmp, 0, 0);
if(tmp) {
if(octstr_parse_long(&tmplong, tmp, 0, 10) != -1)
*rpi = tmplong;
O_DESTROY(tmp);
}
/* dcs* (dcs/ *) */
get_tag(*body, octstr_imm("dcs"), &tmp, 0, 0);
if(tmp) {
/* mclass (dcs/mclass) */
get_tag(tmp, octstr_imm("mclass"), &tmp2, 0, 0);
if(tmp2) {
if(octstr_parse_long(&tmplong, tmp2, 0, 10) != -1)
*mclass = tmplong;
O_DESTROY(tmp2);
}
/* mwi (dcs/mwi) */
get_tag(tmp, octstr_imm("mwi"), &tmp2, 0, 0);
if(tmp2) {
if(octstr_parse_long(&tmplong, tmp2, 0, 10) != -1)
*mwi = tmplong;
O_DESTROY(tmp2);
}
/* coding (dcs/coding) */
get_tag(tmp, octstr_imm("coding"), &tmp2, 0, 0);
if(tmp2) {
if(octstr_parse_long(&tmplong, tmp2, 0, 10) != -1)
*coding = tmplong;
O_DESTROY(tmp2);
}
/* compress (dcs/compress) */
get_tag(tmp, octstr_imm("compress"), &tmp2, 0, 0);
if(tmp2) {
if(octstr_parse_long(&tmplong, tmp2, 0, 10) != -1)
*compress = tmplong;
O_DESTROY(tmp2);
}
/* alt-dcs (dcs/alt-dcs) */
get_tag(tmp, octstr_imm("alt-dcs"), &tmp2, 0, 0);
if(tmp2) {
if(octstr_parse_long(&tmplong, tmp2, 0, 10) != -1)
*alt_dcs = tmplong;
O_DESTROY(tmp2);
}
O_DESTROY(tmp);
}
/* statusrequest* (statusrequest/ *) */
get_tag(*body, octstr_imm("statusrequest"), &tmp, 0, 0);
if(tmp) {
/* dlr-mask (statusrequest/dlr-mask) */
get_tag(tmp, octstr_imm("dlr-mask"), &tmp2, 0, 0);
if(tmp2) {
if(octstr_parse_long(&tmplong, tmp2, 0, 10) != -1)
*dlr_mask = tmplong;
O_DESTROY(tmp2);
}
get_tag(tmp, octstr_imm("dlr-url"), dlr_url, 0, 0);
O_DESTROY(tmp);
}
/* validity (vp/delay) */
get_tag(*body, octstr_imm("vp"), &tmp, 0, 0);
if(tmp) {
get_tag(tmp, octstr_imm("delay"), &tmp2, 0, 0);
if(tmp2) {
if(octstr_parse_long(&tmplong, tmp2, 0, 10) != -1)
*validity = tmplong;
O_DESTROY(tmp2);
}
O_DESTROY(tmp);
}
/* deferred (timing/delay) */
get_tag(*body, octstr_imm("timing"), &tmp, 0, 0);
if(tmp) {
get_tag(tmp, octstr_imm("delay"), &tmp2, 0, 0);
if(tmp2) {
if(octstr_parse_long(&tmplong, tmp2, 0, 10) != -1)
*deferred = tmplong;
O_DESTROY(tmp2);
}
O_DESTROY(tmp);
}
/* priority */
get_tag(*body, octstr_imm("priority"), &tmp, 0, 0);
if(tmp) {
if(octstr_parse_long(&tmplong, tmp, 0, 10) != -1)
*priority = tmplong;
O_DESTROY(tmp);
}
/* charset from */
tmp = find_charset_encoding(*body);
O_DESTROY(*charset);
if(tmp) {
*charset = octstr_duplicate(tmp);
O_DESTROY(tmp);
} else {
*charset = octstr_create("UTF-8");
}
/* text */
text = NULL;
get_tag(*body, octstr_imm("ud"), &tmp, 0, 0);
if(tmp) {
O_DESTROY(text);
text = octstr_duplicate(tmp);
if(octstr_hex_to_binary(text) == -1)
octstr_url_decode(text);
O_DESTROY(tmp);
}
if(text)
*body = text;
else
*body = octstr_create("");
O_DESTROY(*type);
*type = octstr_create("text/plain");
}
static void fill_message(Msg *msg, URLTranslation *trans,
Octstr *replytext, int octet_stream,
Octstr *from, Octstr *to, Octstr *udh,
int mclass, int mwi, int coding, int compress,
int validity, int deferred,
Octstr *dlr_url, int dlr_mask, int pid, int alt_dcs,
int rpi, Octstr *smsc, Octstr *account,
Octstr *charset, Octstr *binfo, int priority)
{
msg->sms.msgdata = replytext;
msg->sms.time = time(NULL);
if (charset)
msg->sms.charset = charset;
if (dlr_url != NULL) {
if (urltrans_accept_x_kannel_headers(trans)) {
octstr_destroy(msg->sms.dlr_url);
msg->sms.dlr_url = dlr_url;
} else {
warning(0, "Tried to change dlr_url to '%s', denied.",
octstr_get_cstr(dlr_url));
octstr_destroy(dlr_url);
}
}
if (smsc != NULL) {
if (urltrans_accept_x_kannel_headers(trans)) {
octstr_destroy(msg->sms.smsc_id);
msg->sms.smsc_id = smsc;
} else {
warning(0, "Tried to change SMSC to '%s', denied.",
octstr_get_cstr(smsc));
octstr_destroy(smsc);
}
}
if (from != NULL) {
if (urltrans_accept_x_kannel_headers(trans)) {
octstr_destroy(msg->sms.sender);
msg->sms.sender = from;
} else {
warning(0, "Tried to change sender to '%s', denied.",
octstr_get_cstr(from));
octstr_destroy(from);
}
}
if (to != NULL) {
if (urltrans_accept_x_kannel_headers(trans)) {
octstr_destroy(msg->sms.receiver);
msg->sms.receiver = to;
} else {
warning(0, "Tried to change receiver to '%s', denied.",
octstr_get_cstr(to));
octstr_destroy(to);
}
}
if (udh != NULL) {
if (urltrans_accept_x_kannel_headers(trans)) {
octstr_destroy(msg->sms.udhdata);
msg->sms.udhdata = udh;
} else {
warning(0, "Tried to set UDH field, denied.");
O_DESTROY(udh);
}
}
if (mclass != SMS_PARAM_UNDEFINED) {
if (urltrans_accept_x_kannel_headers(trans))
msg->sms.mclass = mclass;
else
warning(0, "Tried to set MClass field, denied.");
}
if (pid != SMS_PARAM_UNDEFINED) {
if (urltrans_accept_x_kannel_headers(trans))
msg->sms.pid = pid;
else
warning(0, "Tried to set PID field, denied.");
}
if (rpi != SMS_PARAM_UNDEFINED) {
if (urltrans_accept_x_kannel_headers(trans))
msg->sms.rpi = rpi;
else
warning(0, "Tried to set RPI field, denied.");
}
if (alt_dcs != SMS_PARAM_UNDEFINED) {
if (urltrans_accept_x_kannel_headers(trans))
msg->sms.alt_dcs = alt_dcs;
else
warning(0, "Tried to set Alt-DCS field, denied.");
}
if (mwi != SMS_PARAM_UNDEFINED) {
if (urltrans_accept_x_kannel_headers(trans))
msg->sms.mwi = mwi;
else
warning(0, "Tried to set MWI field, denied.");
}
if (coding != SMS_PARAM_UNDEFINED) {
if (urltrans_accept_x_kannel_headers(trans))
msg->sms.coding = coding;
else
warning(0, "Tried to set Coding field, denied.");
}
if (compress != SMS_PARAM_UNDEFINED) {
if (urltrans_accept_x_kannel_headers(trans))
msg->sms.compress = compress;
else
warning(0, "Tried to set Compress field, denied.");
}
/* Compatibility Mode */
if ( msg->sms.coding == DC_UNDEF) {
if(octstr_len(udh))
msg->sms.coding = DC_8BIT;
else
msg->sms.coding = DC_7BIT;
}
if (validity != SMS_PARAM_UNDEFINED) {
if (urltrans_accept_x_kannel_headers(trans))
msg->sms.validity = validity;
else
warning(0, "Tried to change validity to '%d', denied.",
validity);
}
if (deferred != SMS_PARAM_UNDEFINED) {
if (urltrans_accept_x_kannel_headers(trans))
msg->sms.deferred = deferred;
else
warning(0, "Tried to change deferred to '%d', denied.",
deferred);
}
if (dlr_mask != SMS_PARAM_UNDEFINED) {
if (urltrans_accept_x_kannel_headers(trans)) {
msg->sms.dlr_mask = dlr_mask;
} else
warning(0, "Tried to change dlr_mask to '%d', denied.",
dlr_mask);
}
if (account) {
if (urltrans_accept_x_kannel_headers(trans)) {
msg->sms.account = account;
} else {
warning(0, "Tried to change account to '%s', denied.",
octstr_get_cstr(account));
octstr_destroy(account);
}
}
if (binfo) {
if (urltrans_accept_x_kannel_headers(trans)) {
msg->sms.binfo = binfo;
} else {
warning(0, "Tried to change billing info to '%s', denied.",
octstr_get_cstr(binfo));
octstr_destroy(binfo);
}
}
if (priority != SMS_PARAM_UNDEFINED) {
if (urltrans_accept_x_kannel_headers(trans))
msg->sms.priority = priority;
else
warning(0, "Tried to change priority to '%d', denied.", priority);
}
}
/***********************************************************************
* Thread to handle failed HTTP requests and retries to deliver the
* information to the HTTP server. The thread uses the smsbox_http_requests
* queue that is spooled by url_result_thread in case the HTTP requests
* fails.
*/
static void http_queue_thread(void *arg)
{
void *id;
Msg *msg;
URLTranslation *trans;
Octstr *req_url;
List *req_headers;
Octstr *req_body;
unsigned long retries;
int method;
while ((id = gwlist_consume(smsbox_http_requests)) != NULL) {
/*
* Sleep for a while in order not to block other operting requests.
* Defaults to 10 sec. if not given via http-queue-delay directive in
* smsbox group.
*/
if (http_queue_delay > 0)
gwthread_sleep(http_queue_delay);
debug("sms.http",0,"HTTP: Queue contains %ld outstanding requests",
gwlist_len(smsbox_http_requests));
/*
* Get all required HTTP request data from the queue and reconstruct
* the id pointer for later lookup in url_result_thread.
*/
get_receiver(id, &msg, &trans, &method, &req_url, &req_headers, &req_body, &retries);
if (retries < max_http_retries) {
id = remember_receiver(msg, trans, method, req_url, req_headers, req_body, ++retries);
debug("sms.http",0,"HTTP: Retrying request <%s> (%ld/%ld)",
octstr_get_cstr(req_url), retries, max_http_retries);
/* re-queue this request to the HTTPCaller list */
http_start_request(caller, method, req_url, req_headers, req_body,
1, id, NULL);
}
msg_destroy(msg);
octstr_destroy(req_url);
http_destroy_headers(req_headers);
octstr_destroy(req_body);
}
}
static void url_result_thread(void *arg)
{
Octstr *final_url, *req_body, *type, *replytext;
List *reply_headers;
int status, method;
void *id;
Msg *msg;
URLTranslation *trans;
Octstr *req_url;
List *req_headers;
Octstr *text_html, *text_plain, *text_wml, *text_xml;
Octstr *octet_stream;
int octets;
unsigned long retries;
unsigned int queued; /* indicate if processes reply is requeued */
Octstr *reply_body, *charset;
Octstr *udh, *from, *to, *dlr_url, *account, *smsc, *binfo;
int dlr_mask, mclass, mwi, coding, compress, pid, alt_dcs, rpi;
int validity, deferred, priority;
text_html = octstr_imm("text/html");
text_wml = octstr_imm("text/vnd.wap.wml");
text_plain = octstr_imm("text/plain");
text_xml = octstr_imm("text/xml");
octet_stream = octstr_imm("application/octet-stream");
for (;;) {
queued = 0;
id = http_receive_result(caller, &status, &final_url, &reply_headers,
&reply_body);
semaphore_up(max_pending_requests);
if (id == NULL)
break;
octets = 0;
from = to = udh = smsc = dlr_url = account = binfo = charset = NULL;
mclass = mwi = coding = compress = pid = alt_dcs = rpi = dlr_mask
= validity = deferred = priority = SMS_PARAM_UNDEFINED;
get_receiver(id, &msg, &trans, &method, &req_url, &req_headers, &req_body, &retries);
if (status == HTTP_OK || status == HTTP_ACCEPTED) {
http_header_get_content_type(reply_headers, &type, &charset);
if (octstr_case_compare(type, text_html) == 0 ||
octstr_case_compare(type, text_wml) == 0) {
if (trans != NULL)
strip_prefix_and_suffix(reply_body, urltrans_prefix(trans),
urltrans_suffix(trans));
replytext = html_to_sms(reply_body);
octstr_strip_blanks(replytext);
get_x_kannel_from_headers(reply_headers, &from, &to, &udh,
NULL, NULL, &smsc, &mclass, &mwi,
&coding, &compress, &validity,
&deferred, &dlr_mask, &dlr_url,
&account, &pid, &alt_dcs, &rpi,
&binfo, &priority);
} else if (octstr_case_compare(type, text_plain) == 0) {
replytext = octstr_duplicate(reply_body);
octstr_destroy(reply_body);
reply_body = NULL;
get_x_kannel_from_headers(reply_headers, &from, &to, &udh,
NULL, NULL, &smsc, &mclass, &mwi,
&coding, &compress, &validity,
&deferred, &dlr_mask, &dlr_url,
&account, &pid, &alt_dcs, &rpi,
&binfo, &priority);
} else if (octstr_case_compare(type, text_xml) == 0) {
replytext = octstr_duplicate(reply_body);
octstr_destroy(reply_body);
reply_body = NULL;
get_x_kannel_from_xml(mt_reply, &type, &replytext, reply_headers,
&from, &to, &udh, NULL, NULL, &smsc, &mclass, &mwi,
&coding, &compress, &validity, &deferred, &dlr_mask,
&dlr_url, &account, &pid, &alt_dcs, &rpi, NULL, &charset,
&binfo, &priority);
} else if (octstr_case_compare(type, octet_stream) == 0) {
replytext = octstr_duplicate(reply_body);
octstr_destroy(reply_body);
octets = 1;
reply_body = NULL;
get_x_kannel_from_headers(reply_headers, &from, &to, &udh,
NULL, NULL, &smsc, &mclass, &mwi,
&coding, &compress, &validity,
&deferred, &dlr_mask, &dlr_url,
&account, &pid, &alt_dcs, &rpi,
&binfo, &priority);
} else {
replytext = octstr_duplicate(reply_couldnotrepresent);
}
if (charset_processing(charset, replytext, coding) == -1) {
replytext = octstr_duplicate(reply_couldnotrepresent);
}
octstr_destroy(type);
} else if (max_http_retries > retries) {
id = remember_receiver(msg, trans, method, req_url, req_headers, req_body, retries);
gwlist_produce(smsbox_http_requests, id);
queued++;
goto requeued;
} else
replytext = octstr_duplicate(reply_couldnotfetch);
fill_message(msg, trans, replytext, octets, from, to, udh, mclass,
mwi, coding, compress, validity, deferred, dlr_url,
dlr_mask, pid, alt_dcs, rpi, smsc, account, charset,
binfo, priority);
if (final_url == NULL)
final_url = octstr_imm("");
if (reply_body == NULL)
reply_body = octstr_imm("");
if (msg->sms.sms_type != report_mo) {
alog("SMS HTTP-request sender:%s request: '%s' "
"url: '%s' reply: %d '%s'",
octstr_get_cstr(msg->sms.receiver),
(msg->sms.msgdata != NULL) ? octstr_get_cstr(msg->sms.msgdata) : "",
octstr_get_cstr(final_url), status,
(status == HTTP_OK) ? "<< successful >>" : octstr_get_cstr(reply_body));
}
requeued:
octstr_destroy(final_url);
http_destroy_headers(reply_headers);
octstr_destroy(reply_body);
octstr_destroy(req_url);
http_destroy_headers(req_headers);
octstr_destroy(req_body);
if (msg->sms.sms_type != report_mo && !queued) {
if (send_message(trans, msg) < 0)
error(0, "failed to send message to phone");
}
msg_destroy(msg);
}
}
/***********************************************************************
* Thread to receive SMS messages from bearerbox and obeying the requests
* in them. HTTP requests are started in the background (another thread
* will deal with the replies) and other requests are fulfilled directly.
*/
/*
* Perform the service requested by the user: translate the request into
* a pattern, if it is an URL, start its fetch and return 0, otherwise
* return the string in `*result' and return 1. Return -1 for errors.
* If we are translating url for ppg dlr, we do not use trans data
* structure defined for sms services. This is indicated by trans = NULL.
*/
static int obey_request(Octstr **result, URLTranslation *trans, Msg *msg)
{
Octstr *pattern, *xml, *tmp;
List *request_headers;
void *id;
struct tm tm;
char p[22];
int type;
FILE *f;
gw_assert(msg != NULL);
gw_assert(msg_type(msg) == sms);
if (msg->sms.sms_type == report_mo)
type = TRANSTYPE_GET_URL;
else
type = urltrans_type(trans);
pattern = urltrans_get_pattern(trans, msg);
gw_assert(pattern != NULL);
switch (type) {
case TRANSTYPE_TEXT:
debug("sms", 0, "formatted text answer: <%s>",
octstr_get_cstr(pattern));
*result = pattern;
alog("SMS request sender:%s request: '%s' fixed answer: '%s'",
octstr_get_cstr(msg->sms.receiver),
octstr_get_cstr(msg->sms.msgdata),
octstr_get_cstr(pattern));
break;
case TRANSTYPE_FILE:
*result = octstr_read_file(octstr_get_cstr(pattern));
octstr_destroy(pattern);
alog("SMS request sender:%s request: '%s' file answer: '%s'",
octstr_get_cstr(msg->sms.receiver),
octstr_get_cstr(msg->sms.msgdata),
octstr_get_cstr(*result));
break;
case TRANSTYPE_EXECUTE:
semaphore_down(max_pending_requests);
debug("sms.exec", 0, "executing sms-service '%s'",
octstr_get_cstr(pattern));
if ((f = popen(octstr_get_cstr(pattern), "r")) != NULL) {
octstr_destroy(pattern);
*result = octstr_read_pipe(f);
pclose(f);
semaphore_up(max_pending_requests);
alog("SMS request sender:%s request: '%s' file answer: '%s'",
octstr_get_cstr(msg->sms.receiver),
octstr_get_cstr(msg->sms.msgdata),
octstr_get_cstr(*result));
} else {
error(0, "popen failed for '%s': %d: %s",
octstr_get_cstr(pattern), errno, strerror(errno));
*result = NULL;
octstr_destroy(pattern);
return -1;
}
break;
/*
* No Kannel headers when we are sending dlrs to wap push
*/
case TRANSTYPE_GET_URL:
request_headers = http_create_empty_headers();
http_header_add(request_headers, "User-Agent", GW_NAME "/" GW_VERSION);
if (trans != 0) {
if (urltrans_send_sender(trans)) {
http_header_add(request_headers, "X-Kannel-From",
octstr_get_cstr(msg->sms.receiver));
}
}
id = remember_receiver(msg, trans, HTTP_METHOD_GET, pattern, request_headers, NULL, 0);
semaphore_down(max_pending_requests);
http_start_request(caller, HTTP_METHOD_GET, pattern, request_headers,
NULL, 1, id, NULL);
octstr_destroy(pattern);
http_destroy_headers(request_headers);
*result = NULL;
return 0;
case TRANSTYPE_POST_URL:
request_headers = http_create_empty_headers();
http_header_add(request_headers, "User-Agent", GW_NAME "/" GW_VERSION);
if (msg->sms.coding == DC_8BIT)
http_header_add(request_headers, "Content-Type",
"application/octet-stream");
else
if(msg->sms.coding == DC_UCS2)
http_header_add(request_headers, "Content-Type", "text/plain; charset=\"UTF-16BE\"");
else {
Octstr *header;
header = octstr_create("text/plain");
if(msg->sms.charset) {
octstr_append(header, octstr_imm("; charset=\""));
octstr_append(header, msg->sms.charset);
octstr_append(header, octstr_imm("\""));
}
http_header_add(request_headers, "Content-Type", octstr_get_cstr(header));
O_DESTROY(header);
}
if (urltrans_send_sender(trans))
http_header_add(request_headers, "X-Kannel-From",
octstr_get_cstr(msg->sms.receiver));
http_header_add(request_headers, "X-Kannel-To",
octstr_get_cstr(msg->sms.sender));
tm = gw_gmtime(msg->sms.time);
sprintf(p, "%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);
http_header_add(request_headers, "X-Kannel-Time", p);
tm = gw_gmtime(time(NULL));
sprintf(p, "%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);
http_header_add(request_headers, "Date", p); /* HTTP RFC 14.18 */
if (octstr_len(msg->sms.udhdata)) {
Octstr *os;
os = octstr_duplicate(msg->sms.udhdata);
octstr_url_encode(os);
http_header_add(request_headers, "X-Kannel-UDH",
octstr_get_cstr(os));
octstr_destroy(os);
}
if (octstr_len(msg->sms.smsc_id)) {
Octstr *os;
os = octstr_duplicate(msg->sms.smsc_id);
http_header_add(request_headers, "X-Kannel-SMSC",
octstr_get_cstr(os));
octstr_destroy(os);
}
if(msg->sms.mclass != SMS_PARAM_UNDEFINED) {
Octstr *os;
os = octstr_format("%d",msg->sms.mclass);
http_header_add(request_headers, "X-Kannel-MClass",
octstr_get_cstr(os));
octstr_destroy(os);
}
if(msg->sms.pid != SMS_PARAM_UNDEFINED) {
Octstr *os;
os = octstr_format("%d",msg->sms.pid);
http_header_add(request_headers, "X-Kannel-PID",
octstr_get_cstr(os));
octstr_destroy(os);
}
if(msg->sms.rpi != SMS_PARAM_UNDEFINED) {
Octstr *os;
os = octstr_format("%d",msg->sms.rpi);
http_header_add(request_headers, "X-Kannel-RPI",
octstr_get_cstr(os));
octstr_destroy(os);
}
if(msg->sms.alt_dcs != SMS_PARAM_UNDEFINED) {
Octstr *os;
os = octstr_format("%d",msg->sms.alt_dcs);
http_header_add(request_headers, "X-Kannel-Alt-DCS",
octstr_get_cstr(os));
octstr_destroy(os);
}
if(msg->sms.mwi != SMS_PARAM_UNDEFINED) {
Octstr *os;
os = octstr_format("%d",msg->sms.mwi);
http_header_add(request_headers, "X-Kannel-MWI",
octstr_get_cstr(os));
octstr_destroy(os);
}
if(msg->sms.coding != SMS_PARAM_UNDEFINED) {
Octstr *os;
os = octstr_format("%d",msg->sms.coding);
http_header_add(request_headers, "X-Kannel-Coding",
octstr_get_cstr(os));
octstr_destroy(os);
}
if(msg->sms.compress != SMS_PARAM_UNDEFINED) {
Octstr *os;
os = octstr_format("%d",msg->sms.compress);
http_header_add(request_headers, "X-Kannel-Compress",
octstr_get_cstr(os));
octstr_destroy(os);
}
if (msg->sms.validity != SMS_PARAM_UNDEFINED) {
Octstr *os;
os = octstr_format("%d",msg->sms.validity);
http_header_add(request_headers, "X-Kannel-Validity",
octstr_get_cstr(os));
octstr_destroy(os);
}
if (msg->sms.deferred != SMS_PARAM_UNDEFINED) {
Octstr *os;
os = octstr_format("%d",msg->sms.deferred);
http_header_add(request_headers, "X-Kannel-Deferred",
octstr_get_cstr(os));
octstr_destroy(os);
}
if (octstr_len(msg->sms.service)) {
Octstr *os;
os = octstr_duplicate(msg->sms.service);
http_header_add(request_headers, "X-Kannel-Service",
octstr_get_cstr(os));
octstr_destroy(os);
}
if (octstr_len(msg->sms.binfo)) {
Octstr *os;
os = octstr_duplicate(msg->sms.binfo);
http_header_add(request_headers, "X-Kannel-BInfo",
octstr_get_cstr(os));
octstr_destroy(os);
}
id = remember_receiver(msg, trans, HTTP_METHOD_POST, pattern, request_headers, msg->sms.msgdata, 0);
semaphore_down(max_pending_requests);
http_start_request(caller, HTTP_METHOD_POST, pattern, request_headers,
msg->sms.msgdata, 1, id, NULL);
octstr_destroy(pattern);
http_destroy_headers(request_headers);
*result = NULL;
return 0;
case TRANSTYPE_POST_XML:
/* XXX The first two chars are beeing eaten somewhere and
* only sometimes - something must be ungry */
#define OCTSTR_APPEND_XML(xml, tag, text) \
octstr_format_append(xml, " \t\t<" tag ">%s" tag ">\n", \
(text?octstr_get_cstr(text):""));
#define OCTSTR_APPEND_XML_NUMBER(xml, tag, value) \
octstr_format_append(xml, " \t\t<" tag ">%ld" tag ">\n", (long) value);
request_headers = http_create_empty_headers();
http_header_add(request_headers, "User-Agent", GW_NAME "/" GW_VERSION);
if(msg->sms.coding == DC_UCS2) {
http_header_add(request_headers, "Content-Type",
"text/xml; charset=\"ISO-8859-1\""); /* for account and other strings */
} else {
Octstr *header;
header = octstr_create("text/xml");
if(msg->sms.charset) {
octstr_append(header, octstr_imm("; charset=\""));
octstr_append(header, msg->sms.charset);
octstr_append(header, octstr_imm("\""));
}
http_header_add(request_headers, "Content-Type", octstr_get_cstr(header));
O_DESTROY(header);
}
tm = gw_gmtime(time(NULL));
sprintf(p, "%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);
http_header_add(request_headers, "Date", p); /* HTTP RFC 14.18 */
xml = octstr_create("");
octstr_append(xml, octstr_imm("sms.coding == DC_UCS2 || msg->sms.charset == NULL)
octstr_append(xml, octstr_imm("ISO-8859-1"));
else
octstr_append(xml, msg->sms.charset);
octstr_append(xml, octstr_imm("\"?>\n"));
/*
* XXX damn windows that breaks with this :
* octstr_append(xml, octstr_imm("\n"));
*/
octstr_append(xml, octstr_imm("\n"));
octstr_append(xml, octstr_imm("\t\n"));
/* oa */
if(urltrans_send_sender(trans)) {
tmp = octstr_create("");
OCTSTR_APPEND_XML(tmp, "number", msg->sms.receiver);
OCTSTR_APPEND_XML(xml, "oa", tmp);
octstr_destroy(tmp);
}
/* da */
tmp = octstr_create("");
OCTSTR_APPEND_XML(tmp, "number", msg->sms.sender);
OCTSTR_APPEND_XML(xml, "da", tmp);
octstr_destroy(tmp);
/* udh */
if(octstr_len(msg->sms.udhdata)) {
Octstr *t;
t = octstr_duplicate(msg->sms.udhdata);
octstr_url_encode(t);
OCTSTR_APPEND_XML(xml, "udh", t);
octstr_destroy(t);
}
/* ud */
if(octstr_len(msg->sms.msgdata)) {
octstr_url_encode(msg->sms.msgdata);
OCTSTR_APPEND_XML(xml, "ud", msg->sms.msgdata);
}
/* pid */
if(msg->sms.pid != SMS_PARAM_UNDEFINED)
OCTSTR_APPEND_XML_NUMBER(xml, "pid", msg->sms.pid);
/* rpi */
if(msg->sms.rpi != SMS_PARAM_UNDEFINED)
OCTSTR_APPEND_XML_NUMBER(xml, "rpi", msg->sms.rpi);
/* dcs */
tmp = octstr_create("");
if(msg->sms.coding != SMS_PARAM_UNDEFINED)
OCTSTR_APPEND_XML_NUMBER(tmp, "coding", msg->sms.coding);
if(msg->sms.mclass != SMS_PARAM_UNDEFINED)
OCTSTR_APPEND_XML_NUMBER(tmp, "mclass", msg->sms.mclass);
if(msg->sms.alt_dcs != SMS_PARAM_UNDEFINED)
OCTSTR_APPEND_XML_NUMBER(tmp, "alt-dcs", msg->sms.alt_dcs);
if(msg->sms.mwi != SMS_PARAM_UNDEFINED)
OCTSTR_APPEND_XML_NUMBER(tmp, "mwi", msg->sms.mwi);
if(msg->sms.compress != SMS_PARAM_UNDEFINED)
OCTSTR_APPEND_XML_NUMBER(tmp, "compress", msg->sms.compress);
if(octstr_len(tmp))
OCTSTR_APPEND_XML(xml, "dcs", tmp)
octstr_destroy(tmp);
/* deferred (timing/delay) */
tmp = octstr_create("");
if(msg->sms.deferred != SMS_PARAM_UNDEFINED)
OCTSTR_APPEND_XML_NUMBER(tmp, "delay", msg->sms.deferred);
if(octstr_len(tmp))
OCTSTR_APPEND_XML(xml, "timing", tmp)
octstr_destroy(tmp);
/* validity (vp/delay) */
tmp = octstr_create("");
if(msg->sms.validity != SMS_PARAM_UNDEFINED)
OCTSTR_APPEND_XML_NUMBER(tmp, "delay", msg->sms.validity);
if(octstr_len(tmp))
OCTSTR_APPEND_XML(xml, "vp", tmp)
octstr_destroy(tmp);
/* time (at) */
tm = gw_gmtime(msg->sms.time);
tmp = octstr_format("%04d%02d"
"%02d%02d%02d"
"%02d0",
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec);
OCTSTR_APPEND_XML(xml, "at", tmp);
octstr_destroy(tmp);
/* smsc */
if (octstr_len(msg->sms.smsc_id)) {
tmp = octstr_create("");
if(octstr_len(msg->sms.smsc_id))
OCTSTR_APPEND_XML(tmp, "account", msg->sms.smsc_id);
if(octstr_len(tmp))
OCTSTR_APPEND_XML(xml, "from", tmp);
O_DESTROY(tmp);
}
/* service = to/service */
if(octstr_len(msg->sms.service)) {
tmp = octstr_create("");
OCTSTR_APPEND_XML(tmp, "service", msg->sms.service);
if(octstr_len(tmp))
OCTSTR_APPEND_XML(xml, "to", tmp);
O_DESTROY(tmp);
}
/* End XML */
octstr_append(xml, octstr_imm("\t\n"));
octstr_append(xml, octstr_imm("\n"));
if(msg->sms.msgdata != NULL)
octstr_destroy(msg->sms.msgdata);
msg->sms.msgdata = xml;
debug("sms", 0, "XMLBuild: XML: <%s>", octstr_get_cstr(msg->sms.msgdata));
id = remember_receiver(msg, trans, HTTP_METHOD_POST, pattern, request_headers, msg->sms.msgdata, 0);
semaphore_down(max_pending_requests);
http_start_request(caller, HTTP_METHOD_POST, pattern, request_headers,
msg->sms.msgdata, 1, id, NULL);
octstr_destroy(pattern);
http_destroy_headers(request_headers);
*result = NULL;
return 0;
case TRANSTYPE_SENDSMS:
error(0, "Got URL translation type SENDSMS for incoming message.");
alog("SMS request sender:%s request: '%s' FAILED bad translation",
octstr_get_cstr(msg->sms.receiver),
octstr_get_cstr(msg->sms.msgdata));
octstr_destroy(pattern);
return -1;
default:
error(0, "Unknown URL translation type %d", urltrans_type(trans));
alog("SMS request sender:%s request: '%s' FAILED unknown translation",
octstr_get_cstr(msg->sms.receiver),
octstr_get_cstr(msg->sms.msgdata));
octstr_destroy(pattern);
return -1;
}
return 1;
}
static void obey_request_thread(void *arg)
{
Msg *msg, *mack, *reply_msg;
Octstr *tmp, *reply;
URLTranslation *trans;
Octstr *p;
int ret, dreport=0;
while ((msg = gwlist_consume(smsbox_requests)) != NULL) {
if (msg->sms.sms_type == report_mo)
dreport = 1;
else
dreport = 0;
/* Recode to iso-8859-1 the MO message if possible */
if (mo_recode && msg->sms.coding == DC_UCS2) {
int converted = 0;
Octstr *text;
text = octstr_duplicate(msg->sms.msgdata);
if(0 == octstr_recode (octstr_imm("iso-8859-1"), octstr_imm("UTF-16BE"), text)) {
if(octstr_search(text, octstr_imm(""), 0) == -1) {
/* XXX I'm trying to search for xxx; text, which indicates that the
* text couldn't be recoded.
* We should use other function to do the recode or detect it using
* other method */
info(0, "MO message converted from UCS-2 to ISO-8859-1");
octstr_destroy(msg->sms.msgdata);
msg->sms.msgdata = octstr_duplicate(text);
msg->sms.charset = octstr_create("ISO-8859-1");
msg->sms.coding = DC_7BIT;
converted=1;
} else {
octstr_destroy(text);
text = octstr_duplicate(msg->sms.msgdata);
}
}
if(!converted && 0 == octstr_recode (octstr_imm("UTF-8"), octstr_imm("UTF-16BE"), text)) {
if(octstr_search(text, octstr_imm(""), 0) == -1) {
/* XXX I'm trying to search for xxx; text, which indicates that the
* text couldn't be recoded.
* We should use other function to do the recode or detect it using
* other method */
info(0, "MO message converted from UCS-2 to UTF-8");
octstr_destroy(msg->sms.msgdata);
msg->sms.msgdata = octstr_duplicate(text);
msg->sms.charset = octstr_create("UTF-8");
msg->sms.coding = DC_7BIT;
/* redundant, but this code could be used if another convertion is required
converted=1;
} else {
octstr_destroy(text);
text = octstr_duplicate(msg->sms.msgdata);
*/
}
}
octstr_destroy(text);
}
if (octstr_len(msg->sms.sender) == 0 ||
octstr_len(msg->sms.receiver) == 0) {
error(0, "smsbox_req_thread: no sender/receiver, dump follows:");
msg_dump(msg, 0);
/*
* Send NACK to bearerbox, otherwise message remains in store file.
*/
mack = msg_create(ack);
mack->ack.nack = ack_failed;
mack->ack.time = msg->sms.time;
uuid_copy(mack->ack.id, msg->sms.id);
write_to_bearerbox(mack);
msg_destroy(msg);
continue;
}
/* create ack message to be sent afterwards */
mack = msg_create(ack);
mack->ack.nack = ack_success;
mack->ack.time = msg->sms.time;
uuid_copy(mack->ack.id, msg->sms.id);
/*
* no smsbox services when we are doing ppg dlr - so trans would be
* NULL in this case.
*/
if (dreport) {
if (msg->sms.service == NULL || (msg->sms.service != NULL &&
ppg_service_name != NULL &&
octstr_compare(msg->sms.service, ppg_service_name) == 0)) {
trans = NULL;
} else {
trans = urltrans_find_service(translations, msg);
}
info(0, "Starting delivery report <%s> from <%s>",
octstr_get_cstr(msg->sms.service),
octstr_get_cstr(msg->sms.sender));
} else {
trans = urltrans_find(translations, msg->sms.msgdata,
msg->sms.smsc_id, msg->sms.sender, msg->sms.receiver);
if (trans == NULL) {
warning(0, "No translation found for <%s> from <%s> to <%s>",
octstr_get_cstr(msg->sms.msgdata),
octstr_get_cstr(msg->sms.sender),
octstr_get_cstr(msg->sms.receiver));
sms_swap(msg);
goto error;
}
info(0, "Starting to service <%s> from <%s> to <%s>",
octstr_get_cstr(msg->sms.msgdata),
octstr_get_cstr(msg->sms.sender),
octstr_get_cstr(msg->sms.receiver));
/*
* now, we change the sender (receiver now 'cause we swap them later)
* if faked-sender or similar set. Note that we ignore if the
* replacement fails.
*/
tmp = octstr_duplicate(msg->sms.sender);
p = urltrans_faked_sender(trans);
if (p != NULL) {
octstr_destroy(msg->sms.sender);
msg->sms.sender = octstr_duplicate(p);
} else if (global_sender != NULL) {
octstr_destroy(msg->sms.sender);
msg->sms.sender = octstr_duplicate(global_sender);
} else {
octstr_destroy(msg->sms.sender);
msg->sms.sender = octstr_duplicate(msg->sms.receiver);
}
octstr_destroy(msg->sms.receiver);
msg->sms.receiver = tmp;
msg->sms.sms_type = mt_reply;
}
/* TODO: check if the sender is approved to use this service */
if(msg->sms.service == NULL && trans != NULL)
msg->sms.service = octstr_duplicate(urltrans_name(trans));
ret = obey_request(&reply, trans, msg);
if (ret != 0) {
if (ret == -1) {
error:
error(0, "request failed");
/* XXX this can be something different, according to
urltranslation */
reply = octstr_duplicate(reply_requestfailed);
trans = NULL; /* do not use any special translation */
}
if (!dreport) {
/* create reply message */
reply_msg = msg_create(sms);
reply_msg->sms.sms_type = mt_reply;
reply_msg->sms.sender = msg->sms.sender;
msg->sms.sender = NULL;
reply_msg->sms.receiver = msg->sms.receiver;
msg->sms.receiver = NULL;
reply_msg->sms.smsc_id = msg->sms.smsc_id;
msg->sms.smsc_id = NULL;
reply_msg->sms.msgdata = reply;
reply_msg->sms.time = time(NULL); /* set current time */
/* send message */
if (send_message(trans, reply_msg) < 0)
error(0, "request_thread: failed");
/* cleanup */
msg_destroy(reply_msg);
}
}
write_to_bearerbox(mack); /* implicit msg_destroy */
msg_destroy(msg);
}
}
/***********************************************************************
* HTTP sendsms interface.
*/
#ifdef HAVE_SECURITY_PAM_APPL_H /*Module for pam authentication */
/*
* Use PAM (Pluggable Authentication Module) to check sendsms authentication.
*/
typedef const struct pam_message pam_message_type;
static const char *PAM_username;
static const char *PAM_password;
static int PAM_conv (int num_msg, pam_message_type **msg,
struct pam_response **resp,
void *appdata_ptr)
{
int count = 0, replies = 0;
struct pam_response *repl = NULL;
int size = sizeof(struct pam_response);
#define GET_MEM \
repl = gw_realloc(repl, size); \
size += sizeof(struct pam_response)
#define COPY_STRING(s) (s) ? gw_strdup(s) : NULL
for (count = 0; count < num_msg; count++) {
switch (msg[count]->msg_style) {
case PAM_PROMPT_ECHO_ON:
GET_MEM;
repl[replies].resp_retcode = PAM_SUCCESS;
repl[replies++].resp = COPY_STRING(PAM_username);
/* PAM frees resp */
break;
case PAM_PROMPT_ECHO_OFF:
GET_MEM;
repl[replies].resp_retcode = PAM_SUCCESS;
repl[replies++].resp = COPY_STRING(PAM_password);
/* PAM frees resp */
break;
case PAM_TEXT_INFO:
warning(0, "unexpected message from PAM: %s", msg[count]->msg);
break;
case PAM_ERROR_MSG:
default:
/* Must be an error of some sort... */
error(0, "unexpected error from PAM: %s", msg[count]->msg);
gw_free(repl);
return PAM_CONV_ERR;
}
}
if (repl)
*resp = repl;
return PAM_SUCCESS;
}
static struct pam_conv PAM_conversation = {
&PAM_conv,
NULL
};
static int authenticate(const char *login, const char *passwd)
{
pam_handle_t *pamh;
int pam_error;
PAM_username = login;
PAM_password = passwd;
pam_error = pam_start("kannel", login, &PAM_conversation, &pamh);
if (pam_error != PAM_SUCCESS ||
(pam_error = pam_authenticate(pamh, 0)) != PAM_SUCCESS) {
pam_end(pamh, pam_error);
return 0;
}
pam_end(pamh, PAM_SUCCESS);
info(0, "sendsms used by <%s>", login);
return 1;
}
/*
* Check for matching username and password for requests.
* Return an URLTranslation if successful NULL otherwise.
*/
static int pam_authorise_user(List *list)
{
Octstr *val, *user = NULL;
char *pwd, *login;
int result;
if ((user = http_cgi_variable(list, "user")) == NULL &&
(user = http_cgi_variable(list, "username"))==NULL)
return 0;
login = octstr_get_cstr(user);
if ((val = http_cgi_variable(list, "password")) == NULL &&
(val = http_cgi_variable(list, "pass")) == NULL)
return 0;
pwd = octstr_get_cstr(val);
result = authenticate(login, pwd);
return result;
}
#endif /* HAVE_SECURITY_PAM_APPL_H */
static Octstr* store_uuid(Msg *msg)
{
char id[UUID_STR_LEN + 1];
Octstr *stored_uuid;
gw_assert(msg != NULL);
gw_assert(!immediate_sendsms_reply);
uuid_unparse(msg->sms.id, id);
stored_uuid = octstr_create(id);
debug("sms.http", 0, "Stored UUID %s", octstr_get_cstr(stored_uuid));
/* this octstr is then used to store the HTTP client into
* client_dict, if need to, in sendsms_thread */
return stored_uuid;
}
static Octstr *smsbox_req_handle(URLTranslation *t, Octstr *client_ip,
HTTPClient *client,
Octstr *from, Octstr *to, Octstr *text,
Octstr *charset, Octstr *udh, Octstr *smsc,
int mclass, int mwi, int coding, int compress,
int validity, int deferred,
int *status, int dlr_mask, Octstr *dlr_url,
Octstr *account, int pid, int alt_dcs, int rpi,
List *receiver, Octstr *binfo, int priority)
{
Msg *msg = NULL;
Octstr *newfrom = NULL;
Octstr *returnerror = NULL;
Octstr *receiv;
Octstr *stored_uuid = NULL;
List *failed_id = NULL;
List *allowed = NULL;
List *denied = NULL;
int no_recv, ret = 0, i;
long del;
/*
* Multi-cast messages with several receivers in 'to' are handled
* in a loop. We only change sms.time and sms.receiver within the
* loop below, because everything else is identical for all receivers.
* If receiver is not null, to list is already present on it
*/
if(receiver == NULL) {
receiver = octstr_split_words(to);
}
no_recv = gwlist_len(receiver);
/*
* check if UDH length is legal, or otherwise discard the
* message, to prevent intentional buffer overflow schemes
*/
if (udh != NULL && (octstr_len(udh) != octstr_get_char(udh, 0) + 1)) {
returnerror = octstr_create("UDH field misformed, rejected");
goto field_error;
}
if (udh != NULL && octstr_len(udh) > MAX_SMS_OCTETS) {
returnerror = octstr_create("UDH field is too long, rejected");
goto field_error;
}
/*
* Check for white and black lists, first for the URLTranlation
* lists and then for the global lists.
*
* Set the 'allowed' and 'denied' lists accordingly to process at
* least all allowed receiver messages. This is a constrain
* walk through all disallowing rules within the lists.
*/
allowed = gwlist_create();
denied = gwlist_create();
for (i = 0; i < no_recv; i++) {
receiv = gwlist_get(receiver, i);
/*
* Check if there are any illegal characters in the 'to' scheme
*/
if (strspn(octstr_get_cstr(receiv), sendsms_number_chars) < octstr_len(receiv)) {
info(0,"Illegal characters in 'to' string ('%s') vs '%s'",
octstr_get_cstr(receiv), sendsms_number_chars);
gwlist_append_unique(denied, receiv, octstr_item_match);
}
/*
* First of all fill the two lists systematicaly by the rules,
* then we will revice the lists.
*/
if (urltrans_white_list(t) &&
numhash_find_number(urltrans_white_list(t), receiv) < 1) {
info(0, "Number <%s> is not in white-list, message discarded",
octstr_get_cstr(receiv));
gwlist_append_unique(denied, receiv, octstr_item_match);
} else {
gwlist_append_unique(allowed, receiv, octstr_item_match);
}
if (urltrans_white_list_regex(t) &&
gw_regex_match_pre(urltrans_white_list_regex(t), receiv) == 0) {
info(0, "Number <%s> is not in white-list-regex, message discarded",
octstr_get_cstr(receiv));
gwlist_append_unique(denied, receiv, octstr_item_match);
} else {
gwlist_append_unique(allowed, receiv, octstr_item_match);
}
if (urltrans_black_list(t) &&
numhash_find_number(urltrans_black_list(t), receiv) == 1) {
info(0, "Number <%s> is in black-list, message discarded",
octstr_get_cstr(receiv));
gwlist_append_unique(denied, receiv, octstr_item_match);
} else {
gwlist_append_unique(allowed, receiv, octstr_item_match);
}
if (urltrans_black_list_regex(t) &&
gw_regex_match_pre(urltrans_black_list_regex(t), receiv) == 1) {
info(0, "Number <%s> is in black-list-regex, message discarded",
octstr_get_cstr(receiv));
gwlist_append_unique(denied, receiv, octstr_item_match);
} else {
gwlist_append_unique(allowed, receiv, octstr_item_match);
}
if (white_list &&
numhash_find_number(white_list, receiv) < 1) {
info(0, "Number <%s> is not in global white-list, message discarded",
octstr_get_cstr(receiv));
gwlist_append_unique(denied, receiv, octstr_item_match);
} else {
gwlist_append_unique(allowed, receiv, octstr_item_match);
}
if (white_list_regex &&
gw_regex_match_pre(white_list_regex, receiv) == 0) {
info(0, "Number <%s> is not in global white-list-regex, message discarded",
octstr_get_cstr(receiv));
gwlist_append_unique(denied, receiv, octstr_item_match);
} else {
gwlist_append_unique(allowed, receiv, octstr_item_match);
}
if (black_list &&
numhash_find_number(black_list, receiv) == 1) {
info(0, "Number <%s> is in global black-list, message discarded",
octstr_get_cstr(receiv));
gwlist_append_unique(denied, receiv, octstr_item_match);
} else {
gwlist_append_unique(allowed, receiv, octstr_item_match);
}
if (black_list_regex &&
gw_regex_match_pre(black_list_regex, receiv) == 1) {
info(0, "Number <%s> is in global black-list-regex, message discarded",
octstr_get_cstr(receiv));
gwlist_append_unique(denied, receiv, octstr_item_match);
} else {
gwlist_append_unique(allowed, receiv, octstr_item_match);
}
}
/*
* Now we have to revise the 'allowed' and 'denied' lists by walking
* the 'denied' list and check if items are also present in 'allowed',
* then we will discard them from 'allowed'.
*/
for (i = 0; i < gwlist_len(denied); i++) {
receiv = gwlist_get(denied, i);
del = gwlist_delete_matching(allowed, receiv, octstr_item_match);
}
/* have all receivers been denied by list rules?! */
if (gwlist_len(allowed) == 0) {
returnerror = octstr_create("Number(s) has/have been denied by white- and/or black-lists.");
goto field_error;
}
if (urltrans_faked_sender(t) != NULL) {
/* discard previous from */
newfrom = octstr_duplicate(urltrans_faked_sender(t));
} else if (octstr_len(from) > 0) {
newfrom = octstr_duplicate(from);
} else if (urltrans_default_sender(t) != NULL) {
newfrom = octstr_duplicate(urltrans_default_sender(t));
} else if (global_sender != NULL) {
newfrom = octstr_duplicate(global_sender);
} else {
returnerror = octstr_create("Sender missing and no global set, rejected");
goto field_error;
}
info(0, "sendsms sender:<%s:%s> (%s) to:<%s> msg:<%s>",
octstr_get_cstr(urltrans_username(t)),
octstr_get_cstr(newfrom),
octstr_get_cstr(client_ip),
( to == NULL ? "multi-cast" : octstr_get_cstr(to) ),
( text == NULL ? "" : octstr_get_cstr(text) ));
/*
* Create the msg structure and fill the types. Note that sms.receiver
* and sms.time are set in the multi-cast support loop below.
*/
msg = msg_create(sms);
msg->sms.service = octstr_duplicate(urltrans_name(t));
msg->sms.sms_type = mt_push;
msg->sms.sender = octstr_duplicate(newfrom);
if(octstr_len(account)) {
if(octstr_len(account) <= 32 &&
octstr_search_chars(account, octstr_imm("[]\n\r"), 0) == -1) {
msg->sms.account = account ? octstr_duplicate(account) : NULL;
} else {
returnerror = octstr_create("Account field misformed, rejected");
goto field_error;
}
}
msg->sms.msgdata = text ? octstr_duplicate(text) : octstr_create("");
msg->sms.udhdata = udh ? octstr_duplicate(udh) : octstr_create("");
if (octstr_len(binfo))
msg->sms.binfo = octstr_duplicate(binfo);
if(octstr_len(dlr_url)) {
if(octstr_len(dlr_url) < 8) { /* http(s):// */
returnerror = octstr_create("DLR-URL field misformed, rejected");
goto field_error;
} else {
Octstr *tmp;
tmp = octstr_copy(dlr_url, 0, 7);
if(octstr_case_compare(tmp, octstr_imm("http://")) == 0) {
msg->sms.dlr_url = octstr_duplicate(dlr_url);
} else {
O_DESTROY(tmp);
tmp = octstr_copy(dlr_url, 0, 8);
if(octstr_case_compare(tmp, octstr_imm("https://")) != 0) {
returnerror = octstr_create("DLR-URL field misformed, rejected");
O_DESTROY(tmp);
goto field_error;
}
#ifdef HAVE_LIBSSL
msg->sms.dlr_url = octstr_duplicate(dlr_url);
#else /* HAVE_LIBSSL */
else {
warning(0, "DLR-URL with https but SSL not supported, url is <%s>",
octstr_get_cstr(dlr_url));
}
#endif /* HAVE_LIBSSL */
}
O_DESTROY(tmp);
}
} else {
msg->sms.dlr_url = octstr_create("");
}
if ( dlr_mask < -1 || dlr_mask > 31 ) { /* 00011111 */
returnerror = octstr_create("DLR-Mask field misformed, rejected");
goto field_error;
}
msg->sms.dlr_mask = dlr_mask;
if ( mclass < -1 || mclass > 3 ) {
returnerror = octstr_create("MClass field misformed, rejected");
goto field_error;
}
msg->sms.mclass = mclass;
if ( pid < -1 || pid > 255 ) {
returnerror = octstr_create("PID field misformed, rejected");
goto field_error;
}
msg->sms.pid = pid;
if ( rpi < -1 || rpi > 2) {
returnerror = octstr_create("RPI field misformed, rejected");
goto field_error;
}
msg->sms.rpi = rpi;
if ( alt_dcs < -1 || alt_dcs > 1 ) {
returnerror = octstr_create("Alt-DCS field misformed, rejected");
goto field_error;
}
msg->sms.alt_dcs = alt_dcs;
if ( mwi < -1 || mwi > 7 ) {
returnerror = octstr_create("MWI field misformed, rejected");
goto field_error;
}
msg->sms.mwi = mwi;
if ( coding < -1 || coding > 2 ) {
returnerror = octstr_create("Coding field misformed, rejected");
goto field_error;
}
msg->sms.coding = coding;
if ( compress < -1 || compress > 1 ) {
returnerror = octstr_create("Compress field misformed, rejected");
goto field_error;
}
msg->sms.compress = compress;
/* Compatibility Mode */
if ( msg->sms.coding == DC_UNDEF) {
if(octstr_len(udh))
msg->sms.coding = DC_8BIT;
else
msg->sms.coding = DC_7BIT;
}
if ( validity < -1 ) {
returnerror = octstr_create("Validity field misformed, rejected");
goto field_error;
}
msg->sms.validity = validity;
if ( deferred < -1 ) {
returnerror = octstr_create("Deferred field misformed, rejected");
goto field_error;
}
msg->sms.deferred = deferred;
if (priority != SMS_PARAM_UNDEFINED && (priority < 0 || priority > 3)) {
returnerror = octstr_create("Priority field misformed, rejected");
goto field_error;
}
msg->sms.priority = priority;
/* new smsc-id argument - we should check this one, if able,
but that's advanced logics -- Kalle */
if (urltrans_forced_smsc(t)) {
msg->sms.smsc_id = octstr_duplicate(urltrans_forced_smsc(t));
if (smsc)
info(0, "send-sms request smsc id ignored, "
"as smsc id forced to %s",
octstr_get_cstr(urltrans_forced_smsc(t)));
} else if (smsc) {
msg->sms.smsc_id = octstr_duplicate(smsc);
} else if (urltrans_default_smsc(t)) {
msg->sms.smsc_id = octstr_duplicate(urltrans_default_smsc(t));
} else
msg->sms.smsc_id = NULL;
if (charset_processing(charset, msg->sms.msgdata, msg->sms.coding) == -1) {
returnerror = octstr_create("Charset or body misformed, rejected");
goto field_error;
}
msg->sms.receiver = NULL;
/*
* All checks are done, now add multi-cast request support by
* looping through 'allowed'. This should work for any
* number of receivers within 'to'. If the message fails append
* it to 'failed_id'.
*/
failed_id = gwlist_create();
if (!immediate_sendsms_reply) {
stored_uuid = store_uuid(msg);
dict_put(client_dict, stored_uuid, client);
}
while ((receiv = gwlist_extract_first(allowed)) != NULL) {
O_DESTROY(msg->sms.receiver);
msg->sms.receiver = octstr_duplicate(receiv);
msg->sms.time = time(NULL);
/* send the message and return number of splits */
ret = send_message(t, msg);
if (ret == -1) {
/* add the receiver to the failed list */
gwlist_append(failed_id, receiv);
} else {
/* log the sending as successful for this particular message */
alog("send-SMS request added - sender:%s:%s %s target:%s request: '%s'",
octstr_get_cstr(urltrans_username(t)),
octstr_get_cstr(newfrom), octstr_get_cstr(client_ip),
octstr_get_cstr(receiv),
udh == NULL ? ( text == NULL ? "" : octstr_get_cstr(text) ) : "<< UDH >>");
}
}
if (gwlist_len(failed_id) > 0)
goto transmit_error;
*status = HTTP_ACCEPTED;
returnerror = octstr_create("Sent.");
/*
* Append all denied receivers to the returned body in case this is
* a multi-cast send request
*/
if (gwlist_len(denied) > 0) {
octstr_format_append(returnerror, " Denied receivers are:");
while ((receiv = gwlist_extract_first(denied)) != NULL) {
octstr_format_append(returnerror, " %s", octstr_get_cstr(receiv));
}
}
/*
* Append number of splits to returned body.
* This may be used by the calling client.
*/
if (ret > 1)
octstr_format_append(returnerror, " Message splits: %d", ret);
cleanup:
octstr_destroy(stored_uuid);
gwlist_destroy(failed_id, NULL);
gwlist_destroy(allowed, NULL);
gwlist_destroy(denied, NULL);
gwlist_destroy(receiver, octstr_destroy_item);
octstr_destroy(newfrom);
msg_destroy(msg);
return returnerror;
field_error:
alog("send-SMS request failed - %s",
octstr_get_cstr(returnerror));
*status = HTTP_BAD_REQUEST;
goto cleanup;
transmit_error:
error(0, "sendsms_request: failed");
*status = HTTP_INTERNAL_SERVER_ERROR;
returnerror = octstr_create("Sending failed.");
if (!immediate_sendsms_reply)
dict_remove(client_dict, stored_uuid);
/*
* Append all receivers to the returned body in case this is
* a multi-cast send request
*/
if (no_recv > 1) {
octstr_format_append(returnerror, " Failed receivers are:");
while ((receiv = gwlist_extract_first(failed_id)) != NULL) {
octstr_format_append(returnerror, " %s", octstr_get_cstr(receiv));
}
}
goto cleanup;
}
/*
* new authorisation, usable by POST and GET
*/
static URLTranslation *authorise_username(Octstr *username, Octstr *password,
Octstr *client_ip)
{
URLTranslation *t = NULL;
if (username == NULL || password == NULL)
return NULL;
if ((t = urltrans_find_username(translations, username))==NULL)
return NULL;
if (octstr_compare(password, urltrans_password(t))!=0)
return NULL;
else {
Octstr *allow_ip = urltrans_allow_ip(t);
Octstr *deny_ip = urltrans_deny_ip(t);
if (is_allowed_ip(allow_ip, deny_ip, client_ip) == 0) {
warning(0, "Non-allowed connect tried by <%s> from <%s>, ignored",
octstr_get_cstr(username), octstr_get_cstr(client_ip));
return NULL;
}
}
info(0, "sendsms used by <%s>", octstr_get_cstr(username));
return t;
}
/*
* Authentication whith the database of Kannel.
* Check for matching username and password for requests.
* Return an URLTranslation if successful NULL otherwise.
*/
static URLTranslation *default_authorise_user(List *list, Octstr *client_ip)
{
Octstr *pass, *user = NULL;
if ((user = http_cgi_variable(list, "username")) == NULL)
user = http_cgi_variable(list, "user");
if ((pass = http_cgi_variable(list, "password")) == NULL)
pass = http_cgi_variable(list, "pass");
return authorise_username(user, pass, client_ip);
}
static URLTranslation *authorise_user(List *list, Octstr *client_ip)
{
#ifdef HAVE_SECURITY_PAM_APPL_H
URLTranslation *t;
t = urltrans_find_username(translations, octstr_imm("pam"));
if (t != NULL) {
if (pam_authorise_user(list))
return t;
else
return NULL;
} else
return default_authorise_user(list, client_ip);
#else
return default_authorise_user(list, client_ip);
#endif
}
/*
* Create and send an SMS message from an HTTP request.
* Args: args contains the CGI parameters
*/
static Octstr *smsbox_req_sendsms(List *args, Octstr *client_ip, int *status,
HTTPClient *client)
{
URLTranslation *t = NULL;
Octstr *tmp_string;
Octstr *from, *to, *charset, *text, *udh, *smsc, *dlr_url, *account;
Octstr *binfo;
int dlr_mask, mclass, mwi, coding, compress, validity, deferred, pid;
int alt_dcs, rpi, priority;
from = to = udh = text = smsc = account = dlr_url = charset = binfo = NULL;
mclass = mwi = coding = compress = validity = deferred = dlr_mask =
pid = alt_dcs = rpi = priority = SMS_PARAM_UNDEFINED;
/* check the username and password */
t = authorise_user(args, client_ip);
if (t == NULL) {
*status = HTTP_FORBIDDEN;
return octstr_create("Authorization failed for sendsms");
}
udh = http_cgi_variable(args, "udh");
text = http_cgi_variable(args, "text");
charset = http_cgi_variable(args, "charset");
smsc = http_cgi_variable(args, "smsc");
from = http_cgi_variable(args, "from");
to = http_cgi_variable(args, "to");
account = http_cgi_variable(args, "account");
binfo = http_cgi_variable(args, "binfo");
dlr_url = http_cgi_variable(args, "dlr-url");
if(dlr_url == NULL) { /* deprecated dlrurl without "-" */
dlr_url = http_cgi_variable(args, "dlrurl");
if(dlr_url != NULL)
warning(0, " field used and deprecated. Please use dlr-url instead.");
}
tmp_string = http_cgi_variable(args, "dlr-mask");
if(tmp_string == NULL) { /* deprecated dlrmask without "-" */
tmp_string = http_cgi_variable(args, "dlrmask");
if(tmp_string != NULL)
warning(0, " field used and deprecated. Please use dlr-mask instead.");
}
if(tmp_string != NULL)
sscanf(octstr_get_cstr(tmp_string),"%d", &dlr_mask);
tmp_string = http_cgi_variable(args, "mclass");
if(tmp_string != NULL)
sscanf(octstr_get_cstr(tmp_string),"%d", &mclass);
tmp_string = http_cgi_variable(args, "pid");
if(tmp_string != NULL)
sscanf(octstr_get_cstr(tmp_string),"%d", &pid);
tmp_string = http_cgi_variable(args, "rpi");
if(tmp_string != NULL)
sscanf(octstr_get_cstr(tmp_string),"%d", &rpi);
tmp_string = http_cgi_variable(args, "alt-dcs");
if(tmp_string != NULL)
sscanf(octstr_get_cstr(tmp_string),"%d", &alt_dcs);
tmp_string = http_cgi_variable(args, "mwi");
if(tmp_string != NULL)
sscanf(octstr_get_cstr(tmp_string),"%d", &mwi);
tmp_string = http_cgi_variable(args, "coding");
if(tmp_string != NULL)
sscanf(octstr_get_cstr(tmp_string),"%d", &coding);
tmp_string = http_cgi_variable(args, "compress");
if(tmp_string != NULL)
sscanf(octstr_get_cstr(tmp_string),"%d", &compress);
tmp_string = http_cgi_variable(args, "validity");
if(tmp_string != NULL)
sscanf(octstr_get_cstr(tmp_string),"%d", &validity);
tmp_string = http_cgi_variable(args, "deferred");
if(tmp_string != NULL)
sscanf(octstr_get_cstr(tmp_string),"%d", &deferred);
tmp_string = http_cgi_variable(args, "priority");
if(tmp_string != NULL)
sscanf(octstr_get_cstr(tmp_string),"%d", &priority);
/*
* we required "to" to be defined
*/
if (to == NULL) {
error(0, "%s got insufficient headers ( is NULL)",
octstr_get_cstr(sendsms_url));
*status = HTTP_BAD_REQUEST;
return octstr_create("Missing receiver number, rejected");
}
else if (octstr_len(to) == 0) {
error(0, "%s got empty cgi variable", octstr_get_cstr(sendsms_url));
*status = HTTP_BAD_REQUEST;
return octstr_create("Empty receiver number not allowed, rejected");
}
return smsbox_req_handle(t, client_ip, client, from, to, text, charset, udh,
smsc, mclass, mwi, coding, compress, validity,
deferred, status, dlr_mask, dlr_url, account,
pid, alt_dcs, rpi, NULL, binfo, priority);
}
/*
* Create and send an SMS message from an HTTP request.
* Args: args contains the CGI parameters
*/
static Octstr *smsbox_sendsms_post(List *headers, Octstr *body,
Octstr *client_ip, int *status,
HTTPClient *client)
{
URLTranslation *t = NULL;
Octstr *user, *pass, *ret, *type;
List *tolist;
Octstr *text_html, *text_plain, *text_wml, *text_xml, *octet_stream;
Octstr *text;
Octstr *from, *to, *udh, *smsc, *charset, *dlr_url, *account, *binfo;
int dlr_mask, mclass, mwi, coding, compress, validity, deferred;
int pid, alt_dcs, rpi, priority;
text_html = octstr_imm("text/html");
text_wml = octstr_imm("text/vnd.wap.wml");
text_plain = octstr_imm("text/plain");
text_xml = octstr_imm("text/xml");
octet_stream = octstr_imm("application/octet-stream");
user = pass = ret = type = NULL;
tolist = NULL;
from = to = udh = smsc = account = dlr_url = charset = binfo = NULL;
mclass = mwi = coding = compress = validity = deferred = dlr_mask =
pid = alt_dcs = rpi = priority = SMS_PARAM_UNDEFINED;
http_header_get_content_type(headers, &type, &charset);
if (octstr_case_compare(type, text_html) == 0 ||
octstr_case_compare(type, text_wml) == 0) {
text = html_to_sms(body);
octstr_strip_blanks(text);
octstr_destroy(body);
body = text;
get_x_kannel_from_headers(headers, &from, &to, &udh,
&user, &pass, &smsc, &mclass, &mwi,
&coding, &compress, &validity,
&deferred, &dlr_mask, &dlr_url,
&account, &pid, &alt_dcs, &rpi,
&binfo, &priority);
} else if (octstr_case_compare(type, text_plain) == 0 ||
octstr_case_compare(type, octet_stream) == 0) {
get_x_kannel_from_headers(headers, &from, &to, &udh,
&user, &pass, &smsc, &mclass, &mwi,
&coding, &compress, &validity,
&deferred, &dlr_mask, &dlr_url,
&account, &pid, &alt_dcs, &rpi,
&binfo, &priority);
} else if (octstr_case_compare(type, text_xml) == 0) {
get_x_kannel_from_xml(mt_push, &type, &body, headers,
&from, &to, &udh, &user, &pass, &smsc, &mclass,
&mwi, &coding, &compress, &validity, &deferred,
&dlr_mask, &dlr_url, &account, &pid, &alt_dcs,
&rpi, &tolist, &charset, &binfo, &priority);
} else {
*status = HTTP_BAD_REQUEST;
ret = octstr_create("Invalid content-type");
goto error;
}
if (charset_processing(charset, body, coding) == -1) {
*status = HTTP_BAD_REQUEST;
ret = octstr_create("Invalid charset");
goto error2;
}
/* check the username and password */
t = authorise_username(user, pass, client_ip);
if (t == NULL) {
*status = HTTP_FORBIDDEN;
ret = octstr_create("Authorization failed for sendsms");
}
else if (to == NULL && tolist == NULL) {
error(0, "%s got insufficient headers ( and are NULL)",
octstr_get_cstr(sendsms_url));
*status = HTTP_BAD_REQUEST;
ret = octstr_create("Missing receiver(s) number(s), rejected");
}
else if (to != NULL && octstr_len(to) == 0) {
error(0, "%s got empty cgi variable", octstr_get_cstr(sendsms_url));
*status = HTTP_BAD_REQUEST;
return octstr_create("Empty receiver number not allowed, rejected");
}
else {
if (octstr_case_compare(type,
octstr_imm("application/octet-stream")) == 0) {
if (coding == DC_UNDEF)
coding = DC_8BIT; /* XXX Force UCS-2 with DC Field */
} else if (octstr_case_compare(type,
octstr_imm("text/plain")) == 0) {
if (coding == DC_UNDEF)
coding = DC_7BIT;
} else {
error(0, "%s got weird content type %s", octstr_get_cstr(sendsms_url),
octstr_get_cstr(type));
*status = HTTP_UNSUPPORTED_MEDIA_TYPE;
ret = octstr_create("Unsupported content-type, rejected");
}
if (ret == NULL)
ret = smsbox_req_handle(t, client_ip, client, from, to, body, charset,
udh, smsc, mclass, mwi, coding, compress,
validity, deferred, status, dlr_mask,
dlr_url, account, pid, alt_dcs, rpi, tolist,
binfo, priority);
}
error2:
octstr_destroy(user);
octstr_destroy(pass);
octstr_destroy(from);
octstr_destroy(to);
octstr_destroy(udh);
octstr_destroy(smsc);
octstr_destroy(dlr_url);
octstr_destroy(account);
octstr_destroy(binfo);
error:
octstr_destroy(type);
octstr_destroy(charset);
return ret;
}
/*
* Create and send an SMS message from a XML-RPC request.
* Answer with a valid XML-RPC response for a successful request.
*
* function signature: boolean sms.send(struct)
*
* The MUST contain at least 's with name 'username',
* 'password', 'to' and MAY contain additional 's with name
* 'from', 'account', 'smsc', 'udh', 'dlrmask', 'dlrurl'. All values
* are of type string.
*/
static Octstr *smsbox_xmlrpc_post(List *headers, Octstr *body,
Octstr *client_ip, int *status)
{
Octstr *ret, *type, *user, *pass;
Octstr *from, *to, *udh, *smsc, *charset, *dlr_url, *account, *binfo;
Octstr *output;
Octstr *method_name;
XMLRPCDocument *msg;
int dlr_mask, mclass, mwi, coding, compress, validity,
deferred, pid, alt_dcs, rpi;
from = to = udh = smsc = account = dlr_url = charset = binfo = NULL;
mclass = mwi = coding = compress = validity = deferred = dlr_mask =
pid = alt_dcs = rpi = -1;
user = pass = ret = NULL;
/*
* check if the content type is valid for this request
*/
http_header_get_content_type(headers, &type, &charset);
if (octstr_case_compare(type, octstr_imm("text/xml")) != 0) {
error(0, "Unsupported content-type '%s'", octstr_get_cstr(type));
*status = HTTP_BAD_REQUEST;
ret = octstr_format("Unsupported content-type '%s'", octstr_get_cstr(type));
} else {
/*
* parse the body of the request and check if it is a valid XML-RPC
* structure
*/
msg = xmlrpc_parse_call(body);
if ((xmlrpc_parse_status(msg) != XMLRPC_COMPILE_OK) &&
((output = xmlrpc_parse_error(msg)) != NULL)) {
/* parse failure */
error(0, "%s", octstr_get_cstr(output));
*status = HTTP_BAD_REQUEST;
ret = octstr_format("%s", octstr_get_cstr(output));
octstr_destroy(output);
} else {
/*
* at least the structure has been valid, now check for the
* required methodName and the required variables
*/
if (octstr_case_compare((method_name = xmlrpc_get_call_name(msg)),
octstr_imm("sms.send")) != 0) {
error(0, "Unknown method name '%s'", octstr_get_cstr(method_name));
*status = HTTP_BAD_REQUEST;
ret = octstr_format("Unkown method name '%s'",
octstr_get_cstr(method_name));
} else {
/*
* TODO: check for the required struct members
*/
}
}
xmlrpc_destroy_call(msg);
}
return ret;
}
/*
* Create and send an SMS OTA (auto configuration) message from an HTTP
* request. If cgivar "text" is present, use it as a xml configuration source,
* otherwise read the configuration from the configuration file.
* Args: list contains the CGI parameters
*/
static Octstr *smsbox_req_sendota(List *list, Octstr *client_ip, int *status,
HTTPClient *client)
{
Octstr *id, *from, *phonenumber, *smsc, *ota_doc, *doc_type, *account;
CfgGroup *grp;
Octstr *returnerror;
Octstr *stored_uuid = NULL;
List *grplist;
Octstr *p;
URLTranslation *t;
Msg *msg;
int ret, ota_type;
id = phonenumber = smsc = account = NULL;
/* check the username and password */
t = authorise_user(list, client_ip);
if (t == NULL) {
*status = HTTP_FORBIDDEN;
return octstr_create("Authorization failed for sendota");
}
if ((phonenumber = http_cgi_variable(list, "to")) == NULL) {
if ((phonenumber = http_cgi_variable(list, "phonenumber")) == NULL) {
error(0, "%s needs a valid phone number.", octstr_get_cstr(sendota_url));
*status = HTTP_BAD_REQUEST;
return octstr_create("Wrong sendota args.");
}
}
if (urltrans_faked_sender(t) != NULL) {
from = octstr_duplicate(urltrans_faked_sender(t));
} else if ((from = http_cgi_variable(list, "from")) != NULL &&
octstr_len(from) > 0) {
from = octstr_duplicate(from);
} else if (urltrans_default_sender(t) != NULL) {
from = octstr_duplicate(urltrans_default_sender(t));
} else if (global_sender != NULL) {
from = octstr_duplicate(global_sender);
} else {
*status = HTTP_BAD_REQUEST;
return octstr_create("Sender missing and no global set, rejected");
}
/* check does we have an external XML source for configuration */
if ((ota_doc = http_cgi_variable(list, "text")) != NULL) {
Octstr *sec, *pin;
/*
* We are doing the XML OTA compiler mode for this request
*/
debug("sms", 0, "OTA service with XML document");
ota_doc = octstr_duplicate(ota_doc);
if ((doc_type = http_cgi_variable(list, "type")) == NULL)
doc_type = octstr_format("%s", "settings");
else
doc_type = octstr_duplicate(doc_type);
if ((sec = http_cgi_variable(list, "sec")) == NULL)
sec = octstr_create("USERPIN");
else
sec = octstr_duplicate(sec);
if ((pin = http_cgi_variable(list, "pin")) == NULL)
pin = octstr_create("12345");
else
pin = octstr_duplicate(pin);
if ((ret = ota_pack_message(&msg, ota_doc, doc_type, from,
phonenumber, sec, pin)) < 0) {
*status = HTTP_BAD_REQUEST;
msg_destroy(msg);
if (ret == -2)
return octstr_create("Erroneous document type, cannot"
" compile\n");
else if (ret == -1)
return octstr_create("Erroneous ota source, cannot compile\n");
}
goto send;
} else {
/*
* We are doing the ota-settings or ota-bookmark group mode
* for this request.
*
* Check if a ota-setting ID has been given and decide which OTA
* properties to be send to the client otherwise try to find a
* ota-bookmark ID. If none is found then send the default
* ota-setting group, which is the first within the config file.
*/
id = http_cgi_variable(list, "otaid");
grplist = cfg_get_multi_group(cfg, octstr_imm("ota-setting"));
while (grplist && (grp = gwlist_extract_first(grplist)) != NULL) {
p = cfg_get(grp, octstr_imm("ota-id"));
if (id == NULL || (p != NULL && octstr_compare(p, id) == 0)) {
ota_type = 1;
goto found;
}
octstr_destroy(p);
}
gwlist_destroy(grplist, NULL);
grplist = cfg_get_multi_group(cfg, octstr_imm("ota-bookmark"));
while (grplist && (grp = gwlist_extract_first(grplist)) != NULL) {
p = cfg_get(grp, octstr_imm("ota-id"));
if (id == NULL || (p != NULL && octstr_compare(p, id) == 0)) {
ota_type = 0;
goto found;
}
octstr_destroy(p);
}
gwlist_destroy(grplist, NULL);
if (id != NULL)
error(0, "%s can't find any ota-setting or ota-bookmark group with ota-id '%s'.",
octstr_get_cstr(sendota_url), octstr_get_cstr(id));
else
error(0, "%s can't find any ota-setting group.", octstr_get_cstr(sendota_url));
octstr_destroy(from);
*status = HTTP_BAD_REQUEST;
return octstr_create("Missing ota-setting or ota-bookmark group.");
}
found:
octstr_destroy(p);
gwlist_destroy(grplist, NULL);
/* tokenize the OTA settings or bookmarks group and return the message */
if (ota_type)
msg = ota_tokenize_settings(grp, from, phonenumber);
else
msg = ota_tokenize_bookmarks(grp, from, phonenumber);
send:
/* we still need to check if smsc is forced for this */
smsc = http_cgi_variable(list, "smsc");
if (urltrans_forced_smsc(t)) {
msg->sms.smsc_id = octstr_duplicate(urltrans_forced_smsc(t));
if (smsc)
info(0, "send-sms request smsc id ignored, as smsc id forced to %s",
octstr_get_cstr(urltrans_forced_smsc(t)));
} else if (smsc) {
msg->sms.smsc_id = octstr_duplicate(smsc);
} else if (urltrans_default_smsc(t)) {
msg->sms.smsc_id = octstr_duplicate(urltrans_default_smsc(t));
} else
msg->sms.smsc_id = NULL;
account = http_cgi_variable(list, "account");
if (octstr_len(account) > 0)
msg->sms.account = octstr_duplicate(account);
octstr_dump(msg->sms.msgdata, 0);
info(0, "%s <%s> <%s>", octstr_get_cstr(sendota_url),
id ? octstr_get_cstr(id) : "", octstr_get_cstr(phonenumber));
if (!immediate_sendsms_reply) {
stored_uuid = store_uuid(msg);
dict_put(client_dict, stored_uuid, client);
}
ret = send_message(t, msg);
if (ret == -1) {
error(0, "sendota_request: failed");
*status = HTTP_INTERNAL_SERVER_ERROR;
returnerror = octstr_create("Sending failed.");
dict_remove(client_dict, stored_uuid);
} else {
*status = HTTP_ACCEPTED;
returnerror = octstr_create("Sent.");
}
msg_destroy(msg);
octstr_destroy(stored_uuid);
return returnerror;
}
/*
* Create and send an SMS OTA (auto configuration) message from an HTTP POST
* request. Take the X-Kannel-foobar HTTP headers as parameter information.
* Args: list contains the CGI parameters
*
* We still care about passed GET variable, in case the X-Kannel-foobar
* parameters are not used but the POST contains the XML body itself.
*/
static Octstr *smsbox_sendota_post(List *headers, Octstr *body,
Octstr *client_ip, int *status,
HTTPClient *client)
{
Octstr *name, *val, *ret;
Octstr *from, *to, *id, *user, *pass, *smsc;
Octstr *type, *charset, *doc_type, *ota_doc, *sec, *pin;
Octstr *stored_uuid = NULL;
URLTranslation *t;
Msg *msg;
long l;
int r;
id = from = to = user = pass = smsc = NULL;
doc_type = ota_doc = sec = pin = NULL;
/*
* process all special HTTP headers
*
* XXX can't we do this better?
* Obviously http_header_find_first() does this
*/
for (l = 0; l < gwlist_len(headers); l++) {
http_header_get(headers, l, &name, &val);
if (octstr_case_compare(name, octstr_imm("X-Kannel-OTA-ID")) == 0) {
id = octstr_duplicate(val);
octstr_strip_blanks(id);
}
else if (octstr_case_compare(name, octstr_imm("X-Kannel-From")) == 0) {
from = octstr_duplicate(val);
octstr_strip_blanks(from);
}
else if (octstr_case_compare(name, octstr_imm("X-Kannel-To")) == 0) {
to = octstr_duplicate(val);
octstr_strip_blanks(to);
}
else if (octstr_case_compare(name, octstr_imm("X-Kannel-Username")) == 0) {
user = octstr_duplicate(val);
octstr_strip_blanks(user);
}
else if (octstr_case_compare(name, octstr_imm("X-Kannel-Password")) == 0) {
pass = octstr_duplicate(val);
octstr_strip_blanks(pass);
}
else if (octstr_case_compare(name, octstr_imm("X-Kannel-SMSC")) == 0) {
smsc = octstr_duplicate(val);
octstr_strip_blanks(smsc);
}
else if (octstr_case_compare(name, octstr_imm("X-Kannel-SEC")) == 0) {
sec = octstr_duplicate(val);
octstr_strip_blanks(sec);
}
else if (octstr_case_compare(name, octstr_imm("X-Kannel-PIN")) == 0) {
pin = octstr_duplicate(val);
octstr_strip_blanks(pin);
}
}
/* apply defaults */
if (!sec)
sec = octstr_imm("USERPIN");
if (!pin)
pin = octstr_imm("1234");
/* check the username and password */
t = authorise_username(user, pass, client_ip);
if (t == NULL) {
*status = HTTP_FORBIDDEN;
ret = octstr_create("Authorization failed for sendota");
}
/* let's see if we have at least a target msisdn */
else if (to == NULL) {
error(0, "%s needs a valid phone number.", octstr_get_cstr(sendota_url));
*status = HTTP_BAD_REQUEST;
ret = octstr_create("Wrong sendota args.");
} else {
if (urltrans_faked_sender(t) != NULL) {
from = octstr_duplicate(urltrans_faked_sender(t));
}
else if (from != NULL && octstr_len(from) > 0) {
}
else if (urltrans_default_sender(t) != NULL) {
from = octstr_duplicate(urltrans_default_sender(t));
}
else if (global_sender != NULL) {
from = octstr_duplicate(global_sender);
}
else {
*status = HTTP_BAD_REQUEST;
ret = octstr_create("Sender missing and no global set, rejected");
goto error;
}
/*
* get the content-type of the body document
*/
http_header_get_content_type(headers, &type, &charset);
if (octstr_case_compare(type,
octstr_imm("application/x-wap-prov.browser-settings")) == 0) {
doc_type = octstr_format("%s", "settings");
}
else if (octstr_case_compare(type,
octstr_imm("application/x-wap-prov.browser-bookmarks")) == 0) {
doc_type = octstr_format("%s", "bookmarks");
}
else if (octstr_case_compare(type,
octstr_imm("text/vnd.wap.connectivity-xml")) == 0) {
doc_type = octstr_format("%s", "oma-settings");
}
if (doc_type == NULL) {
error(0, "%s got weird content type %s", octstr_get_cstr(sendota_url),
octstr_get_cstr(type));
*status = HTTP_UNSUPPORTED_MEDIA_TYPE;
ret = octstr_create("Unsupported content-type, rejected");
} else {
/*
* ok, this is want we expect
* now lets compile the whole thing
*/
ota_doc = octstr_duplicate(body);
if ((r = ota_pack_message(&msg, ota_doc, doc_type, from, to, sec, pin)) < 0) {
*status = HTTP_BAD_REQUEST;
msg_destroy(msg);
if (r == -2) {
ret = octstr_create("Erroneous document type, cannot"
" compile\n");
goto error;
}
else if (r == -1) {
ret = octstr_create("Erroneous ota source, cannot compile\n");
goto error;
}
}
/* we still need to check if smsc is forced for this */
if (urltrans_forced_smsc(t)) {
msg->sms.smsc_id = octstr_duplicate(urltrans_forced_smsc(t));
if (smsc)
info(0, "send-sms request smsc id ignored, as smsc id forced to %s",
octstr_get_cstr(urltrans_forced_smsc(t)));
} else if (smsc) {
msg->sms.smsc_id = octstr_duplicate(smsc);
} else if (urltrans_default_smsc(t)) {
msg->sms.smsc_id = octstr_duplicate(urltrans_default_smsc(t));
} else
msg->sms.smsc_id = NULL;
info(0, "%s <%s> <%s>", octstr_get_cstr(sendota_url),
id ? octstr_get_cstr(id) : "XML", octstr_get_cstr(to));
if (!immediate_sendsms_reply) {
stored_uuid = store_uuid(msg);
dict_put(client_dict, stored_uuid, client);
}
r = send_message(t, msg);
if (r == -1) {
error(0, "sendota_request: failed");
*status = HTTP_INTERNAL_SERVER_ERROR;
ret = octstr_create("Sending failed.");
if (!immediate_sendsms_reply)
dict_remove(client_dict, stored_uuid);
} else {
*status = HTTP_ACCEPTED;
ret = octstr_create("Sent.");
}
msg_destroy(msg);
octstr_destroy(stored_uuid);
}
}
error:
octstr_destroy(user);
octstr_destroy(pass);
octstr_destroy(smsc);
return ret;
}
static void sendsms_thread(void *arg)
{
HTTPClient *client;
Octstr *ip, *url, *body, *answer;
List *hdrs, *args;
int status;
for (;;) {
client = http_accept_request(sendsms_port, &ip, &url, &hdrs, &body,
&args);
if (client == NULL)
break;
info(0, "smsbox: Got HTTP request <%s> from <%s>",
octstr_get_cstr(url), octstr_get_cstr(ip));
/*
* determine which kind of HTTP request this is any
* call the necessary routine for it
*/
/* sendsms */
if (octstr_compare(url, sendsms_url) == 0)
{
/*
* decide if this is a GET or POST request and let the
* related routine handle the checking
*/
if (body == NULL)
answer = smsbox_req_sendsms(args, ip, &status, client);
else
answer = smsbox_sendsms_post(hdrs, body, ip, &status, client);
}
/* XML-RPC */
else if (octstr_compare(url, xmlrpc_url) == 0)
{
/*
* XML-RPC request needs to have a POST body
*/
if (body == NULL) {
answer = octstr_create("Incomplete request.");
status = HTTP_BAD_REQUEST;
} else
answer = smsbox_xmlrpc_post(hdrs, body, ip, &status);
}
/* sendota */
else if (octstr_compare(url, sendota_url) == 0)
{
if (body == NULL)
answer = smsbox_req_sendota(args, ip, &status, client);
else
answer = smsbox_sendota_post(hdrs, body, ip, &status, client);
}
/* add aditional URI compares here */
else {
answer = octstr_create("Unknown request.");
status = HTTP_NOT_FOUND;
}
debug("sms.http", 0, "Status: %d Answer: <%s>", status,
octstr_get_cstr(answer));
octstr_destroy(ip);
octstr_destroy(url);
http_destroy_headers(hdrs);
octstr_destroy(body);
http_destroy_cgiargs(args);
if (immediate_sendsms_reply || status != HTTP_ACCEPTED)
http_send_reply(client, status, sendsms_reply_hdrs, answer);
else {
debug("sms.http", 0, "Delayed reply - wait for bearerbox");
}
octstr_destroy(answer);
}
}
/***********************************************************************
* Main program. Configuration, signal handling, etc.
*/
static void signal_handler(int signum) {
/* On some implementations (i.e. linuxthreads), signals are delivered
* to all threads. We only want to handle each signal once for the
* entire box, and we let the gwthread wrapper take care of choosing
* one.
*/
if (!gwthread_shouldhandlesignal(signum))
return;
switch (signum) {
case SIGINT:
if (program_status != shutting_down) {
error(0, "SIGINT received, aborting program...");
program_status = shutting_down;
}
break;
case SIGHUP:
warning(0, "SIGHUP received, catching and re-opening logs");
log_reopen();
alog_reopen();
break;
/*
* It would be more proper to use SIGUSR1 for this, but on some
* platforms that's reserved by the pthread support.
*/
case SIGQUIT:
warning(0, "SIGQUIT received, reporting memory usage.");
gw_check_leaks();
break;
}
}
static void setup_signal_handlers(void) {
struct sigaction act;
act.sa_handler = signal_handler;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
sigaction(SIGINT, &act, NULL);
sigaction(SIGQUIT, &act, NULL);
sigaction(SIGHUP, &act, NULL);
sigaction(SIGPIPE, &act, NULL);
}
static Cfg *init_smsbox(Cfg *cfg)
{
CfgGroup *grp;
Octstr *logfile;
Octstr *p;
long lvl;
Octstr *http_proxy_host = NULL;
long http_proxy_port = -1;
List *http_proxy_exceptions = NULL;
Octstr *http_proxy_username = NULL;
Octstr *http_proxy_password = NULL;
Octstr *http_proxy_exceptions_regex = NULL;
int ssl = 0;
int lf, m;
long max_req;
bb_port = BB_DEFAULT_SMSBOX_PORT;
bb_ssl = 0;
bb_host = octstr_create(BB_DEFAULT_HOST);
logfile = NULL;
lvl = 0;
lf = m = 1;
/*
* first we take the port number in bearerbox and other values from the
* core group in configuration file
*/
grp = cfg_get_single_group(cfg, octstr_imm("core"));
cfg_get_integer(&bb_port, grp, octstr_imm("smsbox-port"));
#ifdef HAVE_LIBSSL
cfg_get_bool(&bb_ssl, grp, octstr_imm("smsbox-port-ssl"));
#endif /* HAVE_LIBSSL */
cfg_get_integer(&http_proxy_port, grp, octstr_imm("http-proxy-port"));
http_proxy_host = cfg_get(grp,
octstr_imm("http-proxy-host"));
http_proxy_username = cfg_get(grp,
octstr_imm("http-proxy-username"));
http_proxy_password = cfg_get(grp,
octstr_imm("http-proxy-password"));
http_proxy_exceptions = cfg_get_list(grp,
octstr_imm("http-proxy-exceptions"));
http_proxy_exceptions_regex = cfg_get(grp,
octstr_imm("http-proxy-exceptions-regex"));
#ifdef HAVE_LIBSSL
conn_config_ssl(grp);
#endif
/*
* get the remaining values from the smsbox group
*/
grp = cfg_get_single_group(cfg, octstr_imm("smsbox"));
if (grp == NULL)
panic(0, "No 'smsbox' group in configuration");
smsbox_id = cfg_get(grp, octstr_imm("smsbox-id"));
p = cfg_get(grp, octstr_imm("bearerbox-host"));
if (p != NULL) {
octstr_destroy(bb_host);
bb_host = p;
}
cfg_get_integer(&bb_port, grp, octstr_imm("bearerbox-port"));
#ifdef HAVE_LIBSSL
if (cfg_get_bool(&ssl, grp, octstr_imm("bearerbox-port-ssl")) != -1)
bb_ssl = ssl;
#endif /* HAVE_LIBSSL */
cfg_get_bool(&mo_recode, grp, octstr_imm("mo-recode"));
if(mo_recode < 0)
mo_recode = 0;
reply_couldnotfetch= cfg_get(grp, octstr_imm("reply-couldnotfetch"));
if (reply_couldnotfetch == NULL)
reply_couldnotfetch = octstr_create("Could not fetch content, sorry.");
reply_couldnotrepresent= cfg_get(grp, octstr_imm("reply-couldnotfetch"));
if (reply_couldnotrepresent == NULL)
reply_couldnotrepresent = octstr_create("Result could not be represented "
"as an SMS message.");
reply_requestfailed= cfg_get(grp, octstr_imm("reply-requestfailed"));
if (reply_requestfailed == NULL)
reply_requestfailed = octstr_create("Request Failed");
reply_emptymessage= cfg_get(grp, octstr_imm("reply-emptymessage"));
if (reply_emptymessage == NULL)
reply_emptymessage = octstr_create("");
{
Octstr *os;
os = cfg_get(grp, octstr_imm("white-list"));
if (os != NULL) {
white_list = numhash_create(octstr_get_cstr(os));
octstr_destroy(os);
}
os = cfg_get(grp, octstr_imm("white-list-regex"));
if (os != NULL) {
if ((white_list_regex = gw_regex_comp(os, REG_EXTENDED)) == NULL)
panic(0, "Could not compile pattern '%s'", octstr_get_cstr(os));
octstr_destroy(os);
}
os = cfg_get(grp, octstr_imm("black-list"));
if (os != NULL) {
black_list = numhash_create(octstr_get_cstr(os));
octstr_destroy(os);
}
os = cfg_get(grp, octstr_imm("black-list-regex"));
if (os != NULL) {
if ((black_list_regex = gw_regex_comp(os, REG_EXTENDED)) == NULL)
panic(0, "Could not compile pattern '%s'", octstr_get_cstr(os));
octstr_destroy(os);
}
}
cfg_get_integer(&sendsms_port, grp, octstr_imm("sendsms-port"));
/* check if want to bind to a specific interface */
sendsms_interface = cfg_get(grp, octstr_imm("sendsms-interface"));
cfg_get_integer(&sms_max_length, grp, octstr_imm("sms-length"));
#ifdef HAVE_LIBSSL
cfg_get_bool(&ssl, grp, octstr_imm("sendsms-port-ssl"));
#endif /* HAVE_LIBSSL */
/*
* load the configuration settings for the sendsms and sendota URIs
* else assume the default URIs, st.
*/
if ((sendsms_url = cfg_get(grp, octstr_imm("sendsms-url"))) == NULL)
sendsms_url = octstr_imm("/cgi-bin/sendsms");
if ((xmlrpc_url = cfg_get(grp, octstr_imm("xmlrpc-url"))) == NULL)
xmlrpc_url = octstr_imm("/cgi-bin/xmlrpc");
if ((sendota_url = cfg_get(grp, octstr_imm("sendota-url"))) == NULL)
sendota_url = octstr_imm("/cgi-bin/sendota");
global_sender = cfg_get(grp, octstr_imm("global-sender"));
accepted_chars = cfg_get(grp, octstr_imm("sendsms-chars"));
sendsms_number_chars = accepted_chars ?
octstr_get_cstr(accepted_chars) : SENDSMS_DEFAULT_CHARS;
logfile = cfg_get(grp, octstr_imm("log-file"));
cfg_get_integer(&lvl, grp, octstr_imm("log-level"));
if (logfile != NULL) {
info(0, "Starting to log to file %s level %ld",
octstr_get_cstr(logfile), lvl);
log_open(octstr_get_cstr(logfile), lvl, GW_NON_EXCL);
octstr_destroy(logfile);
}
if (global_sender != NULL) {
info(0, "Service global sender set as '%s'",
octstr_get_cstr(global_sender));
}
/* should smsbox reply to sendsms immediate or wait for bearerbox ack */
cfg_get_bool(&immediate_sendsms_reply, grp, octstr_imm("immediate-sendsms-reply"));
/* determine which timezone we use for access logging */
if ((p = cfg_get(grp, octstr_imm("access-log-time"))) != NULL) {
lf = (octstr_case_compare(p, octstr_imm("gmt")) == 0) ? 0 : 1;
octstr_destroy(p);
}
/* should predefined markers be used, ie. prefixing timestamp */
cfg_get_bool(&m, grp, octstr_imm("access-log-clean"));
/* open access-log file */
if ((p = cfg_get(grp, octstr_imm("access-log"))) != NULL) {
info(0, "Logging accesses to '%s'.", octstr_get_cstr(p));
alog_open(octstr_get_cstr(p), lf, m ? 0 : 1);
octstr_destroy(p);
}
/* HTTP queueing values */
cfg_get_integer(&max_http_retries, grp, octstr_imm("http-request-retry"));
cfg_get_integer(&http_queue_delay, grp, octstr_imm("http-queue-delay"));
if (sendsms_port > 0) {
if (http_open_port_if(sendsms_port, ssl, sendsms_interface) == -1) {
if (only_try_http)
error(0, "Failed to open HTTP socket, ignoring it");
else
panic(0, "Failed to open HTTP socket");
} else {
info(0, "Set up send sms service at port %ld", sendsms_port);
gwthread_create(sendsms_thread, NULL);
}
}
/* set maximum allowed MO/DLR requests in parallel */
if (cfg_get_integer(&max_req, grp, octstr_imm("max-pending-requests")) == -1)
max_req = HTTP_MAX_PENDING;
max_pending_requests = semaphore_create(max_req);
/*
* Reading the name we are using for ppg services from ppg core group
*/
if ((grp = cfg_get_single_group(cfg, octstr_imm("ppg"))) != NULL) {
if ((ppg_service_name = cfg_get(grp, octstr_imm("service-name"))) == NULL)
ppg_service_name = octstr_create("ppg");
}
if (http_proxy_host != NULL && http_proxy_port > 0) {
http_use_proxy(http_proxy_host, http_proxy_port,
http_proxy_exceptions, http_proxy_username,
http_proxy_password, http_proxy_exceptions_regex);
}
octstr_destroy(http_proxy_host);
octstr_destroy(http_proxy_username);
octstr_destroy(http_proxy_password);
octstr_destroy(http_proxy_exceptions_regex);
gwlist_destroy(http_proxy_exceptions, octstr_destroy_item);
return cfg;
}
static int check_args(int i, int argc, char **argv) {
if (strcmp(argv[i], "-H")==0 || strcmp(argv[i], "--tryhttp")==0) {
only_try_http = 1;
} else
return -1;
return 0;
}
int main(int argc, char **argv)
{
int cf_index;
Octstr *filename;
double heartbeat_freq = DEFAULT_HEARTBEAT;
gwlib_init();
cf_index = get_and_set_debugs(argc, argv, check_args);
setup_signal_handlers();
if (argv[cf_index] == NULL)
filename = octstr_create("kannel.conf");
else
filename = octstr_create(argv[cf_index]);
cfg = cfg_create(filename);
if (cfg_read(cfg) == -1)
panic(0, "Couldn't read configuration from `%s'.", octstr_get_cstr(filename));
octstr_destroy(filename);
report_versions("smsbox");
init_smsbox(cfg);
if (max_http_retries > 0) {
info(0, "Using HTTP request queueing with %ld retries, %lds delay.",
max_http_retries, http_queue_delay);
}
debug("sms", 0, "----------------------------------------------");
debug("sms", 0, GW_NAME " smsbox version %s starting", GW_VERSION);
translations = urltrans_create();
if (translations == NULL)
panic(0, "urltrans_create failed");
if (urltrans_add_cfg(translations, cfg) == -1)
panic(0, "urltrans_add_cfg failed");
client_dict = dict_create(32, NULL);
sendsms_reply_hdrs = http_create_empty_headers();
http_header_add(sendsms_reply_hdrs, "Content-type", "text/html");
http_header_add(sendsms_reply_hdrs, "Pragma", "no-cache");
http_header_add(sendsms_reply_hdrs, "Cache-Control", "no-cache");
caller = http_caller_create();
smsbox_requests = gwlist_create();
smsbox_http_requests = gwlist_create();
gwlist_add_producer(smsbox_requests);
gwlist_add_producer(smsbox_http_requests);
num_outstanding_requests = counter_create();
catenated_sms_counter = counter_create();
gwthread_create(obey_request_thread, NULL);
gwthread_create(url_result_thread, NULL);
gwthread_create(http_queue_thread, NULL);
connect_to_bearerbox(bb_host, bb_port, bb_ssl, NULL /* bb_our_host */);
/* XXX add our_host if required */
if (0 > heartbeat_start(write_to_bearerbox, heartbeat_freq,
outstanding_requests)) {
info(0, GW_NAME "Could not start heartbeat.");
}
identify_to_bearerbox();
read_messages_from_bearerbox();
info(0, GW_NAME " smsbox terminating.");
heartbeat_stop(ALL_HEARTBEATS);
http_close_all_ports();
gwthread_join_every(sendsms_thread);
gwlist_remove_producer(smsbox_requests);
gwlist_remove_producer(smsbox_http_requests);
gwthread_join_every(obey_request_thread);
http_caller_signal_shutdown(caller);
gwthread_join_every(url_result_thread);
gwthread_join_every(http_queue_thread);
close_connection_to_bearerbox();
alog_close();
urltrans_destroy(translations);
gw_assert(gwlist_len(smsbox_requests) == 0);
gw_assert(gwlist_len(smsbox_http_requests) == 0);
gwlist_destroy(smsbox_requests, NULL);
gwlist_destroy(smsbox_http_requests, NULL);
http_caller_destroy(caller);
counter_destroy(num_outstanding_requests);
counter_destroy(catenated_sms_counter);
octstr_destroy(bb_host);
octstr_destroy(global_sender);
octstr_destroy(accepted_chars);
octstr_destroy(smsbox_id);
octstr_destroy(sendsms_url);
octstr_destroy(sendota_url);
octstr_destroy(xmlrpc_url);
octstr_destroy(reply_emptymessage);
octstr_destroy(reply_requestfailed);
octstr_destroy(reply_couldnotfetch);
octstr_destroy(reply_couldnotrepresent);
octstr_destroy(sendsms_interface);
octstr_destroy(ppg_service_name);
numhash_destroy(black_list);
numhash_destroy(white_list);
if (white_list_regex != NULL) gw_regex_destroy(white_list_regex);
if (black_list_regex != NULL) gw_regex_destroy(black_list_regex);
semaphore_destroy(max_pending_requests);
cfg_destroy(cfg);
dict_destroy(client_dict);
http_destroy_headers(sendsms_reply_hdrs);
/*
* Just sleep for a while to get bearerbox chance to restart.
* Otherwise we will fail while trying to connect to bearerbox!
*/
if (restart) {
gwthread_sleep(5.0);
}
gwlib_shutdown();
/* now really restart */
if (restart)
execvp(argv[0], argv);
return 0;
}
int charset_processing (Octstr *charset, Octstr *body, int coding) {
int resultcode = 0;
if (octstr_len(charset)) {
/* debug("sms.http", 0, "enter charset, coding=%d, msgdata is %s", coding, octstr_get_cstr(body));
octstr_dump(body, 0); */
if (coding == DC_7BIT) {
/*
* For 7 bit, convert to WINDOWS-1252
*/
if (charset_convert (body, octstr_get_cstr(charset), "WINDOWS-1252") < 0) {
resultcode = -1;
}
} else if (coding == DC_UCS2) {
/*
* For UCS-2, convert to UTF-16BE
*/
if (octstr_recode (octstr_imm ("UTF-16BE"), charset, body) < 0) {
resultcode = -1;
}
}
/* debug("sms.http", 0, "exit charset, coding=%d, msgdata is %s", coding, octstr_get_cstr(body));
octstr_dump(body, 0); */
}
return resultcode;
}