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;
+}