2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2006, Digium, Inc.
6 * Mark Spencer <markster@digium.com>
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
21 * \brief Open Settlement Protocol Applications
23 * \author Mark Spencer <markster@digium.com>
25 * \ingroup applications
29 <depend>libosptk</depend>
35 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
43 #include "asterisk/lock.h"
44 #include "asterisk/file.h"
45 #include "asterisk/logger.h"
46 #include "asterisk/channel.h"
47 #include "asterisk/pbx.h"
48 #include "asterisk/options.h"
49 #include "asterisk/config.h"
50 #include "asterisk/module.h"
51 #include "asterisk/utils.h"
52 #include "asterisk/causes.h"
53 #include "asterisk/astosp.h"
54 #include "asterisk/app.h"
55 #include "asterisk/options.h"
57 static char *app1= "OSPAuth";
58 static char *synopsis1 = "OSP authentication";
59 static char *descrip1 =
60 " OSPAuth([provider[|options]]): Authenticate a SIP INVITE by OSP and sets\n"
62 " ${OSPINHANDLE}: The in_bound call transaction handle\n"
63 " ${OSPINTIMELIMIT}: The in_bound call duration limit in seconds\n"
65 "The option string may contain the following character:\n"
66 " 'j' -- jump to n+101 priority if the authentication was NOT successful\n"
67 "This application sets the following channel variable upon completion:\n"
68 " OSPAUTHSTATUS The status of the OSP Auth attempt as a text string, one of\n"
69 " SUCCESS | FAILED | ERROR\n";
71 static char *app2= "OSPLookup";
72 static char *synopsis2 = "Lookup destination by OSP";
73 static char *descrip2 =
74 " OSPLookup(exten[|provider[|options]]): Looks up an extension via OSP and sets\n"
75 "the variables, where 'n' is the number of the result beginning with 1:\n"
76 " ${OSPOUTHANDLE}: The OSP Handle for anything remaining\n"
77 " ${OSPTECH}: The technology to use for the call\n"
78 " ${OSPDEST}: The destination to use for the call\n"
79 " ${OSPCALLING}: The calling number to use for the call\n"
80 " ${OSPOUTTOKEN}: The actual OSP token as a string\n"
81 " ${OSPOUTTIMELIMIT}: The out_bound call duration limit in seconds\n"
82 " ${OSPRESULTS}: The number of OSP results total remaining\n"
84 "The option string may contain the following character:\n"
85 " 'j' -- jump to n+101 priority if the lookup was NOT successful\n"
86 "This application sets the following channel variable upon completion:\n"
87 " OSPLOOKUPSTATUS The status of the OSP Lookup attempt as a text string, one of\n"
88 " SUCCESS | FAILED | ERROR\n";
90 static char *app3 = "OSPNext";
91 static char *synopsis3 = "Lookup next destination by OSP";
92 static char *descrip3 =
93 " OSPNext(cause[|options]): Looks up the next OSP Destination for ${OSPOUTHANDLE}\n"
94 "See OSPLookup for more information\n"
96 "The option string may contain the following character:\n"
97 " 'j' -- jump to n+101 priority if the lookup was NOT successful\n"
98 "This application sets the following channel variable upon completion:\n"
99 " OSPNEXTSTATUS The status of the OSP Next attempt as a text string, one of\n"
100 " SUCCESS | FAILED |ERROR\n";
102 static char *app4 = "OSPFinish";
103 static char *synopsis4 = "Record OSP entry";
104 static char *descrip4 =
105 " OSPFinish([status[|options]]): Records call state for ${OSPINHANDLE}, according to\n"
106 "status, which should be one of BUSY, CONGESTION, ANSWER, NOANSWER, or CHANUNAVAIL\n"
107 "or coincidentally, just what the Dial application stores in its ${DIALSTATUS}.\n"
109 "The option string may contain the following character:\n"
110 " 'j' -- jump to n+101 priority if the finish attempt was NOT successful\n"
111 "This application sets the following channel variable upon completion:\n"
112 " OSPFINISHSTATUS The status of the OSP Finish attempt as a text string, one of\n"
113 " SUCCESS | FAILED |ERROR \n";
117 static int ospauth_exec(struct ast_channel *chan, void *data)
121 char* provider = OSP_DEF_PROVIDER;
122 int priority_jump = 0;
123 struct varshead* headp;
124 struct ast_var_t* current;
125 const char* source = "";
126 const char* token = "";
128 unsigned int timelimit;
130 char buffer[OSP_INTSTR_SIZE];
133 AST_DECLARE_APP_ARGS(args,
134 AST_APP_ARG(provider);
135 AST_APP_ARG(options);
140 tmp = ast_strdupa(data);
142 AST_STANDARD_APP_ARGS(args, tmp);
144 if (!ast_strlen_zero(args.provider)) {
145 provider = args.provider;
147 ast_log(LOG_DEBUG, "OSPAuth: provider '%s'\n", provider);
150 if (strchr(args.options, 'j')) {
154 ast_log(LOG_DEBUG, "OSPAuth: priority jump '%d'\n", priority_jump);
156 headp = &chan->varshead;
157 AST_LIST_TRAVERSE(headp, current, entries) {
158 if (!strcasecmp(ast_var_name(current), "OSPPEERIP")) {
159 source = ast_var_value(current);
160 } else if (!strcasecmp(ast_var_name(current), "OSPINTOKEN")) {
161 token = ast_var_value(current);
164 ast_log(LOG_DEBUG, "OSPAuth: source '%s'\n", source);
165 ast_log(LOG_DEBUG, "OSPAuth: token size '%d'\n", strlen(token));
167 res = ast_osp_auth(provider, &handle, source, chan->cid.cid_num, chan->exten, token, &timelimit);
169 status = OSP_APP_SUCCESS;
171 timelimit = OSP_DEF_TIMELIMIT;
173 status = OSP_APP_FAILED;
175 handle = OSP_INVALID_HANDLE;
176 status = OSP_APP_ERROR;
180 snprintf(buffer, sizeof(buffer), "%d", handle);
181 pbx_builtin_setvar_helper(chan, "OSPINHANDLE", buffer);
182 ast_log(LOG_DEBUG, "OSPAuth: OSPINHANDLE '%s'\n", buffer);
183 snprintf(buffer, sizeof(buffer), "%d", timelimit);
184 pbx_builtin_setvar_helper(chan, "OSPINTIMELIMIT", buffer);
185 ast_log(LOG_DEBUG, "OSPAuth: OSPINTIMELIMIT '%s'\n", buffer);
186 pbx_builtin_setvar_helper(chan, "OSPAUTHSTATUS", status);
187 ast_log(LOG_DEBUG, "OSPAuth: %s\n", status);
190 if (priority_jump || ast_opt_priority_jumping) {
191 ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
195 } else if (res > 0) {
199 LOCAL_USER_REMOVE(u);
204 static int osplookup_exec(struct ast_channel *chan, void *data)
208 char* provider = OSP_DEF_PROVIDER;
209 int priority_jump = 0;
210 struct varshead* headp;
211 struct ast_var_t* current;
212 const char* srcdev = "";
214 char buffer[OSP_TOKSTR_SIZE];
215 struct ast_osp_result result;
218 AST_DECLARE_APP_ARGS(args,
220 AST_APP_ARG(provider);
221 AST_APP_ARG(options);
224 if (ast_strlen_zero(data)) {
225 ast_log(LOG_WARNING, "OSPLookup: Arg required, OSPLookup(exten[|provider[|options]])\n");
231 tmp = ast_strdupa(data);
233 AST_STANDARD_APP_ARGS(args, tmp);
235 ast_log(LOG_DEBUG, "OSPLookup: exten '%s'\n", args.exten);
237 if (!ast_strlen_zero(args.provider)) {
238 provider = args.provider;
240 ast_log(LOG_DEBUG, "OSPlookup: provider '%s'\n", provider);
243 if (strchr(args.options, 'j')) {
247 ast_log(LOG_DEBUG, "OSPLookup: priority jump '%d'\n", priority_jump);
249 result.inhandle = OSP_INVALID_HANDLE;
251 headp = &chan->varshead;
252 AST_LIST_TRAVERSE(headp, current, entries) {
253 if (!strcasecmp(ast_var_name(current), "OSPINHANDLE")) {
254 if (sscanf(ast_var_value(current), "%d", &result.inhandle) != 1) {
255 result.inhandle = OSP_INVALID_HANDLE;
257 } else if (!strcasecmp(ast_var_name(current), "OSPINTIMELIMIT")) {
258 if (sscanf(ast_var_value(current), "%d", &result.intimelimit) != 1) {
259 result.intimelimit = OSP_DEF_TIMELIMIT;
261 } else if (!strcasecmp(ast_var_name(current), "OSPPEERIP")) {
262 srcdev = ast_var_value(current);
265 ast_log(LOG_DEBUG, "OSPLookup: OSPINHANDLE '%d'\n", result.inhandle);
266 ast_log(LOG_DEBUG, "OSPLookup: OSPINTIMELIMIT '%d'\n", result.intimelimit);
267 ast_log(LOG_DEBUG, "OSPLookup: source device '%s'\n", srcdev);
269 res = ast_osp_lookup(provider, srcdev, chan->cid.cid_num, args.exten, &result);
271 status = OSP_APP_SUCCESS;
273 result.tech[0] = '\0';
274 result.dest[0] = '\0';
275 result.calling[0] = '\0';
276 result.token[0] = '\0';
277 result.numresults = 0;
278 result.outtimelimit = OSP_DEF_TIMELIMIT;
280 status = OSP_APP_FAILED;
282 result.outhandle = OSP_INVALID_HANDLE;
283 status = OSP_APP_ERROR;
287 snprintf(buffer, sizeof(buffer), "%d", result.outhandle);
288 pbx_builtin_setvar_helper(chan, "OSPOUTHANDLE", buffer);
289 ast_log(LOG_DEBUG, "OSPLookup: OSPOUTHANDLE '%s'\n", buffer);
290 pbx_builtin_setvar_helper(chan, "OSPTECH", result.tech);
291 ast_log(LOG_DEBUG, "OSPLookup: OSPTECH '%s'\n", result.tech);
292 pbx_builtin_setvar_helper(chan, "OSPDEST", result.dest);
293 ast_log(LOG_DEBUG, "OSPLookup: OSPDEST '%s'\n", result.dest);
294 pbx_builtin_setvar_helper(chan, "OSPCALLING", result.calling);
295 ast_log(LOG_DEBUG, "OSPLookup: OSPCALLING '%s'\n", result.calling);
296 pbx_builtin_setvar_helper(chan, "OSPOUTTOKEN", result.token);
297 ast_log(LOG_DEBUG, "OSPLookup: OSPOUTTOKEN size '%d'\n", strlen(result.token));
298 if (!ast_strlen_zero(result.token)) {
299 snprintf(buffer, sizeof(buffer), "P-OSP-Auth-Token: %s", result.token);
300 pbx_builtin_setvar_helper(chan, "_SIPADDHEADER", buffer);
301 ast_log(LOG_DEBUG, "OSPLookup: SIPADDHEADER size '%d'\n", strlen(buffer));
303 snprintf(buffer, sizeof(buffer), "%d", result.numresults);
304 pbx_builtin_setvar_helper(chan, "OSPRESULTS", buffer);
305 ast_log(LOG_DEBUG, "OSPLookup: OSPRESULTS '%s'\n", buffer);
306 snprintf(buffer, sizeof(buffer), "%d", result.outtimelimit);
307 pbx_builtin_setvar_helper(chan, "OSPOUTTIMELIMIT", buffer);
308 ast_log(LOG_DEBUG, "OSPLookup: OSPOUTTIMELIMIT '%s'\n", buffer);
309 pbx_builtin_setvar_helper(chan, "OSPLOOKUPSTATUS", status);
310 ast_log(LOG_DEBUG, "OSPLookup: %s\n", status);
313 if (priority_jump || ast_opt_priority_jumping) {
314 ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
318 } else if (res > 0) {
322 LOCAL_USER_REMOVE(u);
327 static int str2cause(char *str)
329 int cause = AST_CAUSE_NORMAL;
331 if (ast_strlen_zero(str)) {
332 cause = AST_CAUSE_NOTDEFINED;
333 } else if (!strcasecmp(str, "BUSY")) {
334 cause = AST_CAUSE_BUSY;
335 } else if (!strcasecmp(str, "CONGESTION")) {
336 cause = AST_CAUSE_CONGESTION;
337 } else if (!strcasecmp(str, "ANSWER")) {
338 cause = AST_CAUSE_NORMAL;
339 } else if (!strcasecmp(str, "CANCEL")) {
340 cause = AST_CAUSE_NORMAL;
341 } else if (!strcasecmp(str, "NOANSWER")) {
342 cause = AST_CAUSE_NOANSWER;
343 } else if (!strcasecmp(str, "NOCHANAVAIL")) {
344 cause = AST_CAUSE_CONGESTION;
346 ast_log(LOG_WARNING, "OSP: Unknown cause '%s', using NORMAL\n", str);
352 static int ospnext_exec(struct ast_channel *chan, void *data)
356 int priority_jump = 0;
358 struct varshead* headp;
359 struct ast_var_t* current;
360 struct ast_osp_result result;
362 char buffer[OSP_TOKSTR_SIZE];
365 AST_DECLARE_APP_ARGS(args,
367 AST_APP_ARG(options);
370 if (ast_strlen_zero(data)) {
371 ast_log(LOG_WARNING, "OSPNext: Arg required, OSPNext(cause[|options])\n");
377 tmp = ast_strdupa(data);
379 AST_STANDARD_APP_ARGS(args, tmp);
381 cause = str2cause(args.cause);
382 ast_log(LOG_DEBUG, "OSPNext: cause '%d'\n", cause);
385 if (strchr(args.options, 'j'))
388 ast_log(LOG_DEBUG, "OSPNext: priority jump '%d'\n", priority_jump);
390 result.inhandle = OSP_INVALID_HANDLE;
391 result.outhandle = OSP_INVALID_HANDLE;
392 result.numresults = 0;
394 headp = &chan->varshead;
395 AST_LIST_TRAVERSE(headp, current, entries) {
396 if (!strcasecmp(ast_var_name(current), "OSPINHANDLE")) {
397 if (sscanf(ast_var_value(current), "%d", &result.inhandle) != 1) {
398 result.inhandle = OSP_INVALID_HANDLE;
400 } else if (!strcasecmp(ast_var_name(current), "OSPOUTHANDLE")) {
401 if (sscanf(ast_var_value(current), "%d", &result.outhandle) != 1) {
402 result.outhandle = OSP_INVALID_HANDLE;
404 } else if (!strcasecmp(ast_var_name(current), "OSPINTIMEOUT")) {
405 if (sscanf(ast_var_value(current), "%d", &result.intimelimit) != 1) {
406 result.intimelimit = OSP_DEF_TIMELIMIT;
408 } else if (!strcasecmp(ast_var_name(current), "OSPRESULTS")) {
409 if (sscanf(ast_var_value(current), "%d", &result.numresults) != 1) {
410 result.numresults = 0;
414 ast_log(LOG_DEBUG, "OSPNext: OSPINHANDLE '%d'\n", result.inhandle);
415 ast_log(LOG_DEBUG, "OSPNext: OSPOUTHANDLE '%d'\n", result.outhandle);
416 ast_log(LOG_DEBUG, "OSPNext: OSPINTIMELIMIT '%d'\n", result.intimelimit);
417 ast_log(LOG_DEBUG, "OSPNext: OSPRESULTS '%d'\n", result.numresults);
419 if ((res = ast_osp_next(cause, &result)) > 0) {
420 status = OSP_APP_SUCCESS;
422 result.tech[0] = '\0';
423 result.dest[0] = '\0';
424 result.calling[0] = '\0';
425 result.token[0] = '\0';
426 result.numresults = 0;
427 result.outtimelimit = OSP_DEF_TIMELIMIT;
429 status = OSP_APP_FAILED;
431 result.outhandle = OSP_INVALID_HANDLE;
432 status = OSP_APP_ERROR;
436 pbx_builtin_setvar_helper(chan, "OSPTECH", result.tech);
437 ast_log(LOG_DEBUG, "OSPNext: OSPTECH '%s'\n", result.tech);
438 pbx_builtin_setvar_helper(chan, "OSPDEST", result.dest);
439 ast_log(LOG_DEBUG, "OSPNext: OSPDEST '%s'\n", result.dest);
440 pbx_builtin_setvar_helper(chan, "OSPCALLING", result.calling);
441 ast_log(LOG_DEBUG, "OSPNext: OSPCALLING '%s'\n", result.calling);
442 pbx_builtin_setvar_helper(chan, "OSPOUTTOKEN", result.token);
443 ast_log(LOG_DEBUG, "OSPNext: OSPOUTTOKEN size '%d'\n", strlen(result.token));
444 if (!ast_strlen_zero(result.token)) {
445 snprintf(buffer, sizeof(buffer), "P-OSP-Auth-Token: %s", result.token);
446 pbx_builtin_setvar_helper(chan, "_SIPADDHEADER", buffer);
447 ast_log(LOG_DEBUG, "OSPNext: SIPADDHEADER size '%d'\n", strlen(buffer));
449 snprintf(buffer, sizeof(buffer), "%d", result.numresults);
450 pbx_builtin_setvar_helper(chan, "OSPRESULTS", buffer);
451 ast_log(LOG_DEBUG, "OSPNext: OSPRESULTS '%s'\n", buffer);
452 snprintf(buffer, sizeof(buffer), "%d", result.outtimelimit);
453 pbx_builtin_setvar_helper(chan, "OSPOUTTIMELIMIT", buffer);
454 ast_log(LOG_DEBUG, "OSPNext: OSPOUTTIMELIMIT '%s'\n", buffer);
455 pbx_builtin_setvar_helper(chan, "OSPNEXTSTATUS", status);
456 ast_log(LOG_DEBUG, "OSPNext: %s\n", status);
459 if (priority_jump || ast_opt_priority_jumping) {
460 ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
464 } else if (res > 0) {
468 LOCAL_USER_REMOVE(u);
473 static int ospfinished_exec(struct ast_channel *chan, void *data)
477 int priority_jump = 0;
479 struct varshead* headp;
480 struct ast_var_t* current;
481 int inhandle = OSP_INVALID_HANDLE;
482 int outhandle = OSP_INVALID_HANDLE;
484 time_t start, connect, end;
487 char buffer[OSP_INTSTR_SIZE];
490 AST_DECLARE_APP_ARGS(args,
492 AST_APP_ARG(options);
497 tmp = ast_strdupa(data);
499 AST_STANDARD_APP_ARGS(args, tmp);
502 if (strchr(args.options, 'j'))
505 ast_log(LOG_DEBUG, "OSPFinish: priority jump '%d'\n", priority_jump);
507 headp = &chan->varshead;
508 AST_LIST_TRAVERSE(headp, current, entries) {
509 if (!strcasecmp(ast_var_name(current), "OSPINHANDLE")) {
510 if (sscanf(ast_var_value(current), "%d", &inhandle) != 1) {
511 inhandle = OSP_INVALID_HANDLE;
513 } else if (!strcasecmp(ast_var_name(current), "OSPOUTHANDLE")) {
514 if (sscanf(ast_var_value(current), "%d", &outhandle) != 1) {
515 outhandle = OSP_INVALID_HANDLE;
517 } else if (!recorded &&
518 (!strcasecmp(ast_var_name(current), "OSPAUTHSTATUS") ||
519 !strcasecmp(ast_var_name(current), "OSPLOOKUPSTATUS") ||
520 !strcasecmp(ast_var_name(current), "OSPNEXTSTATUS")))
522 if (strcasecmp(ast_var_value(current), OSP_APP_SUCCESS)) {
527 ast_log(LOG_DEBUG, "OSPFinish: OSPINHANDLE '%d'\n", inhandle);
528 ast_log(LOG_DEBUG, "OSPFinish: OSPOUTHANDLE '%d'\n", outhandle);
529 ast_log(LOG_DEBUG, "OSPFinish: recorded '%d'\n", recorded);
534 cause = str2cause(str);
535 ast_log(LOG_DEBUG, "OSPFinish: cause '%d'\n", cause);
538 start = chan->cdr->start.tv_sec;
539 connect = chan->cdr->answer.tv_sec;
550 ast_log(LOG_DEBUG, "OSPFinish: start '%ld'\n", start);
551 ast_log(LOG_DEBUG, "OSPFinish: connect '%ld'\n", connect);
552 ast_log(LOG_DEBUG, "OSPFinish: end '%ld'\n", end);
554 if (ast_osp_finish(outhandle, cause, start, connect, end) <= 0) {
555 ast_log(LOG_DEBUG, "OSPFinish: Unable to report usage for out_bound call\n");
557 if (ast_osp_finish(inhandle, cause, start, connect, end) <= 0) {
558 ast_log(LOG_DEBUG, "OSPFinish: Unable to report usage for in_bound call\n");
560 snprintf(buffer, sizeof(buffer), "%d", OSP_INVALID_HANDLE);
561 pbx_builtin_setvar_helper(chan, "OSPOUTHANDLE", buffer);
562 pbx_builtin_setvar_helper(chan, "OSPINHANDLE", buffer);
565 status = OSP_APP_SUCCESS;
567 status = OSP_APP_FAILED;
569 status = OSP_APP_ERROR;
571 pbx_builtin_setvar_helper(chan, "OSPFINISHSTATUS", status);
574 if (priority_jump || ast_opt_priority_jumping) {
575 ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
579 } else if (res > 0) {
583 LOCAL_USER_REMOVE(u);
588 static int load_module(void *mod)
594 res = ast_register_application(app1, ospauth_exec, synopsis1, descrip1);
595 res |= ast_register_application(app2, osplookup_exec, synopsis2, descrip2);
596 res |= ast_register_application(app3, ospnext_exec, synopsis3, descrip3);
597 res |= ast_register_application(app4, ospfinished_exec, synopsis4, descrip4);
602 static int unload_module(void *mod)
606 res = ast_unregister_application(app4);
607 res |= ast_unregister_application(app3);
608 res |= ast_unregister_application(app2);
609 res |= ast_unregister_application(app1);
611 STANDARD_HANGUP_LOCALUSERS;
618 static const char *description(void)
620 return "Open Settlement Protocol Applications";
623 static const char *key(void)
625 return(ASTERISK_GPL_KEY);