don't use '%i' at all, since we have no current use cases that need non base-10 parsi...
[asterisk/asterisk.git] / res / res_osp.c
1 /*
2  * Asterisk -- A telephony toolkit for Linux.
3  *
4  * Provide Open Settlement Protocol capability
5  * 
6  * Copyright (C) 2004 - 2005, Digium, Inc.
7  *
8  * Mark Spencer <markster@digium.com>
9  *
10  * This program is free software, distributed under the terms of
11  * the GNU General Public License
12  */
13
14 #include <sys/types.h>
15 #include <osp.h>
16 #include "asterisk/file.h"
17 #include "asterisk/channel.h"
18 #include "asterisk/logger.h"
19 #include "asterisk/say.h"
20 #include "asterisk/module.h"
21 #include "asterisk/options.h"
22 #include "asterisk/crypto.h"
23 #include "asterisk/md5.h"
24 #include "asterisk/cli.h"
25 #include "asterisk/io.h"
26 #include "asterisk/lock.h"
27 #include "asterisk/astosp.h"
28 #include "asterisk/config.h"
29 #include "asterisk/utils.h"
30 #include "asterisk/lock.h"
31 #include "asterisk/causes.h"
32 #include "asterisk/callerid.h"
33 #include <openssl/err.h>
34 #include <stdio.h>
35 #include <dirent.h>
36 #include <string.h>
37 #include <errno.h>
38 #include <unistd.h>
39 #include <fcntl.h>
40 #include "asterisk.h"
41 #include "astconf.h"
42 #include <openssl/bio.h>
43 #include <openssl/pem.h>
44 #include <openssl/evp.h>
45
46
47 #define MAX_CERTS 10
48 #define MAX_SERVICEPOINTS 10
49 #define OSP_MAX 256
50
51 #define OSP_DEFAULT_MAX_CONNECTIONS     20
52 #define OSP_DEFAULT_RETRY_DELAY         0
53 #define OSP_DEFAULT_RETRY_LIMIT         2
54 #define OSP_DEFAULT_TIMEOUT                     500
55
56 static int loadPemCert(unsigned char *FileName, unsigned char *buffer, int *len);
57 static int loadPemPrivateKey(unsigned char *FileName, unsigned char *buffer, int *len);
58
59 AST_MUTEX_DEFINE_STATIC(osplock);
60
61 static int initialized = 0;
62 static int hardware = 0;
63
64 struct osp_provider {
65         char name[OSP_MAX];
66         char localpvtkey[OSP_MAX];
67         char localcert[OSP_MAX];
68         char cacerts[MAX_CERTS][OSP_MAX]; 
69         int cacount;
70         char servicepoints[MAX_SERVICEPOINTS][OSP_MAX];
71         char source[OSP_MAX];
72         int spcount;
73         int dead;
74         int maxconnections;
75         int retrydelay;
76         int retrylimit;
77         int timeout;
78         OSPTPROVHANDLE handle;
79         struct osp_provider *next;
80 };
81 static struct osp_provider *providers;
82
83 static int osp_build(struct ast_config *cfg, char *cat)
84 {
85         OSPTCERT TheAuthCert[MAX_CERTS];
86         unsigned char Reqbuf[4096],LocalBuf[4096],AuthBuf[MAX_CERTS][4096];
87         struct ast_variable *v;
88         struct osp_provider *osp;
89         int x,length,errorcode=0;
90         int mallocd=0,i;
91         char *cacerts[MAX_CERTS];
92         const char *servicepoints[MAX_SERVICEPOINTS];
93         OSPTPRIVATEKEY privatekey;
94         OSPTCERT localcert;
95         OSPTCERT *authCerts[MAX_CERTS];
96
97         
98         
99         ast_mutex_lock(&osplock);
100         osp = providers;
101         while(osp) {
102                 if (!strcasecmp(osp->name, cat))
103                         break;
104                 osp = osp->next;
105         }
106         ast_mutex_unlock(&osplock);
107         if (!osp) {
108                 mallocd = 1;
109                 osp = malloc(sizeof(struct osp_provider));
110                 if (!osp) {
111                         ast_log(LOG_WARNING, "Out of memory!\n");
112                         return -1;
113                 }
114                 memset(osp, 0, sizeof(struct osp_provider));
115                 osp->handle = -1;
116         }
117         strncpy(osp->name, cat, sizeof(osp->name) - 1);
118         snprintf(osp->localpvtkey, sizeof(osp->localpvtkey), AST_KEY_DIR "/%s-privatekey.pem", cat);
119         snprintf(osp->localcert, sizeof(osp->localpvtkey), AST_KEY_DIR "/%s-localcert.pem", cat);
120         osp->maxconnections=OSP_DEFAULT_MAX_CONNECTIONS;
121         osp->retrydelay = OSP_DEFAULT_RETRY_DELAY;
122         osp->retrylimit = OSP_DEFAULT_RETRY_LIMIT;
123         osp->timeout = OSP_DEFAULT_TIMEOUT;
124         osp->source[0] = '\0';
125         ast_log(LOG_DEBUG, "Building OSP Provider '%s'\n", cat);
126         v = ast_variable_browse(cfg, cat);
127         while(v) {
128                 if (!strcasecmp(v->name, "privatekey")) {
129                         if (v->value[0] == '/')
130                                 strncpy(osp->localpvtkey, v->value, sizeof(osp->localpvtkey) - 1);
131                         else
132                                 snprintf(osp->localpvtkey, sizeof(osp->localpvtkey), AST_KEY_DIR "/%s", v->value);
133                 } else if (!strcasecmp(v->name, "localcert")) {
134                         if (v->value[0] == '/')
135                                 strncpy(osp->localcert, v->value, sizeof(osp->localcert) - 1);
136                         else
137                                 snprintf(osp->localcert, sizeof(osp->localcert), AST_KEY_DIR "/%s", v->value);
138                 } else if (!strcasecmp(v->name, "cacert")) {
139                         if (osp->cacount < MAX_CERTS) {
140                                 if (v->value[0] == '/')
141                                         strncpy(osp->cacerts[osp->cacount], v->value, sizeof(osp->cacerts[0]) - 1);
142                                 else
143                                         snprintf(osp->cacerts[osp->cacount], sizeof(osp->cacerts[0]), AST_KEY_DIR "/%s", v->value);
144                                 osp->cacount++;
145                         } else
146                                 ast_log(LOG_WARNING, "Too many CA Certificates at line %d\n", v->lineno);
147                 } else if (!strcasecmp(v->name, "servicepoint")) {
148                         if (osp->spcount < MAX_SERVICEPOINTS) {
149                                 strncpy(osp->servicepoints[osp->spcount], v->value, sizeof(osp->servicepoints[0]) - 1);
150                                 osp->spcount++;
151                         } else
152                                 ast_log(LOG_WARNING, "Too many Service points at line %d\n", v->lineno);
153                 } else if (!strcasecmp(v->name, "maxconnections")) {
154                         if ((sscanf(v->value, "%d", &x) == 1) && (x > 0) && (x <= 1000)) {
155                                 osp->maxconnections = x;
156                         } else
157                                 ast_log(LOG_WARNING, "maxconnections should be an integer from 1 to 1000, not '%s' at line %d\n", v->value, v->lineno);
158                 } else if (!strcasecmp(v->name, "retrydelay")) {
159                         if ((sscanf(v->value, "%d", &x) == 1) && (x >= 0) && (x <= 10)) {
160                                 osp->retrydelay = x;
161                         } else
162                                 ast_log(LOG_WARNING, "retrydelay should be an integer from 0 to 10, not '%s' at line %d\n", v->value, v->lineno);
163                 } else if (!strcasecmp(v->name, "retrylimit")) {
164                         if ((sscanf(v->value, "%d", &x) == 1) && (x >= 0) && (x <= 100)) {
165                                 osp->retrylimit = x;
166                         } else
167                                 ast_log(LOG_WARNING, "retrylimit should be an integer from 0 to 100, not '%s' at line %d\n", v->value, v->lineno);
168                 } else if (!strcasecmp(v->name, "timeout")) {
169                         if ((sscanf(v->value, "%d", &x) == 1) && (x >= 200) && (x <= 10000)) {
170                                 osp->timeout = x;
171                         } else
172                                 ast_log(LOG_WARNING, "timeout should be an integer from 200 to 10000, not '%s' at line %d\n", v->value, v->lineno);
173                 } else if (!strcasecmp(v->name, "source")) {
174                         strncpy(osp->source, v->value, sizeof(osp->source) - 1);
175                 }
176                 v = v->next;
177         }
178         if (osp->cacount < 1) {
179                 snprintf(osp->cacerts[osp->cacount], sizeof(osp->cacerts[0]), AST_KEY_DIR "/%s-cacert.pem", cat);
180                 osp->cacount++;
181         }
182         for (x=0;x<osp->cacount;x++)
183                 cacerts[x] = osp->cacerts[x];
184         for (x=0;x<osp->spcount;x++)
185                 servicepoints[x] = osp->servicepoints[x];
186         
187         ast_mutex_lock(&osplock);
188         osp->dead = 0;
189         if (osp->handle > -1) {
190                 ast_log(LOG_DEBUG, "Deleting old handle for '%s'\n", osp->name);
191                 OSPPProviderDelete(osp->handle, 0);
192         }
193                 
194
195     length = 0;
196         ast_log(LOG_DEBUG, "Loading private key for '%s' (%s)\n", osp->name, osp->localpvtkey);
197     errorcode = loadPemPrivateKey(osp->localpvtkey,Reqbuf,&length);
198     if (errorcode == 0)
199     {
200         privatekey.PrivateKeyData = Reqbuf;
201         privatekey.PrivateKeyLength = length;
202     }
203     else
204     {
205          return -1;
206     }
207
208     length = 0;
209         ast_log(LOG_DEBUG, "Loading local cert for '%s' (%s)\n", osp->name, osp->localcert);
210     errorcode = loadPemCert(osp->localcert,LocalBuf,&length);
211     if (errorcode == 0)
212     {
213         localcert.CertData = LocalBuf;
214         localcert.CertDataLength = length;
215     }
216     else
217     {
218          return -1;
219     }
220
221     for (i=0;i<osp->cacount;i++)
222     {
223         length = 0;
224                 ast_log(LOG_DEBUG, "Loading CA cert %d for '%s' (%s)\n", i + 1, osp->name, osp->cacerts[i]);
225         errorcode = loadPemCert(osp->cacerts[i],AuthBuf[i],&length);
226         if (errorcode == 0)
227         {
228             TheAuthCert[i].CertData = AuthBuf[i];
229             TheAuthCert[i].CertDataLength = length;
230             authCerts[i] = &(TheAuthCert[i]);
231         }
232         else
233         {
234                         return -1;        
235                 }
236     }
237         
238         ast_log(LOG_DEBUG, "Creating provider handle for '%s'\n", osp->name);
239         
240         ast_log(LOG_DEBUG, "Service point '%s %d'\n", servicepoints[0], osp->spcount);
241         
242         if (OSPPProviderNew(osp->spcount, 
243                                             servicepoints, 
244                                            NULL, 
245                                            "localhost", 
246                                            &privatekey, 
247                                            &localcert, 
248                                            osp->cacount, 
249                                            (const OSPTCERT **)authCerts, 
250                                            1, 
251                                            300, 
252                                            osp->maxconnections, 
253                                            1, 
254                                            osp->retrydelay, 
255                                            osp->retrylimit, 
256                                            osp->timeout, 
257                                            "", 
258                                            "", 
259                                            &osp->handle)) {
260                 ast_log(LOG_WARNING, "Unable to initialize provider '%s'\n", cat);
261                 osp->dead = 1;
262         }
263         
264         if (mallocd) {
265                 osp->next = providers;
266                 providers = osp;
267         }
268         ast_mutex_unlock(&osplock);     
269         return 0;
270 }
271
272 static int show_osp(int fd, int argc, char *argv[])
273 {
274         struct osp_provider *osp;
275         char *search = NULL;
276         int x;
277         int found = 0;
278         if ((argc < 2) || (argc > 3))
279                 return RESULT_SHOWUSAGE;
280         if (argc > 2)
281                 search = argv[2];
282         if (!search) 
283                 ast_cli(fd, "OSP: %s %s\n", initialized ? "Initialized" : "Uninitialized", hardware ? "Accelerated" : "Normal");
284         
285         ast_mutex_lock(&osplock);
286         osp = providers;
287         while(osp) {
288                 if (!search || !strcasecmp(osp->name, search)) {
289                         if (found)
290                                 ast_cli(fd, "\n");
291                         ast_cli(fd, " == OSP Provider '%s' ==\n", osp->name);
292                         ast_cli(fd, "Local Private Key: %s\n", osp->localpvtkey);
293                         ast_cli(fd, "Local Certificate: %s\n", osp->localcert);
294                         for (x=0;x<osp->cacount;x++)
295                                 ast_cli(fd, "CA Certificate %d:  %s\n", x + 1, osp->cacerts[x]);
296                         for (x=0;x<osp->spcount;x++)
297                                 ast_cli(fd, "Service Point %d:   %s\n", x + 1, osp->servicepoints[x]);
298                         ast_cli(fd, "Max Connections:   %d\n", osp->maxconnections);
299                         ast_cli(fd, "Retry Delay:       %d seconds\n", osp->retrydelay);
300                         ast_cli(fd, "Retry Limit:       %d\n", osp->retrylimit);
301                         ast_cli(fd, "Timeout:           %d milliseconds\n", osp->timeout);
302                         ast_cli(fd, "Source:            %s\n", strlen(osp->source) ? osp->source : "<unspecified>");
303                         ast_cli(fd, "OSP Handle:        %d\n", osp->handle);
304                         found++;
305                 }
306                 osp = osp->next;
307         }
308         ast_mutex_unlock(&osplock);
309         if (!found) {
310                 if (search) 
311                         ast_cli(fd, "Unable to find OSP provider '%s'\n", search);
312                 else
313                         ast_cli(fd, "No OSP providers configured\n");
314         }
315         return RESULT_SUCCESS;
316 }
317
318
319 /*----------------------------------------------*
320  *               Loads the Certificate          *
321  *----------------------------------------------*/
322 static int loadPemCert(unsigned char *FileName, unsigned char *buffer, int *len)
323 {
324     int length = 0;
325     unsigned char *temp;
326     BIO *bioIn = NULL;
327     X509 *cert=NULL;
328     int retVal = OSPC_ERR_NO_ERROR;
329
330     temp = buffer;
331     bioIn = BIO_new_file((const char*)FileName,"r");
332     if (bioIn == NULL)
333     {
334                 ast_log(LOG_WARNING,"Failed to find the File - %s \n",FileName);
335                 return -1;
336     }
337     else
338     {
339         cert = PEM_read_bio_X509(bioIn,NULL,NULL,NULL);
340         if (cert == NULL)
341         {
342                         ast_log(LOG_WARNING,"Failed to parse the Certificate from the File - %s \n",FileName);
343                         return -1;
344         }
345         else
346         {
347             length = i2d_X509(cert,&temp);
348             if (cert == 0)
349             {
350                                 ast_log(LOG_WARNING,"Failed to parse the Certificate from the File - %s, Length=0 \n",FileName);
351                                 return -1;
352             }
353             else
354                         {
355                *len = length;
356             }
357         }
358     }
359
360     if (bioIn != NULL)
361     {
362         BIO_free(bioIn);
363     }
364
365     if (cert != NULL)
366     {
367         X509_free(cert);
368     }
369     return retVal;
370 }
371
372 /*----------------------------------------------*
373  *               Loads the Private Key          *
374  *----------------------------------------------*/
375 static int loadPemPrivateKey(unsigned char *FileName, unsigned char *buffer, int *len)
376 {
377     int length = 0;
378     unsigned char *temp;
379     BIO *bioIn = NULL;
380     RSA *pKey = NULL;
381     int retVal = OSPC_ERR_NO_ERROR;
382
383     temp = buffer;
384
385     bioIn = BIO_new_file((const char*)FileName,"r");
386     if (bioIn == NULL)
387     {
388                 ast_log(LOG_WARNING,"Failed to find the File - %s \n",FileName);
389                 return -1;
390     }
391     else
392     {
393         pKey = PEM_read_bio_RSAPrivateKey(bioIn,NULL,NULL,NULL);
394         if (pKey == NULL)
395         {
396                         ast_log(LOG_WARNING,"Failed to parse the Private Key from the File - %s \n",FileName);
397                         return -1;
398         }
399         else
400         {
401             length = i2d_RSAPrivateKey(pKey,&temp);
402             if (length == 0)
403             {
404                                 ast_log(LOG_WARNING,"Failed to parse the Private Key from the File - %s, Length=0 \n",FileName);
405                                 return -1;
406             }
407             else
408             {
409                 *len = length;
410             }
411         }
412     }
413     if (bioIn != NULL)
414     {
415         BIO_free(bioIn);
416     }
417
418     if (pKey != NULL)
419     {
420        RSA_free(pKey);
421     }
422     return retVal;
423 }
424
425 int ast_osp_validate(char *provider, char *token, int *handle, unsigned int *timelimit, char *callerid, struct in_addr addr, char *extension)
426 {
427         char tmp[256]="", *l, *n;
428         char iabuf[INET_ADDRSTRLEN];
429         char source[OSP_MAX] = ""; /* Same length as osp->source */
430         char *token2;
431         int tokenlen;
432         struct osp_provider *osp;
433         int res = 0;
434         unsigned int authorised, dummy;
435
436         if (!provider || !strlen(provider))
437                 provider = "default";
438
439         token2 = ast_strdupa(token);
440         if (!token2)
441                 return -1;
442         tokenlen = ast_base64decode(token2, token, strlen(token));
443         *handle = -1;
444         if (!callerid)
445                 callerid = "";
446         strncpy(tmp, callerid, sizeof(tmp) - 1);
447         ast_callerid_parse(tmp, &n, &l);
448         if (!l)
449                 l = "";
450         else {
451                 ast_shrink_phone_number(l);
452                 if (!ast_isphonenumber(l))
453                         l = "";
454         }
455         callerid = l;
456         ast_mutex_lock(&osplock);
457         ast_inet_ntoa(iabuf, sizeof(iabuf), addr);
458         osp = providers;
459         while(osp) {
460                 if (!strcasecmp(osp->name, provider)) {
461                         if (OSPPTransactionNew(osp->handle, handle)) {
462                                 ast_log(LOG_WARNING, "Unable to create OSP Transaction handle!\n");
463                         } else {
464                                 strncpy(source, osp->source, sizeof(source) - 1);
465                                 res = 1;
466                         }
467                         break;
468                 }
469                 osp = osp->next;
470         }
471         ast_mutex_unlock(&osplock);
472         if (res) {
473                 res = 0;
474                 dummy = 0;
475                 if (!OSPPTransactionValidateAuthorisation(*handle, iabuf, source, NULL, NULL, 
476                         callerid, OSPC_E164, extension, OSPC_E164, 0, "", tokenlen, token2, &authorised, timelimit, &dummy, NULL, TOKEN_ALGO_BOTH)) {
477                         if (authorised) {
478                                 ast_log(LOG_DEBUG, "Validated token for '%s' from '%s@%s'\n", extension, callerid, iabuf);
479                                 res = 1;
480                         }
481                 }
482         }
483         return res;     
484 }
485
486 int ast_osp_lookup(struct ast_channel *chan, char *provider, char *extension, char *callerid, struct ast_osp_result *result)
487 {
488         int cres;
489         int res = 0;
490         int counts;
491         int tokenlen;
492         unsigned int dummy=0;
493         unsigned int timelimit;
494         unsigned int callidlen;
495         struct osp_provider *osp;
496         char source[OSP_MAX] = ""; /* Same length as osp->source */
497         char uniqueid[32] = "";
498         char callednum[2048]="";
499         char callingnum[2048]="";
500         char destination[2048]="";
501         char token[2000];
502         char tmp[256]="", *l, *n;
503         OSPTCALLID *callid;
504         OSPE_DEST_PROT prot;
505
506         result->handle = -1;
507         result->numresults = 0;
508         result->tech[0] = '\0';
509         result->dest[0] = '\0';
510         result->token[0] = '\0';
511
512         if (!provider || !strlen(provider))
513                 provider = "default";
514
515         if (!callerid)
516                 callerid = "";
517         strncpy(tmp, callerid, sizeof(tmp) - 1);
518         ast_callerid_parse(tmp, &n, &l);
519         if (!l)
520                 l = "";
521         else {
522                 ast_shrink_phone_number(l);
523                 if (!ast_isphonenumber(l))
524                         l = "";
525         }
526         callerid = l;
527
528         if (chan) {
529                 strncpy(uniqueid, chan->uniqueid, sizeof(uniqueid) - 1);
530                 cres = ast_autoservice_start(chan);
531                 if (cres < 0)
532                         return cres;
533         }
534         ast_mutex_lock(&osplock);
535         osp = providers;
536         while(osp) {
537                 if (!strcasecmp(osp->name, provider)) {
538                         if (OSPPTransactionNew(osp->handle, &result->handle)) {
539                                 ast_log(LOG_WARNING, "Unable to create OSP Transaction handle!\n");
540                         } else {
541                                 strncpy(source, osp->source, sizeof(source) - 1);
542                                 res = 1;
543                         }
544                         break;
545                 }
546                 osp = osp->next;
547         }
548         ast_mutex_unlock(&osplock);
549         if (res) {
550                 res = 0;
551                 callid = OSPPCallIdNew(strlen(uniqueid), uniqueid);
552                 if (callid) {
553                         /* No more than 10 back */
554                         counts = 10;
555                         dummy = 0;
556                         callidlen = sizeof(uniqueid);
557                         if (!OSPPTransactionRequestAuthorisation(result->handle, source, "", 
558                                   callerid,OSPC_E164, extension, OSPC_E164, NULL, 1, &callid, NULL, &counts, &dummy, NULL)) {
559                                 if (counts) {
560                                         tokenlen = sizeof(token);
561                                         result->numresults = counts - 1;
562                                         if (!OSPPTransactionGetFirstDestination(result->handle, 0, NULL, NULL, &timelimit, &callidlen, uniqueid, 
563                                                 sizeof(callednum), callednum, sizeof(callingnum), callingnum, sizeof(destination), destination, 0, NULL, &tokenlen, token)) {
564                                                 ast_log(LOG_DEBUG, "Got destination '%s' and called: '%s' calling: '%s' for '%s' (provider '%s')\n",
565                                                         destination, callednum, callingnum, extension, provider);
566                                                 do {
567                                                         ast_base64encode(result->token, token, tokenlen, sizeof(result->token) - 1);
568                                                         if ((strlen(destination) > 2) && !OSPPTransactionGetDestProtocol(result->handle, &prot)) {
569                                                                 res = 1;
570                                                                 /* Strip leading and trailing brackets */
571                                                                 destination[strlen(destination) - 1] = '\0';
572                                                                 switch(prot) {
573                                                                 case OSPE_DEST_PROT_H323_SETUP:
574                                                                         strncpy(result->tech, "H323", sizeof(result->tech) - 1);
575                                                                         snprintf(result->dest, sizeof(result->dest), "%s@%s", callednum, destination + 1);
576                                                                         break;
577                                                                 case OSPE_DEST_PROT_SIP:
578                                                                         strncpy(result->tech, "SIP", sizeof(result->tech) - 1);
579                                                                         snprintf(result->dest, sizeof(result->dest), "%s@%s", callednum, destination + 1);
580                                                                         break;
581                                                                 case OSPE_DEST_PROT_IAX:
582                                                                         strncpy(result->tech, "IAX", sizeof(result->tech) - 1);
583                                                                         snprintf(result->dest, sizeof(result->dest), "%s@%s", callednum, destination + 1);
584                                                                         break;
585                                                                 default:
586                                                                         ast_log(LOG_DEBUG, "Unknown destination protocol '%d', skipping...\n", prot);
587                                                                         res = 0;
588                                                                 }
589                                                                 if (!res && result->numresults) {
590                                                                         result->numresults--;
591                                                                         if (OSPPTransactionGetNextDestination(result->handle, OSPC_FAIL_INCOMPATIBLE_DEST, 0, NULL, NULL, &timelimit, &callidlen, uniqueid, 
592                                                                                         sizeof(callednum), callednum, sizeof(callingnum), callingnum, sizeof(destination), destination, 0, NULL, &tokenlen, token)) {
593                                                                                         break;
594                                                                         }
595                                                                 }
596                                                         } else {
597                                                                 ast_log(LOG_DEBUG, "Missing destination protocol\n");
598                                                                 break;
599                                                         }
600                                                 } while(!res && result->numresults);
601                                         }
602                                 }
603                                 
604                         }
605                         OSPPCallIdDelete(&callid);
606                 }
607                 if (!res) {
608                         OSPPTransactionDelete(result->handle);
609                         result->handle = -1;
610                 }
611                 
612         }
613         if (!osp) 
614                 ast_log(LOG_NOTICE, "OSP Provider '%s' does not exist!\n", provider);
615         if (chan) {
616                 cres = ast_autoservice_stop(chan);
617                 if (cres < 0)
618                         return cres;
619         }
620         return res;
621 }
622
623 int ast_osp_next(struct ast_osp_result *result, int cause)
624 {
625         int res = 0;
626         int tokenlen;
627         unsigned int dummy=0;
628         unsigned int timelimit;
629         unsigned int callidlen;
630         char uniqueid[32] = "";
631         char callednum[2048]="";
632         char callingnum[2048]="";
633         char destination[2048]="";
634         char token[2000];
635         OSPE_DEST_PROT prot;
636
637         result->tech[0] = '\0';
638         result->dest[0] = '\0';
639         result->token[0] = '\0';
640
641         if (result->handle > -1) {
642                 dummy = 0;
643                 callidlen = sizeof(uniqueid);
644                 if (result->numresults) {
645                         tokenlen = sizeof(token);
646                         while(!res && result->numresults) {
647                                 result->numresults--;
648                                 if (!OSPPTransactionGetNextDestination(result->handle, OSPC_FAIL_INCOMPATIBLE_DEST, 0, NULL, NULL, &timelimit, &callidlen, uniqueid, 
649                                                                         sizeof(callednum), callednum, sizeof(callingnum), callingnum, sizeof(destination), destination, 0, NULL, &tokenlen, token)) {
650                                         ast_base64encode(result->token, token, tokenlen, sizeof(result->token) - 1);
651                                         if ((strlen(destination) > 2) && !OSPPTransactionGetDestProtocol(result->handle, &prot)) {
652                                                 res = 1;
653                                                 /* Strip leading and trailing brackets */
654                                                 destination[strlen(destination) - 1] = '\0';
655                                                 switch(prot) {
656                                                 case OSPE_DEST_PROT_H323_SETUP:
657                                                         strncpy(result->tech, "H323", sizeof(result->tech) - 1);
658                                                         snprintf(result->dest, sizeof(result->dest), "%s@%s", callednum, destination + 1);
659                                                         break;
660                                                 case OSPE_DEST_PROT_SIP:
661                                                         strncpy(result->tech, "SIP", sizeof(result->tech) - 1);
662                                                         snprintf(result->dest, sizeof(result->dest), "%s@%s", callednum, destination + 1);
663                                                         break;
664                                                 case OSPE_DEST_PROT_IAX:
665                                                         strncpy(result->tech, "IAX", sizeof(result->tech) - 1);
666                                                         snprintf(result->dest, sizeof(result->dest), "%s@%s", callednum, destination + 1);
667                                                         break;
668                                                 default:
669                                                         ast_log(LOG_DEBUG, "Unknown destination protocol '%d', skipping...\n", prot);
670                                                         res = 0;
671                                                 }
672                                         } else {
673                                                 ast_log(LOG_DEBUG, "Missing destination protocol\n");
674                                                 break;
675                                         }
676                                 }
677                         }
678                         
679                 }
680                 if (!res) {
681                         OSPPTransactionDelete(result->handle);
682                         result->handle = -1;
683                 }
684                 
685         }
686         return res;
687 }
688
689 static enum OSPEFAILREASON cause2reason(int cause)
690 {
691         switch(cause) {
692         case AST_CAUSE_BUSY:
693                 return OSPC_FAIL_USER_BUSY;
694         case AST_CAUSE_CONGESTION:
695                 return OSPC_FAIL_SWITCHING_EQUIPMENT_CONGESTION;
696         case AST_CAUSE_UNALLOCATED:
697                 return OSPC_FAIL_UNALLOC_NUMBER;
698         case AST_CAUSE_NOTDEFINED:
699                 return OSPC_FAIL_NORMAL_UNSPECIFIED;
700         case AST_CAUSE_NOANSWER:
701                 return OSPC_FAIL_NO_ANSWER_FROM_USER;
702         case AST_CAUSE_NORMAL:
703         default:
704                 return OSPC_FAIL_NORMAL_CALL_CLEARING;
705         }
706 }
707
708 int ast_osp_terminate(int handle, int cause, time_t start, time_t duration)
709 {
710         unsigned int dummy = 0;
711         int res = -1;
712         enum OSPEFAILREASON reason;
713
714         time_t endTime = 0;
715         time_t alertTime = 0;
716         time_t connectTime = 0;
717         unsigned isPddInfoPresent = 0;
718         unsigned pdd = 0;
719         unsigned releaseSource = 0;
720         unsigned char *confId = "";
721         
722         reason = cause2reason(cause);
723         if (OSPPTransactionRecordFailure(handle, reason))
724                 ast_log(LOG_WARNING, "Failed to record call termination for handle %d\n", handle);
725         else if (OSPPTransactionReportUsage(handle, duration, start,
726                                endTime,alertTime,connectTime,isPddInfoPresent,pdd,releaseSource,confId,
727                                0, 0, 0, 0, &dummy, NULL))
728                 ast_log(LOG_WARNING, "Failed to report duration for handle %d\n", handle);
729         else {
730                 ast_log(LOG_DEBUG, "Completed recording handle %d\n", handle);
731                 OSPPTransactionDelete(handle);
732                 res = 0;
733         }
734         return res;
735 }
736
737 static int config_load(void)
738 {
739         struct ast_config *cfg;
740         char *cat;
741         struct osp_provider *osp, *prev = NULL, *next;
742         ast_mutex_lock(&osplock);
743         osp = providers;
744         while(osp) {
745                 osp->dead = 1;
746                 osp = osp->next;
747         }
748         ast_mutex_unlock(&osplock);
749         cfg = ast_config_load("osp.conf");
750         if (cfg) {
751                 if (!initialized) {
752                         cat = ast_variable_retrieve(cfg, "general", "accelerate");
753                         if (cat && ast_true(cat))
754                                 if (OSPPInit(1)) {
755                                         ast_log(LOG_WARNING, "Failed to enable hardware accelleration, falling back to software mode\n");
756                                         OSPPInit(0);
757                                 } else
758                                         hardware = 1;
759                         else
760                                 OSPPInit(0);
761                         initialized = 1;
762                 }
763                 cat = ast_category_browse(cfg, NULL);
764                 while(cat) {
765                         if (strcasecmp(cat, "general"))
766                                 osp_build(cfg, cat);
767                         cat = ast_category_browse(cfg, cat);
768                 }
769                 ast_config_destroy(cfg);
770         } else
771                 ast_log(LOG_NOTICE, "No OSP configuration found.  OSP support disabled\n");
772         ast_mutex_lock(&osplock);
773         osp = providers;
774         while(osp) {
775                 next = osp->next;
776                 if (osp->dead) {
777                         if (prev)
778                                 prev->next = next;
779                         else
780                                 providers = next;
781                         /* XXX Cleanup OSP structure first XXX */
782                         free(osp);
783                 } else 
784                         prev = osp;
785                 osp = next;
786         }
787         ast_mutex_unlock(&osplock);
788         return 0;
789 }
790
791 static char show_osp_usage[] = 
792 "Usage: show osp\n"
793 "       Displays information on Open Settlement Protocol\n";
794
795 static struct ast_cli_entry cli_show_osp = 
796 { { "show", "osp", NULL }, show_osp, "Displays OSP information", show_osp_usage };
797
798 int reload(void)
799 {
800         config_load();
801         ast_log(LOG_NOTICE, "XXX Should reload OSP config XXX\n");
802         return 0;
803 }
804
805 int load_module(void)
806 {
807         config_load();
808         ast_cli_register(&cli_show_osp);
809         return 0;
810 }
811
812 int unload_module(void)
813 {
814         /* Can't unload this once we're loaded */
815         return -1;
816 }
817
818 char *description(void)
819 {
820         return "Open Settlement Protocol Support";
821 }
822
823 int usecount(void)
824 {
825         /* We should never be unloaded */
826         return 1;
827 }
828
829 char *key()
830 {
831         return ASTERISK_GPL_KEY;
832 }