diff --git a/apps/Makefile b/apps/Makefile index e5af2b4d7fb97a5a173ffd75e1f448b24d83275d..bdf491e35d1d747ca24dab2a616d076ff5cde867 100755 --- a/apps/Makefile +++ b/apps/Makefile @@ -40,6 +40,7 @@ endif APPS+=$(shell if [ -f /usr/include/linux/zaptel.h ]; then echo "app_zapras.so app_meetme.so app_flash.so app_zapbarge.so app_zapscan.so" ; fi) APPS+=$(shell if [ -f /usr/local/include/zaptel.h ]; then echo "app_zapras.so app_meetme.so app_flash.so app_zapbarge.so app_zapscan.so" ; fi) +APPS+=$(shell if [ -f /usr/include/osp/osp.h ]; then echo "app_osplookup.so" ; fi) CFLAGS+=-fPIC diff --git a/apps/app_dial.c b/apps/app_dial.c index 3a963f9f3e74f6116c163c9c2c5b1e5ebfc6fe1b..4b05ad478e7366b28f64d6839b6087461ac3b38c 100755 --- a/apps/app_dial.c +++ b/apps/app_dial.c @@ -362,6 +362,7 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in, struct localu if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP))) { /* Got hung up */ *to=-1; + strcpy(status, "CANCEL"); return NULL; } if (f && (f->frametype == AST_FRAME_DTMF) && *allowdisconnect && @@ -722,36 +723,21 @@ static int dial_exec(struct ast_channel *chan, void *data) /* If creating a SIP channel, look for a variable called */ /* VXML_URL in the calling channel and copy it to the */ /* new channel. */ - if (strcasecmp(tech,"SIP")==0) - { - headp=&chan->varshead; - AST_LIST_TRAVERSE(headp,current,entries) { - if (strcasecmp(ast_var_name(current),"VXML_URL")==0) - { - newvar=ast_var_assign(ast_var_name(current),ast_var_value(current)); - newheadp=&tmp->chan->varshead; - AST_LIST_INSERT_HEAD(newheadp,newvar,entries); - break; - } - } - } + /* Check for ALERT_INFO in the SetVar list. This is for */ /* SIP distinctive ring as per the RFC. For Cisco 7960s, */ /* SetVar(ALERT_INFO=<x>) where x is an integer value 1-5. */ /* However, the RFC says it should be a URL. -km- */ - - if (strcasecmp(tech,"SIP")==0) - { - headp=&chan->varshead; - AST_LIST_TRAVERSE(headp,current,entries) { - /* Search for ALERT_INFO */ - if (strcasecmp(ast_var_name(current),"ALERT_INFO")==0) - { - newvar=ast_var_assign(ast_var_name(current),ast_var_value(current)); - newheadp=&tmp->chan->varshead; - AST_LIST_INSERT_HEAD(newheadp,newvar,entries); - break; - } + headp=&chan->varshead; + AST_LIST_TRAVERSE(headp,current,entries) { + if (!strcasecmp(ast_var_name(current),"VXML_URL") || + !strcasecmp(ast_var_name(current), "ALERT_INFO") || + !strcasecmp(ast_var_name(current), "OSPTOKEN") || + !strcasecmp(ast_var_name(current), "OSPHANDLE")) + { + newvar=ast_var_assign(ast_var_name(current),ast_var_value(current)); + newheadp=&tmp->chan->varshead; + AST_LIST_INSERT_HEAD(newheadp,newvar,entries); } } @@ -850,6 +836,10 @@ static int dial_exec(struct ast_channel *chan, void *data) goto out; } if (peer) { +#ifdef OSP_SUPPORT + /* Once call is answered, ditch the OSP Handle */ + pbx_builtin_setvar_helper(chan, "OSPHANDLE", ""); +#endif strcpy(status, "ANSWER"); /* Ah ha! Someone answered within the desired timeframe. Of course after this we will always return with -1 so that it is hung up properly after the diff --git a/apps/app_osplookup.c b/apps/app_osplookup.c new file mode 100755 index 0000000000000000000000000000000000000000..7e6e0105b9debe9f7f6f8285ed39c2d511f84eb4 --- /dev/null +++ b/apps/app_osplookup.c @@ -0,0 +1,274 @@ +/* + * Asterisk -- A telephony toolkit for Linux. + * + * Time of day - Report the time of day + * + * Copyright (C) 1999, Mark Spencer + * + * Mark Spencer <markster@linux-support.net> + * + * This program is free software, distributed under the terms of + * the GNU General Public License + */ + +#include <asterisk/lock.h> +#include <asterisk/file.h> +#include <asterisk/logger.h> +#include <asterisk/channel.h> +#include <asterisk/pbx.h> +#include <asterisk/options.h> +#include <asterisk/config.h> +#include <asterisk/module.h> +#include <asterisk/utils.h> +#include <asterisk/causes.h> +#include <asterisk/astosp.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <stdlib.h> +#include <ctype.h> + +static char *tdesc = "OSP Lookup"; + +static char *app = "OSPLookup"; +static char *app2 = "OSPNext"; +static char *app3 = "OSPFinish"; + +static char *synopsis = "Lookup number in OSP"; +static char *synopsis2 = "Lookup next OSP entry"; +static char *synopsis3 = "Record OSP entry"; + +static char *descrip = +" OSPLookup(exten[|provider[|options]]): Looks up an extension via OSP and sets\n" +"the variables, where 'n' is the number of the result beginning with 1:\n" +" ${OSPTECH}: The technology to use for the call\n" +" ${OSPDEST}: The destination to use for the call\n" +" ${OSPTOKEN}: The actual OSP token as a string\n" +" ${OSPHANDLE}: The OSP Handle for anything remaining\n" +" ${OSPRESULTS}: The number of OSP results total remaining\n" +"\n" +"If the lookup was *not* successful and there exists a priority n + 101,\n" +"then that priority will be taken next.\n" ; + +static char *descrip2 = +" OSPNext: Looks up the next OSP Destination for ${OSPHANDLE}\n" +"See OSPLookup for more information\n" +"\n" +"If the lookup was *not* successful and there exists a priority n + 101,\n" +"then that priority will be taken next.\n" ; + +static char *descrip3 = +" OSPFinish(status): Records call state for ${OSPHANDLE}, according to\n" +"status, which should be one of BUSY, CONGESTION, ANSWER, NOANSWER, or NOCHANAVAIL\n" +"or coincidentally, just what the Dial application stores in its ${DIALSTATUS}\n" +"\n" +"If the finishing was *not* successful and there exists a priority n + 101,\n" +"then that priority will be taken next.\n" ; + +STANDARD_LOCAL_USER; + +LOCAL_USER_DECL; + +static int str2cause(char *cause) +{ + if (!strcasecmp(cause, "BUSY")) + return AST_CAUSE_BUSY; + if (!strcasecmp(cause, "CONGESTION")) + return AST_CAUSE_CONGESTION; + if (!strcasecmp(cause, "ANSWER")) + return AST_CAUSE_NORMAL; + if (!strcasecmp(cause, "CANCEL")) + return AST_CAUSE_NORMAL; + if (!strcasecmp(cause, "NOANSWER")) + return AST_CAUSE_NOANSWER; + if (!strcasecmp(cause, "NOCHANAVAIL")) + return AST_CAUSE_CONGESTION; + ast_log(LOG_WARNING, "Unknown cause '%s', using NORMAL\n", cause); + return AST_CAUSE_NORMAL; +} + +static int osplookup_exec(struct ast_channel *chan, void *data) +{ + int res=0; + struct localuser *u; + char *temp; + char *provider, *opts=NULL; + struct ast_osp_result result; + if (!data || ast_strlen_zero(data) || !(temp = ast_strdupa(data))) { + ast_log(LOG_WARNING, "OSPLookup requires an argument (extension)\n"); + return -1; + } + provider = strchr(temp, '|'); + if (provider) { + *provider = '\0'; + provider++; + opts = strchr(provider, '|'); + if (opts) { + *opts = '\0'; + opts++; + } + } + LOCAL_USER_ADD(u); + ast_log(LOG_DEBUG, "Whoo hoo, looking up OSP on '%s' via '%s'\n", temp, provider ? provider : "<default>"); + if ((res = ast_osp_lookup(chan, provider, temp, chan->callerid, &result)) > 0) { + char tmp[80]; + snprintf(tmp, sizeof(tmp), "%d", result.handle); + pbx_builtin_setvar_helper(chan, "OSPHANDLE", tmp); + pbx_builtin_setvar_helper(chan, "OSPTECH", result.tech); + pbx_builtin_setvar_helper(chan, "OSPDEST", result.dest); + pbx_builtin_setvar_helper(chan, "OSPTOKEN", result.token); + snprintf(tmp, sizeof(tmp), "%d", result.numresults); + pbx_builtin_setvar_helper(chan, "OSPRESULTS", tmp); + } else { + if (!res) + ast_log(LOG_NOTICE, "OSP Lookup failed for '%s' (provider '%s')\n", temp, provider ? provider : "<default>"); + else + ast_log(LOG_DEBUG, "Got hangup on '%s' while doing OSP Lookup for '%s' (provider '%s')!\n", chan->name, temp, provider ? provider : "<default>" ); + } + if (!res) { + /* Look for a "busy" place */ + if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 101, chan->callerid)) + chan->priority += 100; + } else if (res > 0) + res = 0; + LOCAL_USER_REMOVE(u); + return res; +} + +static int ospnext_exec(struct ast_channel *chan, void *data) +{ + int res=0; + struct localuser *u; + char *temp; + int cause; + struct ast_osp_result result; + if (!data || ast_strlen_zero(data)) { + ast_log(LOG_WARNING, "OSPNext should have an argument (cause)\n"); + } + LOCAL_USER_ADD(u); + cause = str2cause((char *)data); + temp = pbx_builtin_getvar_helper(chan, "OSPHANDLE"); + result.handle = -1; + if (temp && strlen(temp) && (sscanf(temp, "%i", &result.handle) == 1) && (result.handle > -1)) { + if ((res = ast_osp_next(&result, cause)) > 0) { + char tmp[80]; + snprintf(tmp, sizeof(tmp), "%d", result.handle); + pbx_builtin_setvar_helper(chan, "OSPHANDLE", tmp); + pbx_builtin_setvar_helper(chan, "OSPTECH", result.tech); + pbx_builtin_setvar_helper(chan, "OSPDEST", result.dest); + pbx_builtin_setvar_helper(chan, "OSPTOKEN", result.token); + snprintf(tmp, sizeof(tmp), "%d", result.numresults); + pbx_builtin_setvar_helper(chan, "OSPRESULTS", tmp); + } + } else { + if (!res) { + if (result.handle < 0) + ast_log(LOG_NOTICE, "OSP Lookup Next failed for handle '%d'\n", result.handle); + else + ast_log(LOG_DEBUG, "No OSP handle specified\n"); + } else + ast_log(LOG_DEBUG, "Got hangup on '%s' while doing OSP Next!\n", chan->name); + } + if (!res) { + /* Look for a "busy" place */ + if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 101, chan->callerid)) + chan->priority += 100; + } else if (res > 0) + res = 0; + LOCAL_USER_REMOVE(u); + return res; +} + +static int ospfinished_exec(struct ast_channel *chan, void *data) +{ + int res=0; + struct localuser *u; + char *temp; + int cause; + time_t start=0, duration=0; + struct ast_osp_result result; + if (!data || ast_strlen_zero(data)) { + ast_log(LOG_WARNING, "OSPFinish should have an argument (cause)\n"); + } + if (chan->cdr) { + start = chan->cdr->answer.tv_sec; + duration = time(NULL) - start; + } else + ast_log(LOG_WARNING, "OSPFinish called on channel '%s' with no CDR!\n", chan->name); + LOCAL_USER_ADD(u); + cause = str2cause((char *)data); + temp = pbx_builtin_getvar_helper(chan, "OSPHANDLE"); + result.handle = -1; + if (temp && strlen(temp) && (sscanf(temp, "%i", &result.handle) == 1) && (result.handle > -1)) { + if (!ast_osp_terminate(result.handle, cause, start, duration)) { + pbx_builtin_setvar_helper(chan, "OSPHANDLE", ""); + res = 1; + } + } else { + if (!res) { + if (result.handle > -1) + ast_log(LOG_NOTICE, "OSP Finish failed for handle '%d'\n", result.handle); + else + ast_log(LOG_DEBUG, "No OSP handle specified\n"); + } else + ast_log(LOG_DEBUG, "Got hangup on '%s' while doing OSP Terminate!\n", chan->name); + } + if (!res) { + /* Look for a "busy" place */ + if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 101, chan->callerid)) + chan->priority += 100; + } else if (res > 0) + res = 0; + LOCAL_USER_REMOVE(u); + return res; +} + + +int unload_module(void) +{ + int res; + STANDARD_HANGUP_LOCALUSERS; + res = ast_unregister_application(app3); + res |= ast_unregister_application(app2); + res |= ast_unregister_application(app); + return res; +} + +int load_module(void) +{ + int res; + res = ast_register_application(app, osplookup_exec, synopsis, descrip); + if (res) + return(res); + res = ast_register_application(app2, ospnext_exec, synopsis2, descrip2); + if (res) + return(res); + res = ast_register_application(app3, ospfinished_exec, synopsis3, descrip3); + if (res) + return(res); + return(0); +} + +int reload(void) +{ + return 0; +} + + +char *description(void) +{ + return tdesc; +} + +int usecount(void) +{ + int res; + STANDARD_USECOUNT(res); + return res; +} + +char *key() +{ + return ASTERISK_GPL_KEY; +} + diff --git a/channels/chan_sip.c b/channels/chan_sip.c index 751d44284690e4ddc2b9dbc8f4661abf715a7f75..3bc39b7036deeabb7591eff3cacdd19f1d36709c 100755 --- a/channels/chan_sip.c +++ b/channels/chan_sip.c @@ -40,6 +40,9 @@ #include <asterisk/astdb.h> #include <asterisk/causes.h> #include <asterisk/utils.h> +#ifdef OSP_SUPPORT +#include <asterisk/astosp.h> +#endif #include <sys/socket.h> #include <sys/ioctl.h> #include <net/if.h> @@ -314,6 +317,10 @@ static struct sip_pvt { int needreinvite; /* Do we need to send another reinvite? */ int pendingbye; /* Need to send bye after we ack? */ int gotrefer; /* Got a refer? */ +#ifdef OSP_SUPPORT + int osphandle; /* OSP Handle for call */ + time_t ospstart; /* OSP Start time */ +#endif struct sip_request initreq; /* Initial request */ int maxtime; /* Max time for first response */ @@ -514,7 +521,7 @@ static int transmit_response_with_sdp(struct sip_pvt *p, char *msg, struct sip_r static int transmit_response_with_auth(struct sip_pvt *p, char *msg, struct sip_request *req, char *rand, int reliable, char *header); static int transmit_request(struct sip_pvt *p, char *msg, int inc, int reliable, int newbranch); static int transmit_request_with_auth(struct sip_pvt *p, char *msg, int inc, int reliable, int newbranch); -static int transmit_invite(struct sip_pvt *p, char *msg, int sendsdp, char *auth, char *authheader, char *vxml_url,char *distinctive_ring, int init); +static int transmit_invite(struct sip_pvt *p, char *msg, int sendsdp, char *auth, char *authheader, char *vxml_url,char *distinctive_ring, char *osptoken,int init); static int transmit_reinvite_with_sdp(struct sip_pvt *p); static int transmit_info_with_digit(struct sip_pvt *p, char digit); static int transmit_message_with_text(struct sip_pvt *p, char *text); @@ -1355,6 +1362,10 @@ static int sip_call(struct ast_channel *ast, char *dest, int timeout) struct sip_pvt *p; char *vxml_url = NULL; char *distinctive_ring = NULL; + char *osptoken = NULL; +#ifdef OSP_SUPPORT + char *osphandle = NULL; +#endif struct varshead *headp; struct ast_var_t *current; @@ -1371,24 +1382,37 @@ static int sip_call(struct ast_channel *ast, char *dest, int timeout) if (strcasecmp(ast_var_name(current),"VXML_URL")==0) { vxml_url = ast_var_value(current); - break; - } + } else /* Check whether there is a ALERT_INFO variable */ if (strcasecmp(ast_var_name(current),"ALERT_INFO")==0) { distinctive_ring = ast_var_value(current); - break; } +#ifdef OSP_SUPPORT + else if (!strcasecmp(ast_var_name(current), "OSPTOKEN")) { + osptoken = ast_var_value(current); + } else if (!strcasecmp(ast_var_name(current), "OSPHANDLE")) { + osphandle = ast_var_value(current); + } +#endif } res = 0; p->outgoing = 1; +#ifdef OSP_SUPPORT + if (!osptoken || !osphandle || (sscanf(osphandle, "%i", &p->osphandle) != 1)) { + /* Force Disable OSP support */ + osptoken = NULL; + osphandle = NULL; + p->osphandle = -1; + } +#endif ast_log(LOG_DEBUG, "Outgoing Call for %s\n", p->username); res = update_user_counter(p,INC_OUT_USE); if ( res != -1 ) { p->restrictcid = ast->restrictcid; p->jointcapability = p->capability; - transmit_invite(p, "INVITE", 1, NULL, NULL, vxml_url,distinctive_ring, 1); + transmit_invite(p, "INVITE", 1, NULL, NULL, vxml_url,distinctive_ring, osptoken, 1); if (p->maxtime) { /* Initialize auto-congest time */ p->initid = ast_sched_add(sched, p->maxtime * 4, auto_congest, p); @@ -1606,6 +1630,11 @@ static int sip_hangup(struct ast_channel *ast) return 0; } ast_mutex_lock(&p->lock); +#ifdef OSP_SUPPORT + if ((p->osphandle > -1) && (ast->_state == AST_STATE_UP)) { + ast_osp_terminate(p->osphandle, AST_CAUSE_NORMAL, p->ospstart, time(NULL) - p->ospstart); + } +#endif if ( p->outgoing ) { ast_log(LOG_DEBUG, "update_user_counter(%s) - decrement outUse counter\n", p->username); update_user_counter(p, DEC_OUT_USE); @@ -1687,8 +1716,9 @@ static int sip_answer(struct ast_channel *ast) ast_mutex_lock(&p->lock); if (ast->_state != AST_STATE_UP) { - - +#ifdef OSP_SUPPORT + time(&p->ospstart); +#endif codec=pbx_builtin_getvar_helper(p->owner,"SIP_CODEC"); if (codec) { @@ -2137,6 +2167,9 @@ static struct sip_pvt *sip_alloc(char *callid, struct sockaddr_in *sin, int useg p->initid = -1; p->autokillid = -1; p->stateid = -1; +#ifdef OSP_SUPPORT + p->osphandle = -1; +#endif p->rtp = ast_rtp_new(sched, io, 1, 0); if (videosupport) p->vrtp = ast_rtp_new(sched, io, 1, 0); @@ -3543,7 +3576,7 @@ static void initreqprep(struct sip_request *req, struct sip_pvt *p, char *cmd, c /*--- transmit_invite: Build REFER/INVITE/OPTIONS message and trasmit it ---*/ -static int transmit_invite(struct sip_pvt *p, char *cmd, int sdp, char *auth, char *authheader, char *vxml_url, char *distinctive_ring, int init) +static int transmit_invite(struct sip_pvt *p, char *cmd, int sdp, char *auth, char *authheader, char *vxml_url, char *distinctive_ring, char *osptoken, int init) { struct sip_request req; @@ -3564,7 +3597,11 @@ static int transmit_invite(struct sip_pvt *p, char *cmd, int sdp, char *auth, ch if (!ast_strlen_zero(p->referred_by)) add_header(&req, "Referred-By", p->referred_by); } - +#ifdef OSP_SUPPORT + if (osptoken && !ast_strlen_zero(osptoken)) { + add_header(&req, "P-OSP-Auth-Token", osptoken); + } +#endif if (distinctive_ring && !ast_strlen_zero(distinctive_ring)) { add_header(&req, "Alert-info",distinctive_ring); @@ -5792,7 +5829,7 @@ static int do_proxy_auth(struct sip_pvt *p, struct sip_request *req, char *heade /* No way to authenticate */ return -1; } - return transmit_invite(p,msg,!strcasecmp(msg, "INVITE"),digest, respheader, NULL,NULL, init); + return transmit_invite(p,msg,!strcasecmp(msg, "INVITE"),digest, respheader, NULL,NULL,NULL, init); } /*--- reply_digest: reply to authentication for outbound registrations ---*/ @@ -6222,6 +6259,9 @@ static void handle_response(struct sip_pvt *p, int resp, char *rest, struct sip_ build_route(p, req, 1); if (p->owner) { if (p->owner->_state != AST_STATE_UP) { +#ifdef OSP_SUPPORT + time(&p->ospstart); +#endif ast_setstate(p->owner, AST_STATE_UP); ast_queue_control(p->owner, AST_CONTROL_ANSWER); } else { @@ -7303,9 +7343,9 @@ static int sip_poke_peer(struct sip_peer *peer) p->outgoing = 1; #ifdef VOCAL_DATA_HACK strncpy(p->username, "__VOCAL_DATA_SHOULD_READ_THE_SIP_SPEC__", sizeof(p->username)); - transmit_invite(p, "INVITE", 0, NULL, NULL, NULL,NULL, 1); + transmit_invite(p, "INVITE", 0, NULL, NULL, NULL,NULL,NULL, 1); #else - transmit_invite(p, "OPTIONS", 0, NULL, NULL, NULL,NULL, 1); + transmit_invite(p, "OPTIONS", 0, NULL, NULL, NULL,NULL,NULL, 1); #endif gettimeofday(&peer->ps, NULL); peer->pokeexpire = ast_sched_add(sched, DEFAULT_MAXMS * 2, sip_poke_noanswer, peer); diff --git a/include/asterisk/astosp.h b/include/asterisk/astosp.h new file mode 100755 index 0000000000000000000000000000000000000000..92eeab82f81be1d467cc90a7143a678ff8241835 --- /dev/null +++ b/include/asterisk/astosp.h @@ -0,0 +1,35 @@ +/* + * Asterisk -- A telephony toolkit for Linux. + * + * OSP support + * + * Copyright (C) 1999, Mark Spencer + * + * Mark Spencer <markster@linux-support.net> + * + * This program is free software, distributed under the terms of + * the GNU General Public License + */ + +#ifndef _ASTERISK_OSP_H +#define _ASTERISK_OSP_H +#include <asterisk/channel.h> +#include <time.h> + +struct ast_osp_result { + int handle; + int numresults; + char tech[20]; + char dest[256]; + char token[4096]; +}; + +/* Note: Channel will be auto-serviced if specified. Returns -1 on hangup, + 0 if nothing found, or 1 if something is found */ +int ast_osp_lookup(struct ast_channel *chan, char *provider, char *extension, char *callerid, struct ast_osp_result *result); + +int ast_osp_next(struct ast_osp_result *result, int cause); + +int ast_osp_terminate(int handle, int cause, time_t start, time_t duration); + +#endif diff --git a/include/asterisk/causes.h b/include/asterisk/causes.h index 8f8e811fb17e58536a77fb489db55bd8d8b4f515..c77905def7c500f22418c044719ef00e3ae66107 100755 --- a/include/asterisk/causes.h +++ b/include/asterisk/causes.h @@ -20,5 +20,6 @@ #define AST_CAUSE_FAILURE 3 #define AST_CAUSE_CONGESTION 4 #define AST_CAUSE_UNALLOCATED 5 +#define AST_CAUSE_NOANSWER 6 #endif diff --git a/include/asterisk/utils.h b/include/asterisk/utils.h index 16212a038d60d47704c2f7c3db5143b651cbc80e..aa456262f933346e0501c64e63dfb03c3ad5c74b 100755 --- a/include/asterisk/utils.h +++ b/include/asterisk/utils.h @@ -25,7 +25,10 @@ struct ast_hostent { }; extern struct hostent *ast_gethostbyname(const char *host, struct ast_hostent *hp); +extern int ast_base64encode(char *dst, unsigned char *src, int srclen, int max); +extern int ast_base64decode(unsigned char *dst, char *src, int max); extern int test_for_thread_safety(void); +extern int ast_utils_init(void); #endif diff --git a/res/Makefile b/res/Makefile index fa6e3cf815e8176c6b95b480ccaf369f22783424..302c3499f6de825765bfd07cf51b62a86fc58630 100755 --- a/res/Makefile +++ b/res/Makefile @@ -14,6 +14,7 @@ MODS=res_adsi.so res_parking.so res_crypto.so res_musiconhold.so res_indications.so res_monitor.so MODS+=$(shell if [ -f "/usr/include/odbcinst.h" ]; then echo "res_odbc.so res_config_odbc.so"; fi) MODS+=$(shell if [ -f "/usr/local/include/odbcinst.h" ]; then echo "res_odbc.so res_config_odbc.so"; fi) +MODS+=$(shell if [ -f "/usr/include/osp/osp.h" ]; then echo "res_osp.so"; fi) CRYPTO_LIBS=-lssl -lcrypto @@ -24,6 +25,7 @@ CFLAGS+=$(shell [ -f /usr/local/include/zaptel.h ] && echo " -DZAPATA_MOH") # Work around buggy RedHat 9.0 # CFLAGS+=-DOPENSSL_NO_KRB5 -fPIC +OSPLIB=/usr/lib/libosp.a all: depend $(MODS) @@ -39,6 +41,9 @@ clean: res_odbc.so: res_odbc.o $(CC) $(SOLINK) -o $@ $< -lodbc +res_osp.so: res_osp.o $(OSPLIB) + $(CC) $(SOLINK) -o $@ $< $(OSPLIB) + %.so : %.o $(CC) $(SOLINK) -o $@ $< diff --git a/res/res_crypto.c b/res/res_crypto.c index 4e12eb5b776b49fe8bbba2a65217a8eb06733435..8df8d2b4cf80a238bc23cb27bb328f056f87b128 100755 --- a/res/res_crypto.c +++ b/res/res_crypto.c @@ -23,6 +23,7 @@ #include <asterisk/cli.h> #include <asterisk/io.h> #include <asterisk/lock.h> +#include <asterisk/utils.h> #include <openssl/ssl.h> #include <openssl/err.h> #include <stdio.h> @@ -56,9 +57,6 @@ * XXXX */ -static char base64[64]; -static char b2a[256]; - AST_MUTEX_DEFINE_STATIC(keylock); #define KEY_NEEDS_PASSCODE (1 << 16) @@ -298,89 +296,6 @@ static char *binary(int y, int len) #endif -static int base64decode(unsigned char *dst, char *src, int max) -{ - int cnt = 0; - unsigned int byte = 0; - unsigned int bits = 0; - int incnt = 0; -#if 0 - unsigned char *odst = dst; -#endif - while(*src && (cnt < max)) { - /* Shift in 6 bits of input */ - byte <<= 6; - byte |= (b2a[(int)(*src)]) & 0x3f; - bits += 6; -#if 0 - printf("Add: %c %s\n", *src, binary(b2a[(int)(*src)] & 0x3f, 6)); -#endif - src++; - incnt++; - /* If we have at least 8 bits left over, take that character - off the top */ - if (bits >= 8) { - bits -= 8; - *dst = (byte >> bits) & 0xff; -#if 0 - printf("Remove: %02x %s\n", *dst, binary(*dst, 8)); -#endif - dst++; - cnt++; - } - } -#if 0 - dump(odst, cnt); -#endif - /* Dont worry about left over bits, they're extra anyway */ - return cnt; -} - -static int base64encode(char *dst, unsigned char *src, int srclen, int max) -{ - int cnt = 0; - unsigned int byte = 0; - int bits = 0; - int index; - int cntin = 0; -#if 0 - char *odst = dst; - dump(src, srclen); -#endif - /* Reserve one bit for end */ - max--; - while((cntin < srclen) && (cnt < max)) { - byte <<= 8; -#if 0 - printf("Add: %02x %s\n", *src, binary(*src, 8)); -#endif - byte |= *(src++); - bits += 8; - cntin++; - while((bits >= 6) && (cnt < max)) { - bits -= 6; - /* We want only the top */ - index = (byte >> bits) & 0x3f; - *dst = base64[index]; -#if 0 - printf("Remove: %c %s\n", *dst, binary(index, 6)); -#endif - dst++; - cnt++; - } - } - if (bits && (cnt < max)) { - /* Add one last character for the remaining bits, - padding the rest with 0 */ - byte <<= (6 - bits); - index = (byte) & 0x3f; - *(dst++) = base64[index]; - cnt++; - } - *dst = '\0'; - return cnt; -} - int ast_sign(struct ast_key *key, char *msg, char *sig) { unsigned char digest[20]; @@ -410,7 +325,7 @@ int ast_sign(struct ast_key *key, char *msg, char *sig) } /* Success -- encode (256 bytes max as documented) */ - base64encode(sig, dsig, siglen, 256); + ast_base64encode(sig, dsig, siglen, 256); return 0; } @@ -429,7 +344,7 @@ int ast_check_signature(struct ast_key *key, char *msg, char *sig) } /* Decode signature */ - res = base64decode(dsig, sig, sizeof(dsig)); + res = ast_base64decode(dsig, sig, sizeof(dsig)); if (res != sizeof(dsig)) { ast_log(LOG_WARNING, "Signature improper length (expect %d, got %d)\n", (int)sizeof(dsig), (int)res); return -1; @@ -558,41 +473,8 @@ static struct ast_cli_entry cli_show_keys = static struct ast_cli_entry cli_init_keys = { { "init", "keys", NULL }, init_keys, "Initialize RSA key passcodes", init_keys_usage }; -static void base64_init(void) -{ - int x; - memset(b2a, -1, sizeof(b2a)); - /* Initialize base-64 Conversion table */ - for (x=0;x<26;x++) { - /* A-Z */ - base64[x] = 'A' + x; - b2a['A' + x] = x; - /* a-z */ - base64[x + 26] = 'a' + x; - b2a['a' + x] = x + 26; - /* 0-9 */ - if (x < 10) { - base64[x + 52] = '0' + x; - b2a['0' + x] = x + 52; - } - } - base64[62] = '+'; - base64[63] = '/'; - b2a[(int)'+'] = 62; - b2a[(int)'/'] = 63; -#if 0 - for (x=0;x<64;x++) { - if (b2a[(int)base64[x]] != x) { - fprintf(stderr, "!!! %d failed\n", x); - } else - fprintf(stderr, "--- %d passed\n", x); - } -#endif -} - static int crypto_init(void) { - base64_init(); SSL_library_init(); ERR_load_crypto_strings(); ast_cli_register(&cli_show_keys); diff --git a/res/res_osp.c b/res/res_osp.c new file mode 100755 index 0000000000000000000000000000000000000000..deac15c7bb6f50664ed1d3a75dd5c673ce91b991 --- /dev/null +++ b/res/res_osp.c @@ -0,0 +1,736 @@ +/* + * Asterisk -- A telephony toolkit for Linux. + * + * Provide Open Settlement Protocol capability + * + * Copyright (C) 2004, Digium, Inc. + * + * Mark Spencer <markster@digium.com> + * + * This program is free software, distributed under the terms of + * the GNU General Public License + */ + +#include <sys/types.h> +#include <asterisk/file.h> +#include <asterisk/channel.h> +#include <asterisk/logger.h> +#include <asterisk/say.h> +#include <asterisk/module.h> +#include <asterisk/options.h> +#include <asterisk/crypto.h> +#include <asterisk/md5.h> +#include <asterisk/cli.h> +#include <asterisk/io.h> +#include <asterisk/lock.h> +#include <asterisk/astosp.h> +#include <asterisk/config.h> +#include <asterisk/utils.h> +#include <asterisk/lock.h> +#include <asterisk/causes.h> +#include <osp.h> +#include <openssl/err.h> +#include <stdio.h> +#include <dirent.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <fcntl.h> +#include "../asterisk.h" +#include "../astconf.h" +#include <openssl/bio.h> +#include <openssl/pem.h> +#include <openssl/evp.h> + + +#define MAX_CERTS 10 +#define MAX_SERVICEPOINTS 10 +#define OSP_MAX 256 + +#define OSP_DEFAULT_MAX_CONNECTIONS 20 +#define OSP_DEFAULT_RETRY_DELAY 0 +#define OSP_DEFAULT_RETRY_LIMIT 2 +#define OSP_DEFAULT_TIMEOUT 500 + +static int loadPemCert(unsigned char *FileName, unsigned char *buffer, int *len); +static int loadPemPrivateKey(unsigned char *FileName, unsigned char *buffer, int *len); + +AST_MUTEX_DEFINE_STATIC(osplock); + +static int initialized = 0; +static int hardware = 0; + +struct osp_provider { + char name[OSP_MAX]; + char localpvtkey[OSP_MAX]; + char localcert[OSP_MAX]; + char cacerts[MAX_CERTS][OSP_MAX]; + int cacount; + char servicepoints[MAX_SERVICEPOINTS][OSP_MAX]; + char source[OSP_MAX]; + int spcount; + int dead; + int maxconnections; + int retrydelay; + int retrylimit; + int timeout; + OSPTPROVHANDLE handle; + struct osp_provider *next; +}; +static struct osp_provider *providers; + +static int osp_build(struct ast_config *cfg, char *cat) +{ + OSPTCERT TheAuthCert[MAX_CERTS]; + unsigned char Reqbuf[4096],LocalBuf[4096],AuthBuf[MAX_CERTS][4096]; + struct ast_variable *v; + struct osp_provider *osp; + int x,length,errorcode=0; + int mallocd=0,i; + char *cacerts[MAX_CERTS]; + const char *servicepoints[MAX_SERVICEPOINTS]; + OSPTPRIVATEKEY privatekey; + OSPTCERT localcert; + OSPTCERT *authCerts[MAX_CERTS]; + + + + ast_mutex_lock(&osplock); + osp = providers; + while(osp) { + if (!strcasecmp(osp->name, cat)) + break; + osp = osp->next; + } + ast_mutex_unlock(&osplock); + if (!osp) { + mallocd = 1; + osp = malloc(sizeof(struct osp_provider)); + if (!osp) { + ast_log(LOG_WARNING, "Out of memory!\n"); + return -1; + } + memset(osp, 0, sizeof(struct osp_provider)); + osp->handle = -1; + } + strncpy(osp->name, cat, sizeof(osp->name) - 1); + snprintf(osp->localpvtkey, sizeof(osp->localpvtkey), AST_KEY_DIR "/%s-privatekey.pem", cat); + snprintf(osp->localcert, sizeof(osp->localpvtkey), AST_KEY_DIR "/%s-localcert.pem", cat); + osp->maxconnections=OSP_DEFAULT_MAX_CONNECTIONS; + osp->retrydelay = OSP_DEFAULT_RETRY_DELAY; + osp->retrylimit = OSP_DEFAULT_RETRY_LIMIT; + osp->timeout = OSP_DEFAULT_TIMEOUT; + strcpy(osp->source, ""); + ast_log(LOG_DEBUG, "Building OSP Provider '%s'\n", cat); + v = ast_variable_browse(cfg, cat); + while(v) { + if (!strcasecmp(v->name, "privatekey")) { + if (v->value[0] == '/') + strncpy(osp->localpvtkey, v->value, sizeof(osp->localpvtkey) - 1); + else + snprintf(osp->localpvtkey, sizeof(osp->localpvtkey), AST_KEY_DIR "/%s", v->value); + } else if (!strcasecmp(v->name, "localcert")) { + if (v->value[0] == '/') + strncpy(osp->localcert, v->value, sizeof(osp->localcert) - 1); + else + snprintf(osp->localcert, sizeof(osp->localcert), AST_KEY_DIR "/%s", v->value); + } else if (!strcasecmp(v->name, "cacert")) { + if (osp->cacount < MAX_CERTS) { + if (v->value[0] == '/') + strncpy(osp->cacerts[osp->cacount], v->value, sizeof(osp->cacerts[0])); + else + snprintf(osp->cacerts[osp->cacount], sizeof(osp->cacerts[0]), AST_KEY_DIR "/%s", v->value); + osp->cacount++; + } else + ast_log(LOG_WARNING, "Too many CA Certificates at line %d\n", v->lineno); + } else if (!strcasecmp(v->name, "servicepoint")) { + if (osp->spcount < MAX_SERVICEPOINTS) { + strncpy(osp->servicepoints[osp->spcount], v->value, sizeof(osp->servicepoints[0])); + osp->spcount++; + } else + ast_log(LOG_WARNING, "Too many Service points at line %d\n", v->lineno); + } else if (!strcasecmp(v->name, "maxconnections")) { + if ((sscanf(v->value, "%i", &x) == 1) && (x > 0) && (x <= 1000)) { + osp->maxconnections = x; + } else + ast_log(LOG_WARNING, "maxconnections should be an integer from 1 to 1000, not '%s' at line %d\n", v->value, v->lineno); + } else if (!strcasecmp(v->name, "retrydelay")) { + if ((sscanf(v->value, "%i", &x) == 1) && (x >= 0) && (x <= 10)) { + osp->retrydelay = x; + } else + ast_log(LOG_WARNING, "retrydelay should be an integer from 0 to 10, not '%s' at line %d\n", v->value, v->lineno); + } else if (!strcasecmp(v->name, "retrylimit")) { + if ((sscanf(v->value, "%i", &x) == 1) && (x >= 0) && (x <= 100)) { + osp->retrylimit = x; + } else + ast_log(LOG_WARNING, "retrylimit should be an integer from 0 to 100, not '%s' at line %d\n", v->value, v->lineno); + } else if (!strcasecmp(v->name, "timeout")) { + if ((sscanf(v->value, "%i", &x) == 1) && (x >= 200) && (x <= 10000)) { + osp->timeout = x; + } else + ast_log(LOG_WARNING, "timeout should be an integer from 200 to 10000, not '%s' at line %d\n", v->value, v->lineno); + } + v = v->next; + } + if (osp->cacount < 1) { + snprintf(osp->cacerts[osp->cacount], sizeof(osp->cacerts[0]), AST_KEY_DIR "/%s-cacert.pem", cat); + osp->cacount++; + } + for (x=0;x<osp->cacount;x++) + cacerts[x] = osp->cacerts[x]; + for (x=0;x<osp->spcount;x++) + servicepoints[x] = osp->servicepoints[x]; + + ast_mutex_lock(&osplock); + osp->dead = 0; + if (osp->handle > -1) { + ast_log(LOG_DEBUG, "Deleting old handle for '%s'\n", osp->name); + OSPPProviderDelete(osp->handle, 0); + } + + + length = 0; + ast_log(LOG_DEBUG, "Loading private key for '%s' (%s)\n", osp->name, osp->localpvtkey); + errorcode = loadPemPrivateKey(osp->localpvtkey,Reqbuf,&length); + if (errorcode == 0) + { + privatekey.PrivateKeyData = Reqbuf; + privatekey.PrivateKeyLength = length; + } + else + { + return -1; + } + + length = 0; + ast_log(LOG_DEBUG, "Loading local cert for '%s' (%s)\n", osp->name, osp->localcert); + errorcode = loadPemCert(osp->localcert,LocalBuf,&length); + if (errorcode == 0) + { + localcert.CertData = LocalBuf; + localcert.CertDataLength = length; + } + else + { + return -1; + } + + for (i=0;i<osp->cacount;i++) + { + length = 0; + ast_log(LOG_DEBUG, "Loading CA cert %d for '%s' (%s)\n", i + 1, osp->name, osp->cacerts[i]); + errorcode = loadPemCert(osp->cacerts[i],AuthBuf[i],&length); + if (errorcode == 0) + { + TheAuthCert[i].CertData = AuthBuf[i]; + TheAuthCert[i].CertDataLength = length; + authCerts[i] = &(TheAuthCert[i]); + } + else + { + return -1; + } + } + + ast_log(LOG_DEBUG, "Creating provider handle for '%s'\n", osp->name); + + ast_log(LOG_DEBUG, "Service point '%s %d'\n", servicepoints[0], osp->spcount); + + if (OSPPProviderNew(osp->spcount, + servicepoints, + NULL, + "localhost", + &privatekey, + &localcert, + osp->cacount, + (const OSPTCERT **)authCerts, + 1, + 300, + osp->maxconnections, + 60000, + osp->retrydelay, + osp->retrylimit, + osp->timeout, + "", + "", + &osp->handle)) { + ast_log(LOG_WARNING, "Unable to initialize provider '%s'\n", cat); + osp->dead = 1; + } + + if (mallocd) { + osp->next = providers; + providers = osp; + } + ast_mutex_unlock(&osplock); + return 0; +} + +static int show_osp(int fd, int argc, char *argv[]) +{ + struct osp_provider *osp; + char *search = NULL; + int x; + int found = 0; + if ((argc < 2) || (argc > 3)) + return RESULT_SHOWUSAGE; + if (argc > 2) + search = argv[2]; + if (!search) + ast_cli(fd, "OSP: %s %s\n", initialized ? "Initialized" : "Uninitialized", hardware ? "Accelerated" : "Normal"); + + ast_mutex_lock(&osplock); + osp = providers; + while(osp) { + if (!search || !strcasecmp(osp->name, search)) { + if (found) + ast_cli(fd, "\n"); + ast_cli(fd, " == OSP Provider '%s' ==\n", osp->name); + ast_cli(fd, "Local Private Key: %s\n", osp->localpvtkey); + ast_cli(fd, "Local Certificate: %s\n", osp->localcert); + for (x=0;x<osp->cacount;x++) + ast_cli(fd, "CA Certificate %d: %s\n", x + 1, osp->cacerts[x]); + for (x=0;x<osp->spcount;x++) + ast_cli(fd, "Service Point %d: %s\n", x + 1, osp->servicepoints[x]); + ast_cli(fd, "Max Connections: %d\n", osp->maxconnections); + ast_cli(fd, "Retry Delay: %d seconds\n", osp->retrydelay); + ast_cli(fd, "Retry Limit: %d\n", osp->retrylimit); + ast_cli(fd, "Timeout: %d milliseconds\n", osp->timeout); + ast_cli(fd, "Source: %s\n", strlen(osp->source) ? osp->source : "<unspecified>"); + ast_cli(fd, "OSP Handle: %d\n", osp->handle); + found++; + } + osp = osp->next; + } + ast_mutex_unlock(&osplock); + if (!found) { + if (search) + ast_cli(fd, "Unable to find OSP provider '%s'\n", search); + else + ast_cli(fd, "No OSP providers configured\n"); + } + return RESULT_SUCCESS; +} + + +/*----------------------------------------------* + * Loads the Certificate * + *----------------------------------------------*/ +static int loadPemCert(unsigned char *FileName, unsigned char *buffer, int *len) +{ + int length = 0; + unsigned char *temp; + BIO *bioIn = NULL; + X509 *cert=NULL; + int retVal = OSPC_ERR_NO_ERROR; + + temp = buffer; + bioIn = BIO_new_file((const char*)FileName,"r"); + if (bioIn == NULL) + { + ast_log(LOG_WARNING,"Failed to find the File - %s \n",FileName); + return -1; + } + else + { + cert = PEM_read_bio_X509(bioIn,NULL,NULL,NULL); + if (cert == NULL) + { + ast_log(LOG_WARNING,"Failed to parse the Certificate from the File - %s \n",FileName); + return -1; + } + else + { + length = i2d_X509(cert,&temp); + if (cert == 0) + { + ast_log(LOG_WARNING,"Failed to parse the Certificate from the File - %s, Length=0 \n",FileName); + return -1; + } + else + { + *len = length; + } + } + } + + if (bioIn != NULL) + { + BIO_free(bioIn); + } + + if (cert != NULL) + { + X509_free(cert); + } + return retVal; +} + +/*----------------------------------------------* + * Loads the Private Key * + *----------------------------------------------*/ +static int loadPemPrivateKey(unsigned char *FileName, unsigned char *buffer, int *len) +{ + int length = 0; + unsigned char *temp; + BIO *bioIn = NULL; + RSA *pKey = NULL; + int retVal = OSPC_ERR_NO_ERROR; + + temp = buffer; + + bioIn = BIO_new_file((const char*)FileName,"r"); + if (bioIn == NULL) + { + ast_log(LOG_WARNING,"Failed to find the File - %s \n",FileName); + return -1; + } + else + { + pKey = PEM_read_bio_RSAPrivateKey(bioIn,NULL,NULL,NULL); + if (pKey == NULL) + { + ast_log(LOG_WARNING,"Failed to parse the Private Key from the File - %s \n",FileName); + return -1; + } + else + { + length = i2d_RSAPrivateKey(pKey,&temp); + if (length == 0) + { + ast_log(LOG_WARNING,"Failed to parse the Private Key from the File - %s, Length=0 \n",FileName); + return -1; + } + else + { + *len = length; + } + } + } + if (bioIn != NULL) + { + BIO_free(bioIn); + } + + if (pKey != NULL) + { + RSA_free(pKey); + } + return retVal; +} + +int ast_osp_lookup(struct ast_channel *chan, char *provider, char *extension, char *callerid, struct ast_osp_result *result) +{ + int cres; + int res = 0; + int counts; + int tokenlen; + unsigned int dummy=0; + unsigned int timelimit; + unsigned int callidlen; + struct osp_provider *osp; + char source[OSP_MAX]; /* Same length as osp->source */ + char uniqueid[32] = ""; + char callednum[2048]=""; + char destination[2048]=""; + char token[2000]; + OSPTCALLID *callid; + OSPE_DEST_PROT prot; + + result->handle = -1; + result->numresults = 0; + strcpy(result->tech, ""); + strcpy(result->dest, ""); + strcpy(result->token, ""); + + if (!provider || !strlen(provider)) + provider = "default"; + + if (!callerid) + callerid = ""; + + if (chan) { + strncpy(uniqueid, chan->uniqueid, sizeof(uniqueid) - 1); + cres = ast_autoservice_start(chan); + if (cres < 0) + return cres; + } + ast_mutex_lock(&osplock); + osp = providers; + while(osp) { + if (!strcasecmp(osp->name, provider)) { + if (OSPPTransactionNew(osp->handle, &result->handle)) { + ast_log(LOG_WARNING, "Unable to create OSP Transaction handle!\n"); + } else { + strcpy(source, osp->source); + res = 1; + } + break; + } + osp = osp->next; + } + ast_mutex_unlock(&osplock); + if (res) { + res = 0; + callid = OSPPCallIdNew(strlen(uniqueid), uniqueid); + if (callid) { + /* No more than 10 back */ + counts = 10; + dummy = 0; + callidlen = sizeof(uniqueid); + if (!OSPPTransactionRequestAuthorisation(result->handle, source, "", + callerid,OSPC_E164, extension, OSPC_E164, NULL, 1, &callid, NULL, &counts, &dummy, NULL)) { + if (counts) { + tokenlen = sizeof(token); + result->numresults = counts - 1; + if (!OSPPTransactionGetFirstDestination(result->handle, 0, NULL, NULL, &timelimit, &callidlen, uniqueid, + sizeof(callednum), callednum, sizeof(destination), destination, 0, NULL, &tokenlen, token)) { + ast_log(LOG_DEBUG, "Got destination '%s' and '%s' for '%s' (provider '%s')\n", + destination, callednum, extension, provider); + do { + ast_base64encode(result->token, token, tokenlen, sizeof(result->token) - 1); + if ((strlen(destination) > 2) && !OSPPTransactionGetDestProtocol(result->handle, &prot)) { + res = 1; + /* Strip leading and trailing brackets */ + destination[strlen(destination) - 1] = '\0'; + switch(prot) { + case OSPE_DEST_PROT_H323_SETUP: + strcpy(result->tech, "H323"); + snprintf(result->dest, sizeof(result->dest), "%s@%s", callednum, destination + 1); + break; + case OSPE_DEST_PROT_SIP: + strcpy(result->tech, "SIP"); + snprintf(result->dest, sizeof(result->dest), "%s@%s", callednum, destination + 1); + break; + default: + ast_log(LOG_DEBUG, "Unknown destination protocol '%d', skipping...\n", prot); + res = 0; + } + if (!res && result->numresults) { + result->numresults--; + if (OSPPTransactionGetNextDestination(result->handle, OSPC_FAIL_INCOMPATIBLE_DEST, 0, NULL, NULL, &timelimit, &callidlen, uniqueid, + sizeof(callednum), callednum, sizeof(destination), destination, 0, NULL, &tokenlen, token)) { + break; + } + } + } else { + ast_log(LOG_DEBUG, "Missing destination protocol\n"); + break; + } + } while(!res && result->numresults); + } + } + + } + OSPPCallIdDelete(&callid); + } + if (!res) { + OSPPTransactionDelete(result->handle); + result->handle = -1; + } + + } + if (!osp) + ast_log(LOG_NOTICE, "OSP Provider '%s' does not exist!\n", provider); + if (chan) { + cres = ast_autoservice_stop(chan); + if (cres < 0) + return cres; + } + return res; +} + +int ast_osp_next(struct ast_osp_result *result, int cause) +{ + int res = 0; + int tokenlen; + unsigned int dummy=0; + unsigned int timelimit; + unsigned int callidlen; + char uniqueid[32] = ""; + char callednum[2048]=""; + char destination[2048]=""; + char token[2000]; + OSPE_DEST_PROT prot; + + strcpy(result->tech, ""); + strcpy(result->dest, ""); + strcpy(result->token, ""); + + if (result->handle > -1) { + dummy = 0; + callidlen = sizeof(uniqueid); + if (result->numresults) { + tokenlen = sizeof(token); + while(!res && result->numresults) { + result->numresults--; + if (!OSPPTransactionGetNextDestination(result->handle, OSPC_FAIL_INCOMPATIBLE_DEST, 0, NULL, NULL, &timelimit, &callidlen, uniqueid, + sizeof(callednum), callednum, sizeof(destination), destination, 0, NULL, &tokenlen, token)) { + ast_base64encode(result->token, token, tokenlen, sizeof(result->token) - 1); + if ((strlen(destination) > 2) && !OSPPTransactionGetDestProtocol(result->handle, &prot)) { + res = 1; + /* Strip leading and trailing brackets */ + destination[strlen(destination) - 1] = '\0'; + switch(prot) { + case OSPE_DEST_PROT_H323_SETUP: + strcpy(result->tech, "H323"); + snprintf(result->dest, sizeof(result->dest), "%s@%s", callednum, destination + 1); + break; + case OSPE_DEST_PROT_SIP: + strcpy(result->tech, "SIP"); + snprintf(result->dest, sizeof(result->dest), "%s@%s", callednum, destination + 1); + break; + default: + ast_log(LOG_DEBUG, "Unknown destination protocol '%d', skipping...\n", prot); + res = 0; + } + } else { + ast_log(LOG_DEBUG, "Missing destination protocol\n"); + break; + } + } + } + + } + if (!res) { + OSPPTransactionDelete(result->handle); + result->handle = -1; + } + + } + return res; +} + +static enum OSPEFAILREASON cause2reason(int cause) +{ + switch(cause) { + case AST_CAUSE_BUSY: + return OSPC_FAIL_USER_BUSY; + case AST_CAUSE_CONGESTION: + return OSPC_FAIL_SWITCHING_EQUIPMENT_CONGESTION; + case AST_CAUSE_UNALLOCATED: + return OSPC_FAIL_UNALLOC_NUMBER; + case AST_CAUSE_NOTDEFINED: + return OSPC_FAIL_NORMAL_UNSPECIFIED; + case AST_CAUSE_NOANSWER: + return OSPC_FAIL_NO_ANSWER_FROM_USER; + case AST_CAUSE_NORMAL: + default: + return OSPC_FAIL_NORMAL_CALL_CLEARING; + } +} + +int ast_osp_terminate(int handle, int cause, time_t start, time_t duration) +{ + unsigned int dummy = 0; + int res = -1; + enum OSPEFAILREASON reason; + reason = cause2reason(cause); + if (OSPPTransactionRecordFailure(handle, reason)) + ast_log(LOG_WARNING, "Failed to record call termination for handle %d\n", handle); + else if (OSPPTransactionReportUsage(handle, duration, start, 0, 0, 0, 0, &dummy, NULL)) + ast_log(LOG_WARNING, "Failed to report duration for handle %d\n", handle); + else { + ast_log(LOG_DEBUG, "Completed recording handle %d\n", handle); + OSPPTransactionDelete(handle); + res = 0; + } + return res; +} + +static int config_load(void) +{ + struct ast_config *cfg; + char *cat; + struct osp_provider *osp, *prev = NULL, *next; + ast_mutex_lock(&osplock); + osp = providers; + while(osp) { + osp->dead = 1; + osp = osp->next; + } + ast_mutex_unlock(&osplock); + cfg = ast_load("osp.conf"); + if (cfg) { + if (!initialized) { + cat = ast_variable_retrieve(cfg, "general", "accelerate"); + if (cat && ast_true(cat)) + if (OSPPInit(1)) { + ast_log(LOG_WARNING, "Failed to enable hardware accelleration, falling back to software mode\n"); + OSPPInit(0); + } else + hardware = 1; + else + OSPPInit(0); + initialized = 1; + } + cat = ast_category_browse(cfg, NULL); + while(cat) { + if (strcasecmp(cat, "general")) + osp_build(cfg, cat); + cat = ast_category_browse(cfg, cat); + } + ast_destroy(cfg); + } else + ast_log(LOG_NOTICE, "No OSP configuration found. OSP support disabled\n"); + ast_mutex_lock(&osplock); + osp = providers; + while(osp) { + next = osp->next; + if (osp->dead) { + if (prev) + prev->next = next; + else + providers = next; + /* XXX Cleanup OSP structure first XXX */ + free(osp); + } else + prev = osp; + osp = next; + } + ast_mutex_unlock(&osplock); + return 0; +} + +static char show_osp_usage[] = +"Usage: show osp\n" +" Displays information on Open Settlement Protocol\n"; + +static struct ast_cli_entry cli_show_osp = +{ { "show", "osp", NULL }, show_osp, "Displays OSP information", show_osp_usage }; + +int reload(void) +{ + config_load(); + ast_log(LOG_NOTICE, "XXX Should reload OSP config XXX\n"); + return 0; +} + +int load_module(void) +{ + config_load(); + ast_cli_register(&cli_show_osp); + return 0; +} + +int unload_module(void) +{ + /* Can't unload this once we're loaded */ + return -1; +} + +char *description(void) +{ + return "Open Settlement Protocol Support"; +} + +int usecount(void) +{ + /* We should never be unloaded */ + return 1; +} + +char *key() +{ + return ASTERISK_GPL_KEY; +}