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
36 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
38 #include "asterisk/lock.h"
39 #include "asterisk/file.h"
40 #include "asterisk/logger.h"
41 #include "asterisk/channel.h"
42 #include "asterisk/pbx.h"
43 #include "asterisk/options.h"
44 #include "asterisk/config.h"
45 #include "asterisk/module.h"
46 #include "asterisk/utils.h"
47 #include "asterisk/causes.h"
48 #include "asterisk/astosp.h"
49 #include "asterisk/app.h"
50 #include "asterisk/options.h"
52 static char *app1= "OSPAuth";
53 static char *synopsis1 = "OSP authentication";
54 static char *descrip1 =
55 " OSPAuth([provider[|options]]): Authenticate a SIP INVITE by OSP and sets\n"
57 " ${OSPINHANDLE}: The in_bound call transaction handle\n"
58 " ${OSPINTIMELIMIT}: The in_bound call duration limit in seconds\n"
60 "The option string may contain the following character:\n"
61 " 'j' -- jump to n+101 priority if the authentication was NOT successful\n"
62 "This application sets the following channel variable upon completion:\n"
63 " OSPAUTHSTATUS The status of the OSP Auth attempt as a text string, one of\n"
64 " SUCCESS | FAILED | ERROR\n";
66 static char *app2= "OSPLookup";
67 static char *synopsis2 = "Lookup destination by OSP";
68 static char *descrip2 =
69 " OSPLookup(exten[|provider[|options]]): Looks up an extension via OSP and sets\n"
70 "the variables, where 'n' is the number of the result beginning with 1:\n"
71 " ${OSPOUTHANDLE}: The OSP Handle for anything remaining\n"
72 " ${OSPTECH}: The technology to use for the call\n"
73 " ${OSPDEST}: The destination to use for the call\n"
74 " ${OSPCALLING}: The calling number to use for the call\n"
75 " ${OSPOUTTOKEN}: The actual OSP token as a string\n"
76 " ${OSPOUTTIMELIMIT}: The out_bound call duration limit in seconds\n"
77 " ${OSPRESULTS}: The number of OSP results total remaining\n"
79 "The option string may contain the following character:\n"
80 " 'j' -- jump to n+101 priority if the lookup was NOT successful\n"
81 "This application sets the following channel variable upon completion:\n"
82 " OSPLOOKUPSTATUS The status of the OSP Lookup attempt as a text string, one of\n"
83 " SUCCESS | FAILED | ERROR\n";
85 static char *app3 = "OSPNext";
86 static char *synopsis3 = "Lookup next destination by OSP";
87 static char *descrip3 =
88 " OSPNext(cause[|options]): Looks up the next OSP Destination for ${OSPOUTHANDLE}\n"
89 "See OSPLookup for more information\n"
91 "The option string may contain the following character:\n"
92 " 'j' -- jump to n+101 priority if the lookup was NOT successful\n"
93 "This application sets the following channel variable upon completion:\n"
94 " OSPNEXTSTATUS The status of the OSP Next attempt as a text string, one of\n"
95 " SUCCESS | FAILED |ERROR\n";
97 static char *app4 = "OSPFinish";
98 static char *synopsis4 = "Record OSP entry";
99 static char *descrip4 =
100 " OSPFinish([status[|options]]): Records call state for ${OSPINHANDLE}, according to\n"
101 "status, which should be one of BUSY, CONGESTION, ANSWER, NOANSWER, or CHANUNAVAIL\n"
102 "or coincidentally, just what the Dial application stores in its ${DIALSTATUS}.\n"
104 "The option string may contain the following character:\n"
105 " 'j' -- jump to n+101 priority if the finish attempt was NOT successful\n"
106 "This application sets the following channel variable upon completion:\n"
107 " OSPFINISHSTATUS The status of the OSP Finish attempt as a text string, one of\n"
108 " SUCCESS | FAILED |ERROR \n";
112 static int ospauth_exec(struct ast_channel *chan, void *data)
116 char* provider = OSP_DEF_PROVIDER;
117 int priority_jump = 0;
118 struct varshead* headp;
119 struct ast_var_t* current;
120 const char* source = "";
121 const char* token = "";
123 unsigned int timelimit;
125 char buffer[OSP_INTSTR_SIZE];
128 AST_DECLARE_APP_ARGS(args,
129 AST_APP_ARG(provider);
130 AST_APP_ARG(options);
135 if (!(tmp = ast_strdupa(data))) {
136 ast_log(LOG_ERROR, "Out of memory\n");
137 LOCAL_USER_REMOVE(u);
141 AST_STANDARD_APP_ARGS(args, tmp);
143 if (!ast_strlen_zero(args.provider)) {
144 provider = args.provider;
146 ast_log(LOG_DEBUG, "OSPAuth: provider '%s'\n", provider);
149 if (strchr(args.options, 'j')) {
153 ast_log(LOG_DEBUG, "OSPAuth: priority jump '%d'\n", priority_jump);
155 headp = &chan->varshead;
156 AST_LIST_TRAVERSE(headp, current, entries) {
157 if (!strcasecmp(ast_var_name(current), "OSPPEERIP")) {
158 source = ast_var_value(current);
159 } else if (!strcasecmp(ast_var_name(current), "OSPINTOKEN")) {
160 token = ast_var_value(current);
163 ast_log(LOG_DEBUG, "OSPAuth: source '%s'\n", source);
164 ast_log(LOG_DEBUG, "OSPAuth: token size '%d'\n", strlen(token));
166 res = ast_osp_auth(provider, &handle, source, chan->cid.cid_num, chan->exten, token, &timelimit);
168 status = OSP_APP_SUCCESS;
170 timelimit = OSP_DEF_TIMELIMIT;
172 status = OSP_APP_FAILED;
174 handle = OSP_INVALID_HANDLE;
175 status = OSP_APP_ERROR;
179 snprintf(buffer, sizeof(buffer), "%d", handle);
180 pbx_builtin_setvar_helper(chan, "OSPINHANDLE", buffer);
181 ast_log(LOG_DEBUG, "OSPAuth: OSPINHANDLE '%s'\n", buffer);
182 snprintf(buffer, sizeof(buffer), "%d", timelimit);
183 pbx_builtin_setvar_helper(chan, "OSPINTIMELIMIT", buffer);
184 ast_log(LOG_DEBUG, "OSPAuth: OSPINTIMELIMIT '%s'\n", buffer);
185 pbx_builtin_setvar_helper(chan, "OSPAUTHSTATUS", status);
186 ast_log(LOG_DEBUG, "OSPAuth: %s\n", status);
189 if (priority_jump || ast_opt_priority_jumping) {
190 ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
194 } else if (res > 0) {
198 LOCAL_USER_REMOVE(u);
203 static int osplookup_exec(struct ast_channel *chan, void *data)
207 char* provider = OSP_DEF_PROVIDER;
208 int priority_jump = 0;
209 struct varshead* headp;
210 struct ast_var_t* current;
211 const char* srcdev = "";
213 char buffer[OSP_TOKSTR_SIZE];
214 struct ast_osp_result result;
217 AST_DECLARE_APP_ARGS(args,
219 AST_APP_ARG(provider);
220 AST_APP_ARG(options);
223 if (ast_strlen_zero(data)) {
224 ast_log(LOG_WARNING, "OSPLookup: Arg required, OSPLookup(exten[|provider[|options]])\n");
230 if (!(tmp = ast_strdupa(data))) {
231 ast_log(LOG_ERROR, "Out of memory\n");
232 LOCAL_USER_REMOVE(u);
236 AST_STANDARD_APP_ARGS(args, tmp);
238 ast_log(LOG_DEBUG, "OSPLookup: exten '%s'\n", args.exten);
240 if (!ast_strlen_zero(args.provider)) {
241 provider = args.provider;
243 ast_log(LOG_DEBUG, "OSPlookup: provider '%s'\n", provider);
246 if (strchr(args.options, 'j')) {
250 ast_log(LOG_DEBUG, "OSPLookup: priority jump '%d'\n", priority_jump);
252 result.inhandle = OSP_INVALID_HANDLE;
254 headp = &chan->varshead;
255 AST_LIST_TRAVERSE(headp, current, entries) {
256 if (!strcasecmp(ast_var_name(current), "OSPINHANDLE")) {
257 if (sscanf(ast_var_value(current), "%d", &result.inhandle) != 1) {
258 result.inhandle = OSP_INVALID_HANDLE;
260 } else if (!strcasecmp(ast_var_name(current), "OSPINTIMELIMIT")) {
261 if (sscanf(ast_var_value(current), "%d", &result.intimelimit) != 1) {
262 result.intimelimit = OSP_DEF_TIMELIMIT;
264 } else if (!strcasecmp(ast_var_name(current), "OSPPEERIP")) {
265 srcdev = ast_var_value(current);
268 ast_log(LOG_DEBUG, "OSPLookup: OSPINHANDLE '%d'\n", result.inhandle);
269 ast_log(LOG_DEBUG, "OSPLookup: OSPINTIMELIMIT '%d'\n", result.intimelimit);
270 ast_log(LOG_DEBUG, "OSPLookup: source device '%s'\n", srcdev);
272 res = ast_osp_lookup(provider, srcdev, chan->cid.cid_num, args.exten, &result);
274 status = OSP_APP_SUCCESS;
276 result.tech[0] = '\0';
277 result.dest[0] = '\0';
278 result.calling[0] = '\0';
279 result.token[0] = '\0';
280 result.numresults = 0;
281 result.outtimelimit = OSP_DEF_TIMELIMIT;
283 status = OSP_APP_FAILED;
285 result.outhandle = OSP_INVALID_HANDLE;
286 status = OSP_APP_ERROR;
290 snprintf(buffer, sizeof(buffer), "%d", result.outhandle);
291 pbx_builtin_setvar_helper(chan, "OSPOUTHANDLE", buffer);
292 ast_log(LOG_DEBUG, "OSPLookup: OSPOUTHANDLE '%s'\n", buffer);
293 pbx_builtin_setvar_helper(chan, "OSPTECH", result.tech);
294 ast_log(LOG_DEBUG, "OSPLookup: OSPTECH '%s'\n", result.tech);
295 pbx_builtin_setvar_helper(chan, "OSPDEST", result.dest);
296 ast_log(LOG_DEBUG, "OSPLookup: OSPDEST '%s'\n", result.dest);
297 pbx_builtin_setvar_helper(chan, "OSPCALLING", result.calling);
298 ast_log(LOG_DEBUG, "OSPLookup: OSPCALLING '%s'\n", result.calling);
299 pbx_builtin_setvar_helper(chan, "OSPOUTTOKEN", result.token);
300 ast_log(LOG_DEBUG, "OSPLookup: OSPOUTTOKEN size '%d'\n", strlen(result.token));
301 if (!ast_strlen_zero(result.token)) {
302 snprintf(buffer, sizeof(buffer), "P-OSP-Auth-Token: %s", result.token);
303 pbx_builtin_setvar_helper(chan, "_SIPADDHEADER", buffer);
304 ast_log(LOG_DEBUG, "OSPLookup: SIPADDHEADER size '%d'\n", strlen(buffer));
306 snprintf(buffer, sizeof(buffer), "%d", result.numresults);
307 pbx_builtin_setvar_helper(chan, "OSPRESULTS", buffer);
308 ast_log(LOG_DEBUG, "OSPLookup: OSPRESULTS '%s'\n", buffer);
309 snprintf(buffer, sizeof(buffer), "%d", result.outtimelimit);
310 pbx_builtin_setvar_helper(chan, "OSPOUTTIMELIMIT", buffer);
311 ast_log(LOG_DEBUG, "OSPLookup: OSPOUTTIMELIMIT '%s'\n", buffer);
312 pbx_builtin_setvar_helper(chan, "OSPLOOKUPSTATUS", status);
313 ast_log(LOG_DEBUG, "OSPLookup: %s\n", status);
316 if (priority_jump || ast_opt_priority_jumping) {
317 ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
321 } else if (res > 0) {
325 LOCAL_USER_REMOVE(u);
330 static int str2cause(char *str)
332 int cause = AST_CAUSE_NORMAL;
334 if (ast_strlen_zero(str)) {
335 cause = AST_CAUSE_NOTDEFINED;
336 } else if (!strcasecmp(str, "BUSY")) {
337 cause = AST_CAUSE_BUSY;
338 } else if (!strcasecmp(str, "CONGESTION")) {
339 cause = AST_CAUSE_CONGESTION;
340 } else if (!strcasecmp(str, "ANSWER")) {
341 cause = AST_CAUSE_NORMAL;
342 } else if (!strcasecmp(str, "CANCEL")) {
343 cause = AST_CAUSE_NORMAL;
344 } else if (!strcasecmp(str, "NOANSWER")) {
345 cause = AST_CAUSE_NOANSWER;
346 } else if (!strcasecmp(str, "NOCHANAVAIL")) {
347 cause = AST_CAUSE_CONGESTION;
349 ast_log(LOG_WARNING, "OSP: Unknown cause '%s', using NORMAL\n", str);
355 static int ospnext_exec(struct ast_channel *chan, void *data)
359 int priority_jump = 0;
361 struct varshead* headp;
362 struct ast_var_t* current;
363 struct ast_osp_result result;
365 char buffer[OSP_TOKSTR_SIZE];
368 AST_DECLARE_APP_ARGS(args,
370 AST_APP_ARG(options);
373 if (ast_strlen_zero(data)) {
374 ast_log(LOG_WARNING, "OSPNext: Arg required, OSPNext(cause[|options])\n");
380 if (!(tmp = ast_strdupa(data))) {
381 ast_log(LOG_ERROR, "Out of memory\n");
382 LOCAL_USER_REMOVE(u);
386 AST_STANDARD_APP_ARGS(args, tmp);
388 cause = str2cause(args.cause);
389 ast_log(LOG_DEBUG, "OSPNext: cause '%d'\n", cause);
392 if (strchr(args.options, 'j'))
395 ast_log(LOG_DEBUG, "OSPNext: priority jump '%d'\n", priority_jump);
397 result.inhandle = OSP_INVALID_HANDLE;
398 result.outhandle = OSP_INVALID_HANDLE;
399 result.numresults = 0;
401 headp = &chan->varshead;
402 AST_LIST_TRAVERSE(headp, current, entries) {
403 if (!strcasecmp(ast_var_name(current), "OSPINHANDLE")) {
404 if (sscanf(ast_var_value(current), "%d", &result.inhandle) != 1) {
405 result.inhandle = OSP_INVALID_HANDLE;
407 } else if (!strcasecmp(ast_var_name(current), "OSPOUTHANDLE")) {
408 if (sscanf(ast_var_value(current), "%d", &result.outhandle) != 1) {
409 result.outhandle = OSP_INVALID_HANDLE;
411 } else if (!strcasecmp(ast_var_name(current), "OSPINTIMEOUT")) {
412 if (sscanf(ast_var_value(current), "%d", &result.intimelimit) != 1) {
413 result.intimelimit = OSP_DEF_TIMELIMIT;
415 } else if (!strcasecmp(ast_var_name(current), "OSPRESULTS")) {
416 if (sscanf(ast_var_value(current), "%d", &result.numresults) != 1) {
417 result.numresults = 0;
421 ast_log(LOG_DEBUG, "OSPNext: OSPINHANDLE '%d'\n", result.inhandle);
422 ast_log(LOG_DEBUG, "OSPNext: OSPOUTHANDLE '%d'\n", result.outhandle);
423 ast_log(LOG_DEBUG, "OSPNext: OSPINTIMELIMIT '%d'\n", result.intimelimit);
424 ast_log(LOG_DEBUG, "OSPNext: OSPRESULTS '%d'\n", result.numresults);
426 if ((res = ast_osp_next(cause, &result)) > 0) {
427 status = OSP_APP_SUCCESS;
429 result.tech[0] = '\0';
430 result.dest[0] = '\0';
431 result.calling[0] = '\0';
432 result.token[0] = '\0';
433 result.numresults = 0;
434 result.outtimelimit = OSP_DEF_TIMELIMIT;
436 status = OSP_APP_FAILED;
438 result.outhandle = OSP_INVALID_HANDLE;
439 status = OSP_APP_ERROR;
443 pbx_builtin_setvar_helper(chan, "OSPTECH", result.tech);
444 ast_log(LOG_DEBUG, "OSPNext: OSPTECH '%s'\n", result.tech);
445 pbx_builtin_setvar_helper(chan, "OSPDEST", result.dest);
446 ast_log(LOG_DEBUG, "OSPNext: OSPDEST '%s'\n", result.dest);
447 pbx_builtin_setvar_helper(chan, "OSPCALLING", result.calling);
448 ast_log(LOG_DEBUG, "OSPNext: OSPCALLING '%s'\n", result.calling);
449 pbx_builtin_setvar_helper(chan, "OSPOUTTOKEN", result.token);
450 ast_log(LOG_DEBUG, "OSPNext: OSPOUTTOKEN size '%d'\n", strlen(result.token));
451 if (!ast_strlen_zero(result.token)) {
452 snprintf(buffer, sizeof(buffer), "P-OSP-Auth-Token: %s", result.token);
453 pbx_builtin_setvar_helper(chan, "_SIPADDHEADER", buffer);
454 ast_log(LOG_DEBUG, "OSPNext: SIPADDHEADER size '%d'\n", strlen(buffer));
456 snprintf(buffer, sizeof(buffer), "%d", result.numresults);
457 pbx_builtin_setvar_helper(chan, "OSPRESULTS", buffer);
458 ast_log(LOG_DEBUG, "OSPNext: OSPRESULTS '%s'\n", buffer);
459 snprintf(buffer, sizeof(buffer), "%d", result.outtimelimit);
460 pbx_builtin_setvar_helper(chan, "OSPOUTTIMELIMIT", buffer);
461 ast_log(LOG_DEBUG, "OSPNext: OSPOUTTIMELIMIT '%s'\n", buffer);
462 pbx_builtin_setvar_helper(chan, "OSPNEXTSTATUS", status);
463 ast_log(LOG_DEBUG, "OSPNext: %s\n", status);
466 if (priority_jump || ast_opt_priority_jumping) {
467 ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
471 } else if (res > 0) {
475 LOCAL_USER_REMOVE(u);
480 static int ospfinished_exec(struct ast_channel *chan, void *data)
484 int priority_jump = 0;
486 struct varshead* headp;
487 struct ast_var_t* current;
488 int inhandle = OSP_INVALID_HANDLE;
489 int outhandle = OSP_INVALID_HANDLE;
491 time_t start, connect, end;
494 char buffer[OSP_INTSTR_SIZE];
497 AST_DECLARE_APP_ARGS(args,
499 AST_APP_ARG(options);
504 if (!(tmp = ast_strdupa(data))) {
505 ast_log(LOG_ERROR, "Out of memory\n");
506 LOCAL_USER_REMOVE(u);
510 AST_STANDARD_APP_ARGS(args, tmp);
513 if (strchr(args.options, 'j'))
516 ast_log(LOG_DEBUG, "OSPFinish: priority jump '%d'\n", priority_jump);
518 headp = &chan->varshead;
519 AST_LIST_TRAVERSE(headp, current, entries) {
520 if (!strcasecmp(ast_var_name(current), "OSPINHANDLE")) {
521 if (sscanf(ast_var_value(current), "%d", &inhandle) != 1) {
522 inhandle = OSP_INVALID_HANDLE;
524 } else if (!strcasecmp(ast_var_name(current), "OSPOUTHANDLE")) {
525 if (sscanf(ast_var_value(current), "%d", &outhandle) != 1) {
526 outhandle = OSP_INVALID_HANDLE;
528 } else if (!recorded &&
529 (!strcasecmp(ast_var_name(current), "OSPAUTHSTATUS") ||
530 !strcasecmp(ast_var_name(current), "OSPLOOKUPSTATUS") ||
531 !strcasecmp(ast_var_name(current), "OSPNEXTSTATUS")))
533 if (strcasecmp(ast_var_value(current), OSP_APP_SUCCESS)) {
538 ast_log(LOG_DEBUG, "OSPFinish: OSPINHANDLE '%d'\n", inhandle);
539 ast_log(LOG_DEBUG, "OSPFinish: OSPOUTHANDLE '%d'\n", outhandle);
540 ast_log(LOG_DEBUG, "OSPFinish: recorded '%d'\n", recorded);
545 cause = str2cause(str);
546 ast_log(LOG_DEBUG, "OSPFinish: cause '%d'\n", cause);
549 start = chan->cdr->start.tv_sec;
550 connect = chan->cdr->answer.tv_sec;
561 ast_log(LOG_DEBUG, "OSPFinish: start '%ld'\n", start);
562 ast_log(LOG_DEBUG, "OSPFinish: connect '%ld'\n", connect);
563 ast_log(LOG_DEBUG, "OSPFinish: end '%ld'\n", end);
565 if (ast_osp_finish(outhandle, cause, start, connect, end) <= 0) {
566 ast_log(LOG_DEBUG, "OSPFinish: Unable to report usage for out_bound call\n");
568 if (ast_osp_finish(inhandle, cause, start, connect, end) <= 0) {
569 ast_log(LOG_DEBUG, "OSPFinish: Unable to report usage for in_bound call\n");
571 snprintf(buffer, sizeof(buffer), "%d", OSP_INVALID_HANDLE);
572 pbx_builtin_setvar_helper(chan, "OSPOUTHANDLE", buffer);
573 pbx_builtin_setvar_helper(chan, "OSPINHANDLE", buffer);
576 status = OSP_APP_SUCCESS;
578 status = OSP_APP_FAILED;
580 status = OSP_APP_ERROR;
582 pbx_builtin_setvar_helper(chan, "OSPFINISHSTATUS", status);
585 if (priority_jump || ast_opt_priority_jumping) {
586 ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
590 } else if (res > 0) {
594 LOCAL_USER_REMOVE(u);
599 int load_module(void)
605 res = ast_register_application(app1, ospauth_exec, synopsis1, descrip1);
606 res |= ast_register_application(app2, osplookup_exec, synopsis2, descrip2);
607 res |= ast_register_application(app3, ospnext_exec, synopsis3, descrip3);
608 res |= ast_register_application(app4, ospfinished_exec, synopsis4, descrip4);
613 int unload_module(void)
617 res = ast_unregister_application(app4);
618 res |= ast_unregister_application(app3);
619 res |= ast_unregister_application(app2);
620 res |= ast_unregister_application(app1);
622 STANDARD_HANGUP_LOCALUSERS;
634 const char *description(void)
636 return("Open Settlement Protocol Applications");
642 STANDARD_USECOUNT(res);
648 return(ASTERISK_GPL_KEY);