RE: Clickatell http driver patch
"Rene Kluwen" <[email protected]>
| Newsgroups | gmane.comp.mobile.kannel.devel |
|---|---|
| Message-ID | <[email protected]> |
Alex, I fixed your points 1, 2 & 4 with the latest patch attached. About point 3: https server support will be nice in the http smsc driver. This is true for ALL http drivers and is not just Clickatell related. So I consider this a seperate issue. Rene Kluwen Chimit -----Original Message----- From: [email protected] [mailto:[email protected]]On Behalf Of Alexander Malysh Sent: dinsdag 14 maart 2006 21:14 To: [email protected] Subject: Re: Clickatell http driver patch Hi again, Rene Kluwen schrieb: > Hi Alex, > > As you suggested, I agree that we better move this thread to the ML. > I posted the patch to the users ML. I will attach it to this post again for > your convenience. > > I will fix your points 1, 2 & 4. Probably before the end of this week. > > About the authentication: Clickatell does not send a username or password > along. > But I will see if it is possible to make a check on api_id. > > Besides that, also connect-allow-ip can be used. From what I have heard from > the Clickatell techs, their callback HTTP farm connects from behind one > single IP address. not really good interface ;) (hint: ip spoofing) Their server should connect via https with username & pass... Thanks, Alex > > Cheers, > > Rene Kluwen > Chimit > > > ----------------------------------------------------------------------- > alex - 03-14-06 19:31 GMT > ----------------------------------------------------------------------- > Hi Rene, > I agree with you that we should support as much http interfaces as > possible. As to you patch... It's to hard to comment on this patch in > mantis. Please post it to ML and I will review it. At least now some > comments: > 1) please fix comments (they mention Brunet but your patch should support > Clickatell ;)) > 2) please kill commented blocks which are there due to copy&paste from > brunet /* e.g. XSER */ > 3) I don't see what should prevent me to send your MO message instead of > Clickatell server (not auth. for MO and DLR) > 4) minor: please fix coding style (indents) > > Thanks, > Alex > >
clickatell MO Revisited.patch
(application/octet-stream, 10.4 KB)
Index: gw/smsc/smsc_http.c
===================================================================
RCS file: /home/cvs/gateway/gw/smsc/smsc_http.c,v
retrieving revision 1.44
diff -u -r1.44 smsc_http.c
--- gw/smsc/smsc_http.c 19 Sep 2005 22:07:33 -0000 1.44
+++ gw/smsc/smsc_http.c 15 Mar 2006 13:22:57 -0000
@@ -137,6 +137,7 @@
long open_sends;
Octstr *username; /* if needed */
Octstr *password; /* as said */
+ Octstr *system_id; /* api id for clickatell */
int no_sender; /* ditto */
int no_coding; /* this, too */
int no_sep; /* not to mention this */
@@ -162,6 +163,7 @@
octstr_destroy(conndata->username);
octstr_destroy(conndata->password);
octstr_destroy(conndata->proxy);
+ octstr_destroy(conndata->system_id);
gw_free(conndata);
}
@@ -510,6 +512,249 @@
/*----------------------------------------------------------------
+ * Clickatell - http://api.clickatell.com/
+ *
+ * Rene Kluwen <[email protected]>
+ */
+
+/* MT related function */
+static void clickatell_send_sms(SMSCConn *conn, Msg *sms)
+{
+ ConnData *conndata = conn->data;
+ Octstr *url;
+ List *headers;
+
+ /* form the basic URL */
+ url = octstr_format("%S/sendmsg?to=%E&from=%E&api_id=%E&user=%E&password=%E",
+ conndata->send_url, sms->sms.receiver, sms->sms.sender, conndata->system_id, conndata->username, conndata->password);
+
+ /*
+ * We use &binfo=<foobar> from sendsms interface to encode
+ * additional paramters. If a mandatory value is not set,
+ * a default value is applied
+ */
+ if (octstr_len(sms->sms.binfo)) {
+ octstr_url_decode(sms->sms.binfo);
+ octstr_format_append(url, "&%S", sms->sms.binfo);
+ }
+
+ /* if coding is not set and UDH exists, assume DC_8BIT
+ * else default to DC_7BIT */
+ /*
+ if (sms->sms.coding == DC_UNDEF)
+ sms->sms.coding = octstr_len(sms->sms.udhdata) > 0 ? DC_8BIT : DC_7BIT;
+ */
+
+ /* add UDH header */
+ if (octstr_len(sms->sms.udhdata)) {
+ octstr_format_append(url, "&data=%H", sms->sms.msgdata);
+ octstr_format_append(url, "&udh=%H", sms->sms.udhdata);
+ }
+ else {
+ octstr_format_append(url, "&text=%E", sms->sms.msgdata);
+ }
+ if (DLR_IS_ENABLED_DEVICE(sms->sms.dlr_mask)) {
+ octstr_format_append(url, "&callback=3&deliv_ack=1");
+ }
+
+ headers = http_create_empty_headers();
+ debug("smsc.http.clickatell", 0, "HTTP[%s]: Sending request <%s>",
+ octstr_get_cstr(conn->id), octstr_get_cstr(url));
+
+ /*
+ * Clickatell requires optionally an SSL-enabled HTTP client call, this is handled
+ * transparently by the Kannel HTTP layer module.
+ */
+ http_start_request(conndata->http_ref, HTTP_METHOD_GET, url, headers, NULL, 0, sms, NULL);
+
+ octstr_destroy(url);
+ http_destroy_headers(headers);
+}
+
+
+/*
+ * Parse a line in the format: ID: XXXXXXXXXXXXXXXXXX
+ * and return a Dict with the 'ID' as key and the value as value,
+ * otherwise return NULL if a parsing error occures.
+ */
+static Dict *clickatell_parse_body(Octstr *body)
+{
+ Dict *param = NULL;
+ List *words = NULL;
+ long len;
+ Octstr *word, *value;
+
+ words = octstr_split_words(body);
+ if ((len = gwlist_len(words)) > 1) {
+ word = gwlist_extract_first(words);
+ if (octstr_compare(word, octstr_imm("ID:")) == 0) {
+ value = gwlist_extract_first(words);
+ param = dict_create(4, NULL);
+ dict_put(param, octstr_imm("ID"), value);
+ }
+ else if (octstr_compare(word, octstr_imm("ERR:")) == 0) {
+ value = gwlist_extract_first(words);
+ param = dict_create(4, NULL);
+ dict_put(param, octstr_imm("ERR"), value);
+ }
+ octstr_destroy(word);
+ }
+ gwlist_destroy(words, (void(*)(void *)) octstr_destroy);
+
+ return param;
+}
+
+
+static void clickatell_parse_reply(SMSCConn *conn, Msg *msg, int status,
+ List *headers, Octstr *body)
+{
+ if (status == HTTP_OK || status == HTTP_ACCEPTED) {
+ Dict *param;
+ Octstr *status, *msgid;
+
+ if ((param = clickatell_parse_body(body)) != NULL &&
+ (msgid = dict_get(param, octstr_imm("ID"))) != NULL &&
+ msgid != NULL) {
+
+ /* SMSC ACK.. now we have the message id. */
+ if (DLR_IS_ENABLED_DEVICE(msg->sms.dlr_mask))
+ dlr_add(conn->id, msgid, msg);
+
+ bb_smscconn_sent(conn, msg, NULL);
+
+ } else {
+ error(0, "HTTP[%s]: Message was malformed or error was returned. SMSC response `%s'.",
+ octstr_get_cstr(conn->id), octstr_get_cstr(body));
+ bb_smscconn_send_failed(conn, msg,
+ SMSCCONN_FAILED_MALFORMED, octstr_duplicate(body));
+ }
+ dict_destroy(param);
+
+ } else {
+ error(0, "HTTP[%s]: Message was rejected. SMSC reponse `%s'.",
+ octstr_get_cstr(conn->id), octstr_get_cstr(body));
+ bb_smscconn_send_failed(conn, msg,
+ SMSCCONN_FAILED_REJECTED, octstr_duplicate(body));
+ }
+}
+
+/* MO related function */
+static void clickatell_receive_sms(SMSCConn *conn, HTTPClient *client,
+ List *headers, Octstr *body, List *cgivars)
+{
+ ConnData *conndata = conn->data;
+ List *reply_headers;
+ int ret;
+ Octstr *apimsgid, *status, *timestamp, *retmsg, *dest, *charge;
+ Octstr *api_id, *from, *to, *text, *charset, *udh;
+ int httpstatus = HTTP_UNAUTHORIZED, dlrstat;
+ Msg *dlrmsg, *momsg;
+ struct tm tm;
+
+ /* dlr parameters */
+ apimsgid = http_cgi_variable(cgivars, "apiMsgId");
+ status = http_cgi_variable(cgivars, "status");
+ timestamp = http_cgi_variable(cgivars, "timestamp"); /* timestamp is for both DLR & MO */
+ dest = http_cgi_variable(cgivars, "to");
+ charge = http_cgi_variable(cgivars, "charge");
+ /* MO parameters */
+ api_id = http_cgi_variable(cgivars, "api_id");
+ from = http_cgi_variable(cgivars, "from");
+ to = http_cgi_variable(cgivars, "to");
+ text = http_cgi_variable(cgivars, "text");
+ charset = http_cgi_variable(cgivars, "charset");
+ udh = http_cgi_variable(cgivars, "udh");
+
+ debug("smsc.http.clickatell", 0, "HTTP[%s]: Received a request",
+ octstr_get_cstr(conn->id));
+
+ if (api_id != NULL && from != NULL && to != NULL && timestamp != NULL && text != NULL && charset != NULL && udh != NULL) {
+ /* we received an MO message */
+ info(0, "Received MO message from %s: <%s>", octstr_get_cstr(from), octstr_get_cstr(text));
+ momsg = msg_create(sms);
+ momsg->sms.sms_type = mo;
+ momsg->sms.sender = octstr_duplicate(from);
+ momsg->sms.receiver = octstr_duplicate(to);
+ momsg->sms.msgdata = octstr_duplicate(text);
+ momsg->sms.charset = octstr_duplicate(charset);
+ if (octstr_compare(udh, octstr_imm("")) != 0) {
+ momsg->sms.udhdata = octstr_duplicate(udh);
+ }
+ strptime(octstr_get_cstr(timestamp), "%Y-%m-%d %H:%M:%S", &tm);
+ momsg->sms.time = gw_mktime(&tm);
+
+ /* note: implicit msg_destroy */
+ ret = bb_smscconn_receive(conn, momsg);
+ retmsg = octstr_create("Thanks");
+ }
+ else if (apimsgid == NULL || status == NULL || timestamp == NULL || dest == NULL) {
+ error(0, "HTTP[%s]: Insufficient args.",
+ octstr_get_cstr(conn->id));
+ retmsg = octstr_create("Insufficient arguments, rejected.");
+ }
+ else {
+ switch (atoi(octstr_get_cstr(status))) {
+ case 1: /* message unknown */
+ case 5: /* error with message */
+ case 6: /* user cancelled message */
+ case 7: /* error delivering message */
+ case 9: /* routing error */
+ case 10: /* message expired */
+ dlrstat = 2; /* delivery failure */
+ break;
+ case 2: /* message queued */
+ case 3: /* delivered */
+ case 11: /* message queued for later delivery */
+ dlrstat = 4; /* message buffered */
+ break;
+ case 4: /* received by recipient */
+ case 8: /* OK */
+ dlrstat = 1; /* message received */
+ break;
+ default: /* unknown status code */
+ dlrstat = 16; /* smsc reject */
+ break;
+ }
+ dlrmsg = dlr_find(conn->id,
+ apimsgid, /* smsc message id */
+ dest , /* destination */
+ dlrstat);
+
+ if (dlrmsg != NULL) {
+ /* dlrmsg->sms.msgdata = octstr_duplicate(apimsgid); */
+ dlrmsg->sms.sms_type = report_mo;
+ dlrmsg->sms.time = atoi(octstr_get_cstr(timestamp));
+ if (charge) {
+ /* unsure if smsbox relays the binfo field to dlrs.
+ But it is here in case they will start to do it. */
+ dlrmsg->sms.binfo = octstr_duplicate(charge);
+ }
+
+ ret = bb_smscconn_receive(conn, dlrmsg);
+ httpstatus = (ret == 0 ? HTTP_OK : HTTP_FORBIDDEN);
+ retmsg = octstr_create("Sent");
+ } 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(apimsgid),
+ octstr_get_cstr(dest), dlrstat);
+ httpstatus = HTTP_OK;
+ retmsg = octstr_create("Thanks");
+ }
+ }
+
+ reply_headers = gwlist_create();
+ http_header_add(reply_headers, "Content-Type", "text/plain");
+ debug("smsc.http.clickatell", 0, "HTTP[%s]: Sending reply `%s'.",
+ octstr_get_cstr(conn->id), octstr_get_cstr(retmsg));
+ http_send_reply(client, httpstatus, reply_headers, retmsg);
+
+ octstr_destroy(retmsg);
+ http_destroy_headers(reply_headers);
+}
+
+
+/*----------------------------------------------------------------
* Brunet - A german aggregator (mainly doing T-Mobil D1 connections)
*
* o bruHTT v1.3L (for MO traffic)
@@ -1135,6 +1380,7 @@
conndata->send_url = cfg_get(cfg, octstr_imm("send-url"));
conndata->username = cfg_get(cfg, octstr_imm("smsc-username"));
conndata->password = cfg_get(cfg, octstr_imm("smsc-password"));
+ conndata->system_id = cfg_get(cfg, octstr_imm("system-id"));
cfg_get_bool(&conndata->no_sender, cfg, octstr_imm("no-sender"));
cfg_get_bool(&conndata->no_coding, cfg, octstr_imm("no-coding"));
cfg_get_bool(&conndata->no_sep, cfg, octstr_imm("no-sep"));
@@ -1189,6 +1435,11 @@
conndata->send_sms = wapme_smsproxy_send_sms;
conndata->parse_reply = wapme_smsproxy_parse_reply;
}
+ else if (octstr_case_compare(type, octstr_imm("clickatell")) == 0) {
+ conndata->receive_sms = clickatell_receive_sms;
+ conndata->send_sms = clickatell_send_sms;
+ conndata->parse_reply = clickatell_parse_reply;
+ }
/*
* ADD NEW HTTP SMSC TYPES HERE
*/