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