Index: doc/userguide/userguide.xml =================================================================== RCS file: /home/cvs/gateway/doc/userguide/userguide.xml,v retrieving revision 1.350 diff -u -b -w -r1.350 userguide.xml --- doc/userguide/userguide.xml 25 May 2009 15:19:35 -0000 1.350 +++ doc/userguide/userguide.xml 9 Jun 2009 14:01:24 -0000 @@ -4080,38 +4080,209 @@ Password for username, if needed. + + + +The "generic" system-type + + For a generic HTTP-based relay, the system-type 'generic' can be used. + It can use the escape codes known from sms-service's get-url config + directives in the send-url string to indicate the HTTP API calling style of + the remote HTTP server, and the 3 HTTP body response parsing regular + expressions 'status-success-regex' for successfull acknowledge, + 'status-permfail-regex' for permanent failure and 'status-tempfail-regex' + for temporary failure. It can also accomodate the incoming parameters and response + codes and text. This system-type has a lot of extra parameters that gives it a lot + of flexibility: + + + + + + Variable + Value + Description + status-success-regex (o) POSIX regular expression - Regular expression for system-type 'generic' to match against HTTP + Regular expression to match against HTTP response body conent, indicating a successfull submission. status-permfail-regex (o) POSIX regular expression - Regular expression for system-type 'generic' to match against HTTP + Regular expression to match against HTTP response body conent, indicating a permanent failure. status-tempfail-regex (o) POSIX regular expression - Regular expression for system-type 'generic' to match against HTTP + Regular expression to match against HTTP response body conent, indicating a temporary failure. + generic-param-username (o) + string + + Overrides the default parameter for the 'username' + field used on incoming requests. + + + generic-param-password (o) + string + + Overrides the default parameter for the 'password' + field used on incoming requests. + + + generic-param-from (o) + string + + Overrides the default parameter for the 'from' field + used on incoming requests. + + + generic-param-to (o) + string + + Overrides the default parameter for the 'to' field + used on incoming requests. + + + generic-param-text (o) + string + + Overrides the default parameter for the 'text' field + used on incoming requests. + + + generic-param-udh (o) + string + + Overrides the default parameter for the 'udh' field + used on incoming requests. + + + generic-param-service (o) + string + + Overrides the default parameter for the 'service' field + used on incoming requests. + + + generic-param-account (o) + string + + Overrides the default parameter for the 'account' field + used on incoming requests. + + + generic-param-binfo (o) + string + + Overrides the default parameter for the 'binfo' field + used on incoming requests. + + + generic-param-dlr-mask (o) + string + + Overrides the default parameter for the 'dlr-mask' field + used on incoming requests. + + + generic-param-dlr-url (o) + string + + Overrides the default parameter for the 'dlr-url' field + used on incoming requests. + + + generic-param-dlr-mid (o) + string + + Overrides the default parameter for the 'dlr-mid' field + used on incoming requests. + + + generic-param-flash (o) + string + + Overrides the default parameter for the 'flash' field + used on incoming requests. + + + generic-param-mclass (o) + string + + Overrides the default parameter for the 'mclass' field + used on incoming requests. + + + generic-param-mwi (o) + string + + Overrides the default parameter for the 'mwi' field + used on incoming requests. + + + generic-param-coding (o) + string + + Overrides the default parameter for the 'coding' field + used on incoming requests. + + + generic-param-validity (o) + string + + Overrides the default parameter for the 'validity' field + used on incoming requests. + + + generic-param-deferred (o) + string + + Overrides the default parameter for the 'deferred' field + used on incoming requests. + + + generic-param-foreign-id (o) + string + + Overrides the default parameter for the 'foreign-id' field + used on incoming requests. + + + generic-message-sent (o) + string + + It allows you to set the text returned when a succesful request is made. + If not set, defaults to 'Sent.'. Note that you can use all sms service escapes + here, see for details. + + + generic-status-sent (o) + string + + Overrides the HTTP status code returned when a successful + request is made. If not set, defaults to 202 (HTTP_ACCEPTED). + + + generic-status-error (o) + string + + Overrides the HTTP status code returned when a request is + rejected for any reason. If not set, defaults to 202 (HTTP_ACCEPTED). + + - For a generic HTTP-based relay, the system-type 'generic' can be used. - It can use the escape codes known from sms-service's get-url config - directives in the send-url string to indicate the HTTP API calling style of - the remote HTTP server, and the 3 HTTP body response parsing regular - expressions 'status-success-regex' for successfull acknowledge, - 'status-permfail-regex' for permanent failure and 'status-tempfail-regex' - for temporary failure. An example for a 'generic' group looks like this: - + An example for a 'generic' group looks like this: group = smsc @@ -4122,14 +4293,13 @@ status-success-regex = "ok" status-permfail-regex = "failure" status-tempfail-regex = "retry later" +generic-param-from = "phoneNumber" +generic-param-to = "shortCode" +generic-message-sent = "Message sent with ID: %I" +generic-status-sent = 200 +generic-status-error = 404 - - Beware that this generic HTTP system-type is for MT only. We don't - provide a way for the MO side too. For MO traffic we simply expect the - remote HTTP caller to use Kannel's sendsms HTTP API on the given port to - inject MO messages. - - + @@ -6120,8 +6290,8 @@ - - Parameters (Escape Codes) +
+ Parameters (Escape Codes) Index: gw/smsc/smsc_http.c =================================================================== RCS file: /home/cvs/gateway/gw/smsc/smsc_http.c,v retrieving revision 1.61 diff -u -b -w -r1.61 smsc_http.c --- gw/smsc/smsc_http.c 14 Jan 2009 11:11:46 -0000 1.61 +++ gw/smsc/smsc_http.c 9 Jun 2009 14:01:27 -0000 @@ -129,8 +129,37 @@ #define DEFAULT_CHARSET "UTF-8" +/* + * This maps fields to values for MO parameters + */ +typedef struct fieldmap { + Octstr *username; + Octstr *password; + Octstr *from; + Octstr *to; + Octstr *text; + Octstr *udh; + Octstr *service; + Octstr *account; + Octstr *binfo; + Octstr *dlr_mask; + Octstr *dlr_url; + Octstr *dlr_mid; + Octstr *flash; + Octstr *mclass; + Octstr *mwi; + Octstr *coding; + Octstr *validity; + Octstr *deferred; + Octstr *foreign_id; + Octstr *message_sent; + long status_sent; + long status_error; +} FieldMap; + typedef struct conndata { HTTPCaller *http_ref; + FieldMap *fieldmap; long receive_thread; long send_cb_thread; int shutdown; @@ -165,6 +194,37 @@ } ConnData; +/* + * Destroys the FieldMap structure + */ +static void fieldmap_destroy(FieldMap *fieldmap) +{ + if (fieldmap == NULL) + return; + octstr_destroy(fieldmap->username); + octstr_destroy(fieldmap->password); + octstr_destroy(fieldmap->from); + octstr_destroy(fieldmap->to); + octstr_destroy(fieldmap->text); + octstr_destroy(fieldmap->udh); + octstr_destroy(fieldmap->service); + octstr_destroy(fieldmap->account); + octstr_destroy(fieldmap->binfo); + octstr_destroy(fieldmap->dlr_mask); + octstr_destroy(fieldmap->dlr_url); + octstr_destroy(fieldmap->dlr_mid); + octstr_destroy(fieldmap->flash); + octstr_destroy(fieldmap->mclass); + octstr_destroy(fieldmap->mwi); + octstr_destroy(fieldmap->coding); + octstr_destroy(fieldmap->validity); + octstr_destroy(fieldmap->deferred); + octstr_destroy(fieldmap->foreign_id); + octstr_destroy(fieldmap->message_sent); + gw_free(fieldmap); +} + + static void conndata_destroy(ConnData *conndata) { if (conndata == NULL) @@ -177,6 +237,7 @@ gw_regex_destroy(conndata->permfail_regex); if (conndata->tempfail_regex) gw_regex_destroy(conndata->tempfail_regex); + fieldmap_destroy(conndata->fieldmap); octstr_destroy(conndata->allow_ip); octstr_destroy(conndata->send_url); octstr_destroy(conndata->dlr_url); @@ -1454,7 +1515,7 @@ /*---------------------------------------------------------------- - * (Semi-)generic HTTP interface + * Generic HTTP interface * * This 'generic' type will handle the 'send-url' directive in the * group the same way the 'sms-service' for smsbox does, via @@ -1462,12 +1523,6 @@ * regex value that match against the reponse body. The HTTP reponse * code is not obeyed. * - * It handles mainly MT messages, due to the fact that MO traffic - * can't be abstracted in a universal way. Therefor we use the - * Kannel sendsms interface layout as generic fallback. So if your - * SMSC provider needs to send MO messages, he needs to implement - * the Kannel sendsms HTTP interface variables. - * * Example config group: * * group = smsc @@ -1477,12 +1532,267 @@ * status-success-regex = "ok" * status-permfail-regex = "failure" * status-tempfail-regex = "retry later" + * generic-param-from = "phoneNumber" + * generic-param-to = "shortCode" + * generic-param-text = "message" + * generic-message-sent = "OK" + * generic-status-sent = 200 + * generic-status-error = 400 * * Note that neither 'smsc-username' nor 'smsc-password' is required, * since they are coded into the the 'send-url' value directly. * * Stipe Tolj + * + * MO processing by Alejandro Guerrieri + */ + +/* + * Get the FieldMap struct to map MO parameters */ +static FieldMap *generic_get_field_map(CfgGroup *grp) +{ + FieldMap *fm = NULL; + fm = gw_malloc(sizeof(FieldMap)); + gw_assert(fm != NULL); + fm->username = cfg_get(grp, octstr_imm("generic-param-username")); + if (fm->username == NULL) + fm->username = octstr_create("username"); + fm->password = cfg_get(grp, octstr_imm("generic-param-password")); + if (fm->password == NULL) + fm->password = octstr_create("password"); + fm->from = cfg_get(grp, octstr_imm("generic-param-from")); + if (fm->from == NULL) + fm->from = octstr_create("from"); + fm->to = cfg_get(grp, octstr_imm("generic-param-to")); + if (fm->to == NULL) + fm->to = octstr_create("to"); + fm->text = cfg_get(grp, octstr_imm("generic-param-text")); + if (fm->text == NULL) + fm->text = octstr_create("text"); + fm->udh = cfg_get(grp, octstr_imm("generic-param-udh")); + if (fm->udh == NULL) + fm->udh = octstr_create("udh"); + /* "service" preloads the "username" parameter to mimic former behaviour */ + fm->service = cfg_get(grp, octstr_imm("generic-param-service")); + if (fm->service == NULL) + fm->service = octstr_create("username"); + fm->account = cfg_get(grp, octstr_imm("generic-param-account")); + if (fm->account == NULL) + fm->account = octstr_create("account"); + fm->binfo = cfg_get(grp, octstr_imm("generic-param-binfo")); + if (fm->binfo == NULL) + fm->binfo = octstr_create("binfo"); + fm->dlr_mask = cfg_get(grp, octstr_imm("generic-param-dlr-mask")); + if (fm->dlr_mask == NULL) + fm->dlr_mask = octstr_create("dlr-mask"); + fm->dlr_url = cfg_get(grp, octstr_imm("generic-param-dlr-url")); + if (fm->dlr_url == NULL) + fm->dlr_url = octstr_create("dlr-url"); + fm->dlr_mid = cfg_get(grp, octstr_imm("generic-param-dlr-mid")); + if (fm->dlr_mid == NULL) + fm->dlr_mid = octstr_create("dlr-mid"); + fm->flash = cfg_get(grp, octstr_imm("generic-param-flash")); + if (fm->flash == NULL) + fm->flash = octstr_create("flash"); + fm->mclass = cfg_get(grp, octstr_imm("generic-param-mclass")); + if (fm->mclass == NULL) + fm->mclass = octstr_create("mclass"); + fm->mwi = cfg_get(grp, octstr_imm("generic-param-mwi")); + if (fm->mwi == NULL) + fm->mwi = octstr_create("mwi"); + fm->coding = cfg_get(grp, octstr_imm("generic-param-coding")); + if (fm->coding == NULL) + fm->coding = octstr_create("coding"); + fm->validity = cfg_get(grp, octstr_imm("generic-param-validity")); + if (fm->validity == NULL) + fm->validity = octstr_create("validity"); + fm->deferred = cfg_get(grp, octstr_imm("generic-param-deferred")); + if (fm->deferred == NULL) + fm->deferred = octstr_create("deferred"); + fm->foreign_id = cfg_get(grp, octstr_imm("generic-param-foreign-id")); + if (fm->foreign_id == NULL) + fm->foreign_id = octstr_create("foreign-id"); + fm->message_sent = cfg_get(grp, octstr_imm("generic-message-sent")); + if (fm->message_sent == NULL) + fm->message_sent = octstr_create("Sent"); + /* both success and error uses HTTP_ACCEPTED to mimic former behaviour */ + if (cfg_get_integer(&fm->status_sent, grp, octstr_imm("generic-status-sent")) == -1) { + fm->status_sent = HTTP_ACCEPTED; + } + if (cfg_get_integer(&fm->status_error, grp, octstr_imm("generic-status-error")) == -1) { + fm->status_error = HTTP_ACCEPTED; + } + + return fm; +} + +static void generic_receive_sms(SMSCConn *conn, HTTPClient *client, + List *headers, Octstr *body, List *cgivars) +{ + ConnData *conndata = conn->data; + FieldMap *fm = conndata->fieldmap; + Octstr *user, *pass, *from, *to, *text, *udh, *account, *binfo; + Octstr *dlrurl, *dlrmid; + Octstr *tmp_string, *retmsg; + int mclass, mwi, coding, validity, deferred, dlrmask; + List *reply_headers; + int ret, retstatus; + + mclass = mwi = coding = validity = + deferred = dlrmask = SMS_PARAM_UNDEFINED; + + /* Parse enough parameters to validate the request */ + user = http_cgi_variable(cgivars, octstr_get_cstr(fm->username)); + pass = http_cgi_variable(cgivars, octstr_get_cstr(fm->password)); + from = http_cgi_variable(cgivars, octstr_get_cstr(fm->from)); + to = http_cgi_variable(cgivars, octstr_get_cstr(fm->to)); + text = http_cgi_variable(cgivars, octstr_get_cstr(fm->text)); + udh = http_cgi_variable(cgivars, octstr_get_cstr(fm->udh)); + dlrurl = http_cgi_variable(cgivars, octstr_get_cstr(fm->dlr_url)); + dlrmid = http_cgi_variable(cgivars, octstr_get_cstr(fm->dlr_mid)); + tmp_string = http_cgi_variable(cgivars, octstr_get_cstr(fm->dlr_mask)); + if (tmp_string) { + sscanf(octstr_get_cstr(tmp_string),"%d", &dlrmask); + } + debug("smsc.http.kannel", 0, "HTTP[%s]: Received an HTTP request", + octstr_get_cstr(conn->id)); + + if ((conndata->username != NULL && conndata->password != NULL) && + (user == NULL || pass == NULL || + octstr_compare(user, conndata->username) != 0 || + octstr_compare(pass, conndata->password) != 0)) { + error(0, "HTTP[%s]: Authorization failure", + octstr_get_cstr(conn->id)); + retmsg = octstr_create("Authorization failed for sendsms"); + retstatus = fm->status_error; + } + else if (dlrmask != 0 && dlrmid != NULL) { + /* we got a DLR, and we don't require additional values */ + Msg *dlrmsg; + + dlrmsg = dlr_find(conn->id, + dlrmid, /* message id */ + to, /* destination */ + dlrmask); + + if (dlrmsg != NULL) { + dlrmsg->sms.sms_type = report_mo; + + debug("smsc.http.kannel", 0, "HTTP[%s]: Received DLR for DLR-URL <%s>", + octstr_get_cstr(conn->id), octstr_get_cstr(dlrmsg->sms.dlr_url)); + + Msg *resp = msg_duplicate(dlrmsg); + ret = bb_smscconn_receive(conn, dlrmsg); + if (ret == -1) { + retmsg = octstr_create("Not accepted"); + retstatus = fm->status_error; + } else { + retmsg = urltrans_fill_escape_codes(fm->message_sent, resp); + retstatus = fm->status_sent; + } + msg_destroy(resp); + } else { + error(0,"HTTP[%s]: Got DLR but could not find message or was not interested " + "in it id<%s> dst<%s>, type<%d>", + octstr_get_cstr(conn->id), octstr_get_cstr(dlrmid), + octstr_get_cstr(to), dlrmask); + retmsg = octstr_create("Unknown DLR, not accepted"); + retstatus = fm->status_error; + } + } + else if (from == NULL || to == NULL || text == NULL) { + error(0, "HTTP[%s]: Insufficient args", + octstr_get_cstr(conn->id)); + retmsg = octstr_create("Insufficient args, rejected"); + retstatus = fm->status_error; + } + else if (udh != NULL && (octstr_len(udh) != octstr_get_char(udh, 0) + 1)) { + error(0, "HTTP[%s]: UDH field misformed, rejected", + octstr_get_cstr(conn->id)); + retmsg = octstr_create("UDH field misformed, rejected"); + retstatus = fm->status_error; + } + else if (udh != NULL && octstr_len(udh) > MAX_SMS_OCTETS) { + error(0, "HTTP[%s]: UDH field is too long, rejected", + octstr_get_cstr(conn->id)); + retmsg = octstr_create("UDH field is too long, rejected"); + retstatus = fm->status_error; + } + else { + /* we got a normal MO SMS */ + Msg *msg; + msg = msg_create(sms); + + /* Parse the rest of the parameters */ + tmp_string = http_cgi_variable(cgivars, octstr_get_cstr(fm->flash)); + if (tmp_string) { + sscanf(octstr_get_cstr(tmp_string),"%d", &msg->sms.mclass); + } + tmp_string = http_cgi_variable(cgivars, octstr_get_cstr(fm->mclass)); + if (tmp_string) { + sscanf(octstr_get_cstr(tmp_string),"%d", &msg->sms.mclass); + } + tmp_string = http_cgi_variable(cgivars, octstr_get_cstr(fm->mwi)); + if (tmp_string) { + sscanf(octstr_get_cstr(tmp_string),"%d", &msg->sms.mwi); + } + tmp_string = http_cgi_variable(cgivars, octstr_get_cstr(fm->coding)); + if (tmp_string) { + sscanf(octstr_get_cstr(tmp_string),"%d", &msg->sms.coding); + } + tmp_string = http_cgi_variable(cgivars, octstr_get_cstr(fm->validity)); + if (tmp_string) { + sscanf(octstr_get_cstr(tmp_string),"%d", &msg->sms.validity); + } + tmp_string = http_cgi_variable(cgivars, octstr_get_cstr(fm->deferred)); + if (tmp_string) { + sscanf(octstr_get_cstr(tmp_string),"%d", &msg->sms.deferred); + } + account = http_cgi_variable(cgivars, octstr_get_cstr(fm->account)); + binfo = http_cgi_variable(cgivars, octstr_get_cstr(fm->binfo)); + + debug("smsc.http.kannel", 0, "HTTP[%s]: Constructing new SMS", + octstr_get_cstr(conn->id)); + + /* convert character encoding if required */ + if (conndata->alt_charset && + charset_convert(text, octstr_get_cstr(conndata->alt_charset), + DEFAULT_CHARSET) != 0) + error(0, "Failed to convert msgdata from charset <%s> to <%s>, will leave it as it is.", + octstr_get_cstr(conndata->alt_charset), DEFAULT_CHARSET); + + msg->sms.service = octstr_duplicate(user); + msg->sms.sender = octstr_duplicate(from); + msg->sms.receiver = octstr_duplicate(to); + msg->sms.msgdata = octstr_duplicate(text); + msg->sms.udhdata = octstr_duplicate(udh); + msg->sms.smsc_id = octstr_duplicate(conn->id); + msg->sms.time = time(NULL); + msg->sms.account = octstr_duplicate(account); + msg->sms.binfo = octstr_duplicate(binfo); + Msg *resp = msg_duplicate(msg); + ret = bb_smscconn_receive(conn, msg); + if (ret == -1) { + retmsg = octstr_create("Not accepted"); + retstatus = fm->status_error; + } else { + retmsg = urltrans_fill_escape_codes(fm->message_sent, resp); + retstatus = fm->status_sent; + } + msg_destroy(resp); + } + + reply_headers = gwlist_create(); + http_header_add(reply_headers, "Content-Type", "text/plain"); + debug("smsc.http.kannel", 0, "HTTP[%s]: Sending reply", + octstr_get_cstr(conn->id)); + http_send_reply(client, retstatus, reply_headers, retmsg); + + octstr_destroy(retmsg); + http_destroy_headers(reply_headers); +} + static void generic_send_sms(SMSCConn *conn, Msg *sms) { @@ -1635,6 +1945,7 @@ cfg_get_bool(&conndata->no_sep, cfg, octstr_imm("no-sep")); conndata->proxy = cfg_get(cfg, octstr_imm("system-id")); conndata->alt_charset = cfg_get(cfg, octstr_imm("alt-charset")); + conndata->fieldmap = NULL; if (conndata->send_url == NULL) panic(0, "HTTP[%s]: Sending not allowed. No 'send-url' specified.", @@ -1693,7 +2004,8 @@ octstr_get_cstr(conn->id)); goto error; } - conndata->receive_sms = kannel_receive_sms; /* emulate sendsms interface */ + conndata->fieldmap = generic_get_field_map(cfg); + conndata->receive_sms = generic_receive_sms; /* emulate sendsms interface */ conndata->send_sms = generic_send_sms; conndata->parse_reply = generic_parse_reply; Index: gwlib/cfg.def =================================================================== RCS file: /home/cvs/gateway/gwlib/cfg.def,v retrieving revision 1.139 diff -u -b -w -r1.139 cfg.def --- gwlib/cfg.def 6 May 2009 18:47:34 -0000 1.139 +++ gwlib/cfg.def 9 Jun 2009 14:01:27 -0000 @@ -386,6 +386,28 @@ OCTSTR(max-sms-octets) OCTSTR(login-prompt) OCTSTR(password-prompt) + OCTSTR(generic-param-username) + OCTSTR(generic-param-password) + OCTSTR(generic-param-from) + OCTSTR(generic-param-to) + OCTSTR(generic-param-text) + OCTSTR(generic-param-udh) + OCTSTR(generic-param-service) + OCTSTR(generic-param-account) + OCTSTR(generic-param-binfo) + OCTSTR(generic-param-dlr-mask) + OCTSTR(generic-param-dlr-url) + OCTSTR(generic-param-dlr-mid) + OCTSTR(generic-param-flash) + OCTSTR(generic-param-mclass) + OCTSTR(generic-param-mwi) + OCTSTR(generic-param-coding) + OCTSTR(generic-param-validity) + OCTSTR(generic-param-deferred) + OCTSTR(generic-param-foreign-id) + OCTSTR(generic-message-sent) + OCTSTR(generic-status-sent) + OCTSTR(generic-status-error) )