/* ==================================================================== * 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("")); 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 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\n", \ (text?octstr_get_cstr(text):"")); #define OCTSTR_APPEND_XML_NUMBER(xml, tag, value) \ octstr_format_append(xml, " \t\t<" tag ">%ld\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 &#xxxx; 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 &#xxxx; 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; }