since the module API is changing, it's a good time to const-ify the description(...
[asterisk/asterisk.git] / apps / app_osplookup.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2006, Digium, Inc.
5  *
6  * Mark Spencer <markster@digium.com>
7  *
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.
13  *
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.
17  */
18
19 /*!
20  * \file
21  * \brief Open Settlement Protocol Applications
22  *
23  * \author Mark Spencer <markster@digium.com>
24  * 
25  * \ingroup applications
26  */
27
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <unistd.h>
31 #include <string.h>
32 #include <ctype.h>
33
34 #include "asterisk.h"
35
36 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
37
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"
51
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"
56 "the variables:\n"
57 " ${OSPINHANDLE}:  The in_bound call transaction handle\n"
58 " ${OSPINTIMELIMIT}:  The in_bound call duration limit in seconds\n"
59 "\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";
65
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"
78 "\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";
84
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"
90 "\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";
96
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"
103 "\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";
109
110 LOCAL_USER_DECL;
111
112 static int ospauth_exec(struct ast_channel *chan, void *data)
113 {
114         int res = 0;
115         struct localuser* u;
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 = "";
122         int handle;
123         unsigned int timelimit;
124         char* tmp;
125         char buffer[OSP_INTSTR_SIZE];
126         char* status;
127
128         AST_DECLARE_APP_ARGS(args,
129                 AST_APP_ARG(provider);
130                 AST_APP_ARG(options);
131         );
132
133         LOCAL_USER_ADD(u);
134
135         if (!(tmp = ast_strdupa(data))) {
136                 ast_log(LOG_ERROR, "Out of memory\n");
137                 LOCAL_USER_REMOVE(u);
138                 return(-1);
139         }
140
141         AST_STANDARD_APP_ARGS(args, tmp);
142
143         if (!ast_strlen_zero(args.provider)) {
144                 provider = args.provider;
145         }
146         ast_log(LOG_DEBUG, "OSPAuth: provider '%s'\n", provider);
147
148         if (args.options) {
149                 if (strchr(args.options, 'j')) {
150                         priority_jump = 1;
151                 }
152         }
153         ast_log(LOG_DEBUG, "OSPAuth: priority jump '%d'\n", priority_jump);
154
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);
161                 }
162         }
163         ast_log(LOG_DEBUG, "OSPAuth: source '%s'\n", source);
164         ast_log(LOG_DEBUG, "OSPAuth: token size '%d'\n", strlen(token));
165
166         res = ast_osp_auth(provider, &handle, source, chan->cid.cid_num, chan->exten, token, &timelimit);
167         if (res > 0) {
168                 status = OSP_APP_SUCCESS;
169         } else {
170                 timelimit = OSP_DEF_TIMELIMIT;
171                 if (!res) {
172                         status = OSP_APP_FAILED;
173                 } else {
174                         handle = OSP_INVALID_HANDLE;
175                         status = OSP_APP_ERROR;
176                 }
177         }
178
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);
187
188         if(!res) {
189                 if (priority_jump || ast_opt_priority_jumping) {
190                         ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
191                 } else {
192                         res = -1;
193                 }
194         } else if (res > 0) {
195                 res = 0;
196         }
197
198         LOCAL_USER_REMOVE(u);
199
200         return(res);
201 }
202
203 static int osplookup_exec(struct ast_channel *chan, void *data)
204 {
205         int res = 0;
206         struct localuser* u;
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 = "";
212         char* tmp;
213         char buffer[OSP_TOKSTR_SIZE];
214         struct ast_osp_result result;
215         char* status;
216
217         AST_DECLARE_APP_ARGS(args,
218                 AST_APP_ARG(exten);
219                 AST_APP_ARG(provider);
220                 AST_APP_ARG(options);
221         );
222         
223         if (ast_strlen_zero(data)) {
224                 ast_log(LOG_WARNING, "OSPLookup: Arg required, OSPLookup(exten[|provider[|options]])\n");
225                 return(-1);
226         }
227
228         LOCAL_USER_ADD(u);
229
230         if (!(tmp = ast_strdupa(data))) {
231                 ast_log(LOG_ERROR, "Out of memory\n");
232                 LOCAL_USER_REMOVE(u);
233                 return(-1);
234         }
235
236         AST_STANDARD_APP_ARGS(args, tmp);
237
238         ast_log(LOG_DEBUG, "OSPLookup: exten '%s'\n", args.exten);
239
240         if (!ast_strlen_zero(args.provider)) {
241                 provider = args.provider;
242         }
243         ast_log(LOG_DEBUG, "OSPlookup: provider '%s'\n", provider);
244
245         if (args.options) {
246                 if (strchr(args.options, 'j')) {
247                         priority_jump = 1;
248                 }
249         }
250         ast_log(LOG_DEBUG, "OSPLookup: priority jump '%d'\n", priority_jump);
251
252         result.inhandle = OSP_INVALID_HANDLE;
253
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;
259                         }
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;
263                         }
264                 } else if (!strcasecmp(ast_var_name(current), "OSPPEERIP")) {
265                         srcdev = ast_var_value(current);
266                 }
267         }
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);
271
272         res = ast_osp_lookup(provider, srcdev, chan->cid.cid_num, args.exten, &result);
273         if (res > 0) {
274                 status = OSP_APP_SUCCESS;
275         } else {
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;
282                 if (!res) {
283                         status = OSP_APP_FAILED;
284                 } else {
285                         result.outhandle = OSP_INVALID_HANDLE;
286                         status = OSP_APP_ERROR;
287                 }
288         }
289
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));
305         }
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);
314
315         if(!res) {
316                 if (priority_jump || ast_opt_priority_jumping) {
317                         ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
318                 } else {
319                         res = -1;
320                 }
321         } else if (res > 0) {
322                 res = 0;
323         }
324
325         LOCAL_USER_REMOVE(u);
326
327         return(res);
328 }
329
330 static int str2cause(char *str)
331 {
332         int cause = AST_CAUSE_NORMAL;
333
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;
348         } else {
349                 ast_log(LOG_WARNING, "OSP: Unknown cause '%s', using NORMAL\n", str);
350         }
351
352         return(cause);
353 }
354
355 static int ospnext_exec(struct ast_channel *chan, void *data)
356 {
357         int res=0;
358         struct localuser *u;
359         int priority_jump = 0;
360         int cause;
361         struct varshead* headp;
362         struct ast_var_t* current;
363         struct ast_osp_result result;
364         char *tmp;
365         char buffer[OSP_TOKSTR_SIZE];
366         char* status;
367
368         AST_DECLARE_APP_ARGS(args,
369                 AST_APP_ARG(cause);
370                 AST_APP_ARG(options);
371         );
372         
373         if (ast_strlen_zero(data)) {
374                 ast_log(LOG_WARNING, "OSPNext: Arg required, OSPNext(cause[|options])\n");
375                 return(-1);
376         }
377
378         LOCAL_USER_ADD(u);
379
380         if (!(tmp = ast_strdupa(data))) {
381                 ast_log(LOG_ERROR, "Out of memory\n");
382                 LOCAL_USER_REMOVE(u);
383                 return(-1);
384         }
385
386         AST_STANDARD_APP_ARGS(args, tmp);
387
388         cause = str2cause(args.cause);
389         ast_log(LOG_DEBUG, "OSPNext: cause '%d'\n", cause);
390
391         if (args.options) {
392                 if (strchr(args.options, 'j'))
393                         priority_jump = 1;
394         }
395         ast_log(LOG_DEBUG, "OSPNext: priority jump '%d'\n", priority_jump);
396
397         result.inhandle = OSP_INVALID_HANDLE;
398         result.outhandle = OSP_INVALID_HANDLE;
399         result.numresults = 0;
400
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;
406                         }
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;
410                         }
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;
414                         }
415                 } else if (!strcasecmp(ast_var_name(current), "OSPRESULTS")) {
416                         if (sscanf(ast_var_value(current), "%d", &result.numresults) != 1) {
417                                 result.numresults = 0;
418                         }
419                 }
420         }
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);
425
426         if ((res = ast_osp_next(cause, &result)) > 0) {
427                 status = OSP_APP_SUCCESS;
428         } else {
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;
435                 if (!res) {
436                         status = OSP_APP_FAILED;
437                 } else {
438                         result.outhandle = OSP_INVALID_HANDLE;
439                         status = OSP_APP_ERROR;
440                 }
441         }
442
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));
455         }
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);
464
465         if(!res) {
466                 if (priority_jump || ast_opt_priority_jumping) {
467                         ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
468                 } else {
469                         res = -1;
470                 }
471         } else if (res > 0) {
472                 res = 0;
473         }
474
475         LOCAL_USER_REMOVE(u);
476
477         return(res);
478 }
479
480 static int ospfinished_exec(struct ast_channel *chan, void *data)
481 {
482         int res = 1;
483         struct localuser* u;
484         int priority_jump = 0;
485         int cause;
486         struct varshead* headp;
487         struct ast_var_t* current;
488         int inhandle = OSP_INVALID_HANDLE;
489         int outhandle = OSP_INVALID_HANDLE;
490         int recorded = 0;
491         time_t start, connect, end;
492         char* tmp;
493         char* str = "";
494         char buffer[OSP_INTSTR_SIZE];
495         char* status;
496
497         AST_DECLARE_APP_ARGS(args,
498                 AST_APP_ARG(status);
499                 AST_APP_ARG(options);
500         );
501         
502         LOCAL_USER_ADD(u);
503
504         if (!(tmp = ast_strdupa(data))) {
505                 ast_log(LOG_ERROR, "Out of memory\n");
506                 LOCAL_USER_REMOVE(u);
507                 return(-1);
508         }
509
510         AST_STANDARD_APP_ARGS(args, tmp);
511
512         if (args.options) {
513                 if (strchr(args.options, 'j'))
514                         priority_jump = 1;
515         }
516         ast_log(LOG_DEBUG, "OSPFinish: priority jump '%d'\n", priority_jump);
517
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;
523                         }
524                 } else if (!strcasecmp(ast_var_name(current), "OSPOUTHANDLE")) {
525                         if (sscanf(ast_var_value(current), "%d", &outhandle) != 1) {
526                                 outhandle = OSP_INVALID_HANDLE;
527                         }
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"))) 
532                 {
533                         if (strcasecmp(ast_var_value(current), OSP_APP_SUCCESS)) {
534                                 recorded = 1;
535                         }
536                 }
537         }
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);
541
542         if (!recorded) {
543                 str = args.status;
544         }
545         cause = str2cause(str);
546         ast_log(LOG_DEBUG, "OSPFinish: cause '%d'\n", cause);
547
548         if (chan->cdr) {
549                 start = chan->cdr->start.tv_sec;
550                 connect = chan->cdr->answer.tv_sec;
551                 if (connect) {
552                         end = time(NULL);
553                 } else {
554                         end = connect;
555                 }
556         } else {
557                 start = 0;
558                 connect = 0;
559                 end = 0;
560         }
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);
564
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");
567         }
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");
570         }
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);
574
575         if (res > 0) {
576                 status = OSP_APP_SUCCESS;
577         } else if (!res) {
578                 status = OSP_APP_FAILED;
579         } else {
580                 status = OSP_APP_ERROR;
581         }
582         pbx_builtin_setvar_helper(chan, "OSPFINISHSTATUS", status);
583
584         if(!res) {
585                 if (priority_jump || ast_opt_priority_jumping) {
586                         ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
587                 } else {
588                         res = -1;
589                 }
590         } else if (res > 0) {
591                 res = 0;
592         }
593
594         LOCAL_USER_REMOVE(u);
595
596         return(res);
597 }
598
599 int load_module(void)
600 {
601         int res;
602         
603         ast_osp_adduse();
604
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);
609
610         return(res);
611 }
612
613 int unload_module(void)
614 {
615         int res;
616         
617         res = ast_unregister_application(app4);
618         res |= ast_unregister_application(app3);
619         res |= ast_unregister_application(app2);
620         res |= ast_unregister_application(app1);
621
622         STANDARD_HANGUP_LOCALUSERS;
623
624         ast_osp_deluse();
625
626         return(res);
627 }
628
629 int reload(void)
630 {
631         return(0);
632 }
633
634 const char *description(void)
635 {
636         return("Open Settlement Protocol Applications");
637 }
638
639 int usecount(void)
640 {
641         int res;
642         STANDARD_USECOUNT(res);
643         return(res);
644 }
645
646 const char *key()
647 {
648         return(ASTERISK_GPL_KEY);
649 }
650