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