1 /* ====================================================================
2  * The Kannel Software License, Version 1.0
3  *
4  * Copyright (c) 2001-2014 Kannel Group
5  * Copyright (c) 1998-2001 WapIT Ltd.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  *
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in
17  *    the documentation and/or other materials provided with the
18  *    distribution.
19  *
20  * 3. The end-user documentation included with the redistribution,
21  *    if any, must include the following acknowledgment:
22  *       "This product includes software developed by the
23  *        Kannel Group (http://www.kannel.org/)."
24  *    Alternately, this acknowledgment may appear in the software itself,
25  *    if and wherever such third-party acknowledgments normally appear.
26  *
27  * 4. The names "Kannel" and "Kannel Group" must not be used to
28  *    endorse or promote products derived from this software without
29  *    prior written permission. For written permission, please
30  *    contact org@kannel.org.
31  *
32  * 5. Products derived from this software may not be called "Kannel",
33  *    nor may "Kannel" appear in their name, without prior written
34  *    permission of the Kannel Group.
35  *
36  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
37  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
38  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
39  * DISCLAIMED.  IN NO EVENT SHALL THE KANNEL GROUP OR ITS CONTRIBUTORS
40  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
41  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
42  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
43  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
44  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
45  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
46  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
47  * ====================================================================
48  *
49  * This software consists of voluntary contributions made by many
50  * individuals on behalf of the Kannel Group.  For more information on
51  * the Kannel Group, please see <http://www.kannel.org/>.
52  *
53  * Portions of this software are based upon software originally written at
54  * WapIT Ltd., Helsinki, Finland for the Kannel project.
55  */
56 
57 /*----------------------------------------------------------------
58  * Generic HTTP interface
59  *
60  * This 'generic' type will handle the 'send-url' directive in the
61  * group the same way the 'sms-service' for smsbox does, via
62  * URLTranslation. Response interpretation is based on the three
63  * regex value that match against the reponse body. The HTTP reponse
64  * code is not obeyed.
65  *
66  * Example config group:
67  *
68  *  group = smsc
69  *  smsc = http
70  *  system-type = generic
71  *  send-url = "http://<foobar>/<uri>?from=%P&to=%p&text=%b"
72  *  status-success-regex = "ok"
73  *  status-permfail-regex = "failure"
74  *  status-tempfail-regex = "retry later"
75  *  generic-foreign-id-regex = "<id>(.+)</id>"
76  *  generic-param-from = "phoneNumber"
77  *  generic-param-to = "shortCode"
78  *  generic-param-text = "message"
79  *  generic-message-sent = "OK"
80  *  generic-status-sent = 200
81  *  generic-status-error = 400
82  *
83  * Note that neither 'smsc-username' nor 'smsc-password' is required,
84  * since they are coded into the the 'send-url' value directly.
85  *
86  * Stipe Tolj <st@tolj.org>
87  *
88  * MO processing by Alejandro Guerrieri <aguerrieri at kannel dot org>
89  */
90 
91 #include "gwlib/gwlib.h"
92 
93 /*
94  * This maps fields to values for MO parameters
95  */
96 struct fieldmap {
97     Octstr *username;
98     Octstr *password;
99     Octstr *from;
100     Octstr *to;
101     Octstr *text;
102     Octstr *udh;
103     Octstr *service;
104     Octstr *account;
105     Octstr *binfo;
106     Octstr *meta_data;
107     Octstr *dlr_mask;
108     Octstr *dlr_err;
109     Octstr *dlr_url;
110     Octstr *dlr_mid;
111     Octstr *flash;
112     Octstr *mclass;
113     Octstr *mwi;
114     Octstr *coding;
115     Octstr *validity;
116     Octstr *deferred;
117     Octstr *foreign_id;
118     Octstr *message_sent;
119     long status_sent;
120     long status_error;
121 };
122 
123 struct generic_values {
124     struct fieldmap *map;
125 
126     /* The following are compiled regex for the 'generic' type for handling
127      * success, permanent failure and temporary failure. For types that use
128      * simple HTTP body parsing, these may be used also for other types,
129      * ie. for our own Kannel reply parsing. */
130     regex_t *success_regex;
131     regex_t *permfail_regex;
132     regex_t *tempfail_regex;
133 
134     /* Compiled regex for the 'generic' type to get the foreign message id
135     * from the HTTP response body */
136     regex_t *generic_foreign_id_regex;
137 };
138 
139 /*
140  * Destroys the FieldMap structure
141  */
fieldmap_destroy(struct fieldmap * fieldmap)142 static void fieldmap_destroy(struct fieldmap *fieldmap)
143 {
144     if (fieldmap == NULL)
145         return;
146     octstr_destroy(fieldmap->username);
147     octstr_destroy(fieldmap->password);
148     octstr_destroy(fieldmap->from);
149     octstr_destroy(fieldmap->to);
150     octstr_destroy(fieldmap->text);
151     octstr_destroy(fieldmap->udh);
152     octstr_destroy(fieldmap->service);
153     octstr_destroy(fieldmap->account);
154     octstr_destroy(fieldmap->binfo);
155     octstr_destroy(fieldmap->meta_data);
156     octstr_destroy(fieldmap->dlr_mask);
157     octstr_destroy(fieldmap->dlr_err);
158     octstr_destroy(fieldmap->dlr_url);
159     octstr_destroy(fieldmap->dlr_mid);
160     octstr_destroy(fieldmap->flash);
161     octstr_destroy(fieldmap->mclass);
162     octstr_destroy(fieldmap->mwi);
163     octstr_destroy(fieldmap->coding);
164     octstr_destroy(fieldmap->validity);
165     octstr_destroy(fieldmap->deferred);
166     octstr_destroy(fieldmap->foreign_id);
167     octstr_destroy(fieldmap->message_sent);
168     gw_free(fieldmap);
169 }
170 
171 
172 /*
173  * Get the FieldMap struct to map MO parameters
174  */
generic_get_field_map(CfgGroup * grp)175 static struct fieldmap *generic_get_field_map(CfgGroup *grp)
176 {
177     struct fieldmap *fm = NULL;
178 
179     fm = gw_malloc(sizeof(*fm));
180     gw_assert(fm != NULL);
181 
182     fm->username = cfg_get(grp, octstr_imm("generic-param-username"));
183     if (fm->username == NULL)
184         fm->username = octstr_create("username");
185     fm->password = cfg_get(grp, octstr_imm("generic-param-password"));
186     if (fm->password == NULL)
187         fm->password = octstr_create("password");
188     fm->from = cfg_get(grp, octstr_imm("generic-param-from"));
189     if (fm->from == NULL)
190         fm->from = octstr_create("from");
191     fm->to = cfg_get(grp, octstr_imm("generic-param-to"));
192     if (fm->to == NULL)
193         fm->to = octstr_create("to");
194     fm->text = cfg_get(grp, octstr_imm("generic-param-text"));
195     if (fm->text == NULL)
196         fm->text = octstr_create("text");
197     fm->udh = cfg_get(grp, octstr_imm("generic-param-udh"));
198     if (fm->udh == NULL)
199         fm->udh = octstr_create("udh");
200     /* "service" preloads the "username" parameter to mimic former behaviour */
201     fm->service = cfg_get(grp, octstr_imm("generic-param-service"));
202     if (fm->service == NULL)
203         fm->service = octstr_create("username");
204     fm->account = cfg_get(grp, octstr_imm("generic-param-account"));
205     if (fm->account == NULL)
206         fm->account = octstr_create("account");
207     fm->binfo = cfg_get(grp, octstr_imm("generic-param-binfo"));
208     if (fm->binfo == NULL)
209         fm->binfo = octstr_create("binfo");
210     fm->dlr_mask = cfg_get(grp, octstr_imm("generic-param-dlr-mask"));
211     if (fm->dlr_mask == NULL)
212         fm->dlr_mask = octstr_create("dlr-mask");
213     fm->dlr_err = cfg_get(grp, octstr_imm("generic-param-dlr-err"));
214     if (fm->dlr_err == NULL)
215         fm->dlr_err = octstr_create("dlr-err");
216     fm->dlr_url = cfg_get(grp, octstr_imm("generic-param-dlr-url"));
217     if (fm->dlr_url == NULL)
218         fm->dlr_url = octstr_create("dlr-url");
219     fm->dlr_mid = cfg_get(grp, octstr_imm("generic-param-dlr-mid"));
220     if (fm->dlr_mid == NULL)
221         fm->dlr_mid = octstr_create("dlr-mid");
222     fm->flash = cfg_get(grp, octstr_imm("generic-param-flash"));
223     if (fm->flash == NULL)
224         fm->flash = octstr_create("flash");
225     fm->mclass = cfg_get(grp, octstr_imm("generic-param-mclass"));
226     if (fm->mclass == NULL)
227         fm->mclass = octstr_create("mclass");
228     fm->mwi = cfg_get(grp, octstr_imm("generic-param-mwi"));
229     if (fm->mwi == NULL)
230         fm->mwi = octstr_create("mwi");
231     fm->coding = cfg_get(grp, octstr_imm("generic-param-coding"));
232     if (fm->coding == NULL)
233         fm->coding = octstr_create("coding");
234     fm->validity = cfg_get(grp, octstr_imm("generic-param-validity"));
235     if (fm->validity == NULL)
236         fm->validity = octstr_create("validity");
237     fm->deferred = cfg_get(grp, octstr_imm("generic-param-deferred"));
238     if (fm->deferred == NULL)
239         fm->deferred = octstr_create("deferred");
240     fm->foreign_id = cfg_get(grp, octstr_imm("generic-param-foreign-id"));
241     if (fm->foreign_id == NULL)
242         fm->foreign_id = octstr_create("foreign-id");
243     fm->meta_data = cfg_get(grp, octstr_imm("generic-param-meta-data"));
244     if (fm->meta_data == NULL)
245         fm->meta_data = octstr_create("meta-data");
246     fm->message_sent = cfg_get(grp, octstr_imm("generic-message-sent"));
247     if (fm->message_sent == NULL)
248         fm->message_sent = octstr_create("Sent");
249     /* both success and error uses HTTP_ACCEPTED to mimic former behaviour */
250     if (cfg_get_integer(&fm->status_sent, grp, octstr_imm("generic-status-sent")) == -1) {
251         fm->status_sent = HTTP_ACCEPTED;
252     }
253     if (cfg_get_integer(&fm->status_error, grp, octstr_imm("generic-status-error")) == -1) {
254         fm->status_error = HTTP_ACCEPTED;
255     }
256 
257     return fm;
258 }
259 
generic_receive_sms(SMSCConn * conn,HTTPClient * client,List * headers,Octstr * body,List * cgivars)260 static void generic_receive_sms(SMSCConn *conn, HTTPClient *client,
261                                List *headers, Octstr *body, List *cgivars)
262 {
263     ConnData *conndata = conn->data;
264     struct generic_values *values = conndata->data;
265     struct fieldmap *fm = values->map;
266     Octstr *user, *pass, *from, *to, *text, *udh, *account, *binfo, *meta_data;
267     Octstr *dlrmid, *dlrerr;
268     Octstr *tmp_string, *retmsg;
269     int dlrmask;
270     List *reply_headers;
271     int ret, retstatus;
272 
273     dlrmask = SMS_PARAM_UNDEFINED;
274 
275     /* Parse enough parameters to validate the request */
276     user = http_cgi_variable(cgivars, octstr_get_cstr(fm->username));
277     pass = http_cgi_variable(cgivars, octstr_get_cstr(fm->password));
278     from = http_cgi_variable(cgivars, octstr_get_cstr(fm->from));
279     to = http_cgi_variable(cgivars, octstr_get_cstr(fm->to));
280     text = http_cgi_variable(cgivars, octstr_get_cstr(fm->text));
281     udh = http_cgi_variable(cgivars, octstr_get_cstr(fm->udh));
282     dlrmid = http_cgi_variable(cgivars, octstr_get_cstr(fm->dlr_mid));
283     tmp_string = http_cgi_variable(cgivars, octstr_get_cstr(fm->dlr_mask));
284     if (tmp_string) {
285         sscanf(octstr_get_cstr(tmp_string),"%d", &dlrmask);
286     }
287     dlrerr = http_cgi_variable(cgivars, octstr_get_cstr(fm->dlr_err));
288 
289     debug("smsc.http.generic", 0, "HTTP[%s]: Received an HTTP request",
290           octstr_get_cstr(conn->id));
291 
292     if ((conndata->username != NULL && conndata->password != NULL) &&
293         (user == NULL || pass == NULL ||
294         octstr_compare(user, conndata->username) != 0 ||
295         octstr_compare(pass, conndata->password) != 0)) {
296         error(0, "HTTP[%s]: Authorization failure",
297               octstr_get_cstr(conn->id));
298         retmsg = octstr_create("Authorization failed for sendsms");
299         retstatus = fm->status_error;
300     } else if (dlrmask != DLR_UNDEFINED && dlrmid != NULL) {
301         /* we got a DLR, and we don't require additional values */
302         Msg *dlrmsg;
303 
304         dlrmsg = dlr_find(conn->id,
305             dlrmid, /* message id */
306             to, /* destination */
307             dlrmask, 0);
308 
309         if (dlrmsg != NULL) {
310             dlrmsg->sms.sms_type = report_mo;
311             dlrmsg->sms.msgdata = octstr_duplicate(text);
312             dlrmsg->sms.account = octstr_duplicate(conndata->username);
313 
314             debug("smsc.http.generic", 0, "HTTP[%s]: Received DLR for DLR-URL <%s>",
315                   octstr_get_cstr(conn->id), octstr_get_cstr(dlrmsg->sms.dlr_url));
316 
317             if (dlrerr != NULL) {
318                 /* pass errorcode as is */
319                 if (dlrmsg->sms.meta_data == NULL)
320                     dlrmsg->sms.meta_data = octstr_create("");
321 
322                 meta_data_set_value(dlrmsg->sms.meta_data, METADATA_DLR_GROUP,
323                                     octstr_imm(METADATA_DLR_GROUP_ERRORCODE), dlrerr, 1);
324             }
325 
326             Msg *resp = msg_duplicate(dlrmsg);
327             ret = bb_smscconn_receive(conn, dlrmsg);
328             if (ret == -1) {
329                 retmsg = octstr_create("Not accepted");
330                 retstatus = fm->status_error;
331             } else {
332                 retmsg = urltrans_fill_escape_codes(fm->message_sent, resp);
333                 retstatus = fm->status_sent;
334             }
335             msg_destroy(resp);
336         } else {
337             error(0,"HTTP[%s]: Got DLR but could not find message or was not interested "
338                   "in it id<%s> dst<%s>, type<%d>",
339                   octstr_get_cstr(conn->id), octstr_get_cstr(dlrmid),
340                   octstr_get_cstr(to), dlrmask);
341             retmsg = octstr_create("Unknown DLR, not accepted");
342             retstatus = fm->status_error;
343         }
344     } else if (from == NULL || to == NULL || text == NULL) {
345         error(0, "HTTP[%s]: Insufficient args",
346               octstr_get_cstr(conn->id));
347         retmsg = octstr_create("Insufficient args, rejected");
348         retstatus = fm->status_error;
349     } else if (udh != NULL && (octstr_len(udh) != octstr_get_char(udh, 0) + 1)) {
350         error(0, "HTTP[%s]: UDH field misformed, rejected",
351               octstr_get_cstr(conn->id));
352         retmsg = octstr_create("UDH field misformed, rejected");
353         retstatus = fm->status_error;
354     } else if (udh != NULL && octstr_len(udh) > MAX_SMS_OCTETS) {
355         error(0, "HTTP[%s]: UDH field is too long, rejected",
356               octstr_get_cstr(conn->id));
357         retmsg = octstr_create("UDH field is too long, rejected");
358         retstatus = fm->status_error;
359     }
360     else {
361         /* we got a normal MO SMS */
362         Msg *msg;
363         msg = msg_create(sms);
364 
365         /* Parse the rest of the parameters */
366         tmp_string = http_cgi_variable(cgivars, octstr_get_cstr(fm->flash));
367         if (tmp_string) {
368             sscanf(octstr_get_cstr(tmp_string),"%ld", &msg->sms.mclass);
369         }
370         tmp_string = http_cgi_variable(cgivars, octstr_get_cstr(fm->mclass));
371         if (tmp_string) {
372             sscanf(octstr_get_cstr(tmp_string),"%ld", &msg->sms.mclass);
373         }
374         tmp_string = http_cgi_variable(cgivars, octstr_get_cstr(fm->mwi));
375         if (tmp_string) {
376             sscanf(octstr_get_cstr(tmp_string),"%ld", &msg->sms.mwi);
377         }
378         tmp_string = http_cgi_variable(cgivars, octstr_get_cstr(fm->coding));
379         if (tmp_string) {
380             sscanf(octstr_get_cstr(tmp_string),"%ld", &msg->sms.coding);
381         }
382         tmp_string = http_cgi_variable(cgivars, octstr_get_cstr(fm->validity));
383         if (tmp_string) {
384             sscanf(octstr_get_cstr(tmp_string),"%ld", &msg->sms.validity);
385             msg->sms.validity = time(NULL) + msg->sms.validity * 60;
386         }
387         tmp_string = http_cgi_variable(cgivars, octstr_get_cstr(fm->deferred));
388         if (tmp_string) {
389             sscanf(octstr_get_cstr(tmp_string),"%ld", &msg->sms.deferred);
390             msg->sms.deferred = time(NULL) + msg->sms.deferred * 60;
391         }
392         account = http_cgi_variable(cgivars, octstr_get_cstr(fm->account));
393         binfo = http_cgi_variable(cgivars, octstr_get_cstr(fm->binfo));
394         meta_data = http_cgi_variable(cgivars, octstr_get_cstr(fm->meta_data));
395 
396         debug("smsc.http.generic", 0, "HTTP[%s]: Constructing new SMS",
397               octstr_get_cstr(conn->id));
398 
399         /* convert character encoding if required */
400         if (conndata->alt_charset &&
401             charset_convert(text, octstr_get_cstr(conndata->alt_charset),
402                     DEFAULT_CHARSET) != 0)
403             error(0, "Failed to convert msgdata from charset <%s> to <%s>, will leave it as it is.",
404                     octstr_get_cstr(conndata->alt_charset), DEFAULT_CHARSET);
405 
406         msg->sms.service = octstr_duplicate(user);
407         msg->sms.sender = octstr_duplicate(from);
408         msg->sms.receiver = octstr_duplicate(to);
409         msg->sms.msgdata = octstr_duplicate(text);
410         msg->sms.udhdata = octstr_duplicate(udh);
411         msg->sms.smsc_id = octstr_duplicate(conn->id);
412         msg->sms.time = time(NULL);
413         msg->sms.account = octstr_duplicate(account);
414         msg->sms.binfo = octstr_duplicate(binfo);
415         msg->sms.meta_data = octstr_duplicate(meta_data);
416         Msg *resp = msg_duplicate(msg);
417         ret = bb_smscconn_receive(conn, msg);
418         if (ret == -1) {
419             retmsg = octstr_create("Not accepted");
420             retstatus = fm->status_error;
421         } else {
422             retmsg = urltrans_fill_escape_codes(fm->message_sent, resp);
423             retstatus = fm->status_sent;
424         }
425         msg_destroy(resp);
426     }
427 
428     reply_headers = gwlist_create();
429     http_header_add(reply_headers, "Content-Type", "text/plain");
430     debug("smsc.http.generic", 0, "HTTP[%s]: Sending reply",
431           octstr_get_cstr(conn->id));
432     http_send_reply(client, retstatus, reply_headers, retmsg);
433 
434     octstr_destroy(retmsg);
435     http_destroy_headers(reply_headers);
436 }
437 
438 
generic_send_sms(SMSCConn * conn,Msg * sms)439 static int generic_send_sms(SMSCConn *conn, Msg *sms)
440 {
441     ConnData *conndata = conn->data;
442     Octstr *url = NULL;
443     List *headers;
444 
445     /* We use the escape code population function from our
446      * URLTranslation module to fill in the appropriate values
447      * into the URL scheme. */
448     url = urltrans_fill_escape_codes(conndata->send_url, sms);
449 
450     headers = gwlist_create();
451     debug("smsc.http.generic", 0, "HTTP[%s]: Sending request <%s>",
452           octstr_get_cstr(conn->id), octstr_get_cstr(url));
453     http_start_request(conndata->http_ref, HTTP_METHOD_GET, url, headers,
454                        NULL, 0, sms, NULL);
455 
456     octstr_destroy(url);
457     http_destroy_headers(headers);
458 
459     return 0;
460 }
461 
generic_parse_reply(SMSCConn * conn,Msg * msg,int status,List * headers,Octstr * body)462 static void generic_parse_reply(SMSCConn *conn, Msg *msg, int status,
463                                 List *headers, Octstr *body)
464 {
465     ConnData *conndata = conn->data;
466     struct generic_values *values = conndata->data;
467     regmatch_t pmatch[2];
468     Octstr *msgid = NULL;
469 
470     /*
471      * Our generic type checks only content on the HTTP response body.
472      * We use the pre-compiled regex to match against the states.
473      * This is the most generic criteria (at the moment).
474      */
475     if ((values->success_regex != NULL) &&
476         (gw_regex_exec(values->success_regex, body, 0, NULL, 0) == 0)) {
477         /* SMSC ACK... the message id should be in the body */
478 
479         /* add to our own DLR storage */
480         if (DLR_IS_ENABLED_DEVICE(msg->sms.dlr_mask)) {
481             /* directive 'generic-foreign-id-regex' is present, fetch the foreign ID */
482             if ((values->generic_foreign_id_regex != NULL)) {
483                 if (gw_regex_exec(values->generic_foreign_id_regex, body, sizeof(pmatch) / sizeof(regmatch_t), pmatch, 0) == 0) {
484                     if (pmatch[1].rm_so != -1 && pmatch[1].rm_eo != -1) {
485                         msgid = octstr_copy(body, pmatch[1].rm_so, pmatch[1].rm_eo - pmatch[1].rm_so);
486                         debug("smsc.http.generic", 0, "HTTP[%s]: Found foreign message id <%s> in body.",
487                               octstr_get_cstr(conn->id), octstr_get_cstr(msgid));
488                         dlr_add(conn->id, msgid, msg, 0);
489                         octstr_destroy(msgid);
490                     }
491                 }
492                 if (msgid == NULL)
493                     warning(0, "HTTP[%s]: Can't get the foreign message id from the HTTP body.",
494                             octstr_get_cstr(conn->id));
495             } else {
496                 char id[UUID_STR_LEN + 1];
497                 /* use own own UUID as msg ID in the DLR storage */
498                 uuid_unparse(msg->sms.id, id);
499                 msgid = octstr_create(id);
500                 dlr_add(conn->id, msgid, msg, 0);
501                 octstr_destroy(msgid);
502             }
503         }
504         bb_smscconn_sent(conn, msg, NULL);
505     }
506     else if ((values->permfail_regex != NULL) &&
507         (gw_regex_exec(values->permfail_regex, body, 0, NULL, 0) == 0)) {
508         error(0, "HTTP[%s]: Message not accepted.", octstr_get_cstr(conn->id));
509         bb_smscconn_send_failed(conn, msg,
510             SMSCCONN_FAILED_MALFORMED, octstr_duplicate(body));
511     }
512     else if ((values->tempfail_regex != NULL) &&
513         (gw_regex_exec(values->tempfail_regex, body, 0, NULL, 0) == 0)) {
514         warning(0, "HTTP[%s]: Message temporary not accepted, will retry.",
515                 octstr_get_cstr(conn->id));
516         bb_smscconn_send_failed(conn, msg,
517             SMSCCONN_FAILED_TEMPORARILY, octstr_duplicate(body));
518     }
519     else {
520         error(0, "HTTP[%s]: Message was rejected. SMSC reponse was:",
521               octstr_get_cstr(conn->id));
522         octstr_dump(body, 0);
523         bb_smscconn_send_failed(conn, msg,
524             SMSCCONN_FAILED_REJECTED, octstr_create("REJECTED"));
525     }
526 }
527 
generic_init(SMSCConn * conn,CfgGroup * cfg)528 static int generic_init(SMSCConn *conn, CfgGroup *cfg)
529 {
530     Octstr *os;
531     ConnData *conndata = conn->data;
532     struct generic_values *values;
533 
534     /* we need at least the criteria for a successful sent */
535     if ((os = cfg_get(cfg, octstr_imm("status-success-regex"))) == NULL) {
536         error(0, "HTTP[%s]: 'status-success-regex' required for generic http smsc",
537               octstr_get_cstr(conn->id));
538         return -1;
539     }
540     conndata->data = values = gw_malloc(sizeof(*values));
541     /* reset */
542     memset(conndata->data, 0, sizeof(*values));
543 
544     values->success_regex = values->permfail_regex = values->tempfail_regex = NULL;
545     values->generic_foreign_id_regex = NULL;
546 
547     values->map = generic_get_field_map(cfg);
548 
549     /* pre-compile regex expressions */
550     if (os != NULL) {   /* this is implicit due to the above if check */
551         if ((values->success_regex = gw_regex_comp(os, REG_EXTENDED|REG_NOSUB)) == NULL)
552             error(0, "HTTP[%s]: Could not compile pattern '%s' defined for variable 'status-success-regex'",
553                   octstr_get_cstr(conn->id), octstr_get_cstr(os));
554         octstr_destroy(os);
555     }
556     if ((os = cfg_get(cfg, octstr_imm("status-permfail-regex"))) != NULL) {
557         if ((values->permfail_regex = gw_regex_comp(os, REG_EXTENDED|REG_NOSUB)) == NULL)
558             panic(0, "Could not compile pattern '%s' defined for variable 'status-permfail-regex'", octstr_get_cstr(os));
559         octstr_destroy(os);
560     }
561     if ((os = cfg_get(cfg, octstr_imm("status-tempfail-regex"))) != NULL) {
562         if ((values->tempfail_regex = gw_regex_comp(os, REG_EXTENDED|REG_NOSUB)) == NULL)
563             panic(0, "Could not compile pattern '%s' defined for variable 'status-tempfail-regex'", octstr_get_cstr(os));
564         octstr_destroy(os);
565     }
566     if ((os = cfg_get(cfg, octstr_imm("generic-foreign-id-regex"))) != NULL) {
567         if ((values->generic_foreign_id_regex = gw_regex_comp(os, REG_EXTENDED)) == NULL)
568             panic(0, "Could not compile pattern '%s' defined for variable 'generic-foreign-id-regex'", octstr_get_cstr(os));
569         else {
570             /* check quickly that at least 1 group seems to be defined in the regex */
571             if (octstr_search_char(os, '(', 0) == -1 || octstr_search_char(os, ')', 0) == -1)
572                 warning(0, "HTTP[%s]: No group defined in pattern '%s' for variable 'generic-foreign-id-regex'", octstr_get_cstr(conn->id), octstr_get_cstr(os));
573         }
574         octstr_destroy(os);
575     }
576 
577     debug("", 0, "generic init completed");
578 
579     return 0;
580 }
581 
generic_destroy(SMSCConn * conn)582 static void generic_destroy(SMSCConn *conn)
583 {
584     ConnData *conndata;
585     struct generic_values *values;
586 
587     if (conn == NULL || conn->data == NULL)
588         return;
589 
590     conndata = conn->data;
591     values = conndata->data;
592 
593     fieldmap_destroy(values->map);
594     if (values->success_regex)
595         gw_regex_destroy(values->success_regex);
596     if (values->permfail_regex)
597         gw_regex_destroy(values->permfail_regex);
598     if (values->tempfail_regex)
599         gw_regex_destroy(values->tempfail_regex);
600     if (values->generic_foreign_id_regex)
601         gw_regex_destroy(values->generic_foreign_id_regex);
602 
603     gw_free(values);
604     conndata->data = NULL;
605 
606     debug("", 0, "generic destroy completed");
607 }
608 
609 struct smsc_http_fn_callbacks smsc_http_generic_callback = {
610         .init = generic_init,
611         .destroy = generic_destroy,
612         .send_sms = generic_send_sms,
613         .parse_reply = generic_parse_reply,
614         .receive_sms = generic_receive_sms,
615 };
616