Little fixups for busy
[asterisk/asterisk.git] / apps / app_dial.c
1 /*
2  * Asterisk -- A telephony toolkit for Linux.
3  *
4  * Trivial application to dial a channel and send an URL on answer
5  * 
6  * Copyright (C) 1999-2004, 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 <asterisk/lock.h>
15 #include <asterisk/file.h>
16 #include <asterisk/logger.h>
17 #include <asterisk/channel.h>
18 #include <asterisk/pbx.h>
19 #include <asterisk/options.h>
20 #include <asterisk/module.h>
21 #include <asterisk/translate.h>
22 #include <asterisk/say.h>
23 #include <asterisk/config.h>
24 #include <asterisk/features.h>
25 #include <asterisk/musiconhold.h>
26 #include <asterisk/callerid.h>
27 #include <asterisk/utils.h>
28 #include <asterisk/app.h>
29 #include <asterisk/causes.h>
30 #include <stdlib.h>
31 #include <errno.h>
32 #include <unistd.h>
33 #include <string.h>
34 #include <stdlib.h>
35 #include <stdio.h>
36 #include <sys/time.h>
37 #include <sys/signal.h>
38 #include <netinet/in.h>
39
40 static char *tdesc = "Dialing Application";
41
42 static char *app = "Dial";
43
44 static char *synopsis = "Place a call and connect to the current channel";
45
46 static char *descrip =
47 "  Dial(Technology/resource[&Technology2/resource2...][|timeout][|options][|URL]):\n"
48 "Requests one or more channels and places specified outgoing calls on them.\n"
49 "As soon as a channel answers, the Dial app will answer the originating\n"
50 "channel (if it needs to be answered) and will bridge a call with the channel\n"
51 "which first answered. All other calls placed by the Dial app will be hung up.\n"
52 "If a timeout is not specified, the Dial application will wait indefinitely\n"
53 "until either one of the called channels answers, the user hangs up, or all\n"
54 "channels return busy or error. In general, the dialer will return 0 if it\n"
55 "was unable to place the call, or the timeout expired. However, if all\n"
56 "channels were busy, and there exists an extension with priority n+101 (where\n"
57 "n is the priority of the dialer instance), then it will be the next\n"
58 "executed extension (this allows you to setup different behavior on busy from\n"
59 "no-answer).\n"
60 "  This application returns -1 if the originating channel hangs up, or if the\n"
61 "call is bridged and either of the parties in the bridge terminate the call.\n"
62 "The option string may contain zero or more of the following characters:\n"
63 "      't' -- allow the called user transfer the calling user by hitting #.\n"
64 "      'T' -- allow the calling user to transfer the call by hitting #.\n"
65 "      'f' -- Forces callerid to be set as the extension of the line \n"
66 "             making/redirecting the outgoing call. For example, some PSTNs\n"
67 "             don't allow callerids from other extensions then the ones\n"
68 "             that are assigned to you.\n"
69 "      'r' -- indicate ringing to the calling party, pass no audio until answered.\n"
70 "      'm' -- provide hold music to the calling party until answered.\n"
71 "      'M(x) -- Executes the macro (x) upon connect of the call\n"
72 "      'h' -- allow callee to hang up by hitting *.\n"
73 "      'H' -- allow caller to hang up by hitting *.\n"
74 "      'C' -- reset call detail record for this call.\n"
75 "      'P[(x)]' -- privacy mode, using 'x' as database if provided.\n"
76 "      'g' -- goes on in context if the destination channel hangs up\n"
77 "      'A(x)' -- play an announcement to the called party, using x as file\n"
78 "      'S(x)' -- hangup the call after x seconds AFTER called party picked up\n"        
79 "      'D([digits])'  -- Send DTMF digit string *after* called party has answered\n"
80 "             but before the bridge. (w=500ms sec pause)\n"
81 "      'L(x[:y][:z])' -- Limit the call to 'x' ms warning when 'y' ms are left\n"
82 "             repeated every 'z' ms) Only 'x' is required, 'y' and 'z' are optional.\n"
83 "             The following special variables are optional:\n"
84 "             * LIMIT_PLAYAUDIO_CALLER    yes|no (default yes)\n"
85 "                                         Play sounds to the caller.\n"
86 "             * LIMIT_PLAYAUDIO_CALLEE    yes|no\n"
87 "                                         Play sounds to the callee.\n"
88 "             * LIMIT_TIMEOUT_FILE        File to play when time is up.\n"
89 "             * LIMIT_CONNECT_FILE        File to play when call begins.\n"
90 "             * LIMIT_WARNING_FILE        File to play as warning if 'y' is defined.\n"
91 "                        'timeleft' is a special sound macro to auto-say the time \n"
92 "                        left and is the default.\n\n"
93 "  In addition to transferring the call, a call may be parked and then picked\n"
94 "up by another user.\n"
95 "  The optional URL will be sent to the called party if the channel supports it.\n"
96 "  If the OUTBOUND_GROUP variable is set, all peer channels created by this\n"
97 "  application will be put into that group (as in SetGroup).\n"
98 "  This application sets the following channel variables upon completion:\n"
99 "      DIALEDTIME    Time from dial to answer\n" 
100 "      ANSWEREDTIME  Time for actual call\n"
101 "      DIALSTATUS    The status of the call as a text string, one of\n"
102 "             CHANUNAVAIL | CONGESTION | NOANSWER | BUSY | ANSWER | CANCEL\n"
103 "";
104
105 /* We define a customer "local user" structure because we
106    use it not only for keeping track of what is in use but
107    also for keeping track of who we're dialing. */
108
109 struct localuser {
110         struct ast_channel *chan;
111         int stillgoing;
112         int allowredirect_in;
113         int allowredirect_out;
114         int ringbackonly;
115         int musiconhold;
116         int allowdisconnect_in;
117         int allowdisconnect_out;
118         int forcecallerid;
119         struct localuser *next;
120 };
121
122 LOCAL_USER_DECL;
123
124 static void hanguptree(struct localuser *outgoing, struct ast_channel *exception)
125 {
126         /* Hang up a tree of stuff */
127         struct localuser *oo;
128         while(outgoing) {
129                 /* Hangup any existing lines we have open */
130                 if (outgoing->chan && (outgoing->chan != exception))
131                         ast_hangup(outgoing->chan);
132                 oo = outgoing;
133                 outgoing=outgoing->next;
134                 free(oo);
135         }
136 }
137
138 #define AST_MAX_WATCHERS 256
139
140 #define HANDLE_CAUSE(blah, bleh) do { \
141         switch(cause) { \
142         case AST_CAUSE_BUSY: \
143                 if (bleh->cdr) \
144                         ast_cdr_busy(bleh->cdr); \
145                 numbusy++; \
146                 break; \
147         case AST_CAUSE_CONGESTION: \
148         case AST_CAUSE_UNREGISTERED: \
149                 if (bleh->cdr) \
150                         ast_cdr_busy(bleh->cdr); \
151                 numcongestion++; \
152                 break; \
153         default: \
154                 numnochan++; \
155                 break; \
156         } \
157 } while(0)
158
159
160 static struct ast_channel *wait_for_answer(struct ast_channel *in, struct localuser *outgoing, int *to, int *allowredir_in, int *allowredir_out, int *allowdisconnect_in, int *allowdisconnect_out, int *sentringing, char *status, size_t statussize, int busystart, int nochanstart, int congestionstart)
161 {
162         struct localuser *o;
163         int found;
164         int numlines;
165         int numbusy = busystart;
166         int numcongestion = congestionstart;
167         int numnochan = nochanstart;
168         int prestart = busystart + congestionstart + nochanstart;
169         int cause;
170         int orig = *to;
171         struct ast_frame *f;
172         struct ast_channel *peer = NULL;
173         struct ast_channel *watchers[AST_MAX_WATCHERS];
174         int pos;
175         int single;
176         struct ast_channel *winner;
177         
178         single = (outgoing && !outgoing->next && !outgoing->musiconhold && !outgoing->ringbackonly);
179         
180         if (single) {
181                 /* Turn off hold music, etc */
182                 ast_deactivate_generator(in);
183                 /* If we are calling a single channel, make them compatible for in-band tone purpose */
184                 ast_channel_make_compatible(outgoing->chan, in);
185         }
186         
187         
188         while(*to && !peer) {
189                 o = outgoing;
190                 found = -1;
191                 pos = 1;
192                 numlines = prestart;
193                 watchers[0] = in;
194                 while(o) {
195                         /* Keep track of important channels */
196                         if (o->stillgoing && o->chan) {
197                                 watchers[pos++] = o->chan;
198                                 found = 1;
199                         }
200                         o = o->next;
201                         numlines++;
202                 }
203                 if (found < 0) {
204                         if (numlines == (numbusy + numcongestion + numnochan)) {
205                                 if (option_verbose > 2)
206                                         ast_verbose( VERBOSE_PREFIX_2 "Everyone is busy/congested at this time\n");
207                                 if (numbusy)
208                                         strncpy(status, "BUSY", statussize - 1);
209                                 else if (numcongestion)
210                                         strncpy(status, "CONGESTION", statussize - 1);
211                                 else if (numnochan)
212                                         strncpy(status, "CHANUNAVAIL", statussize - 1);
213                                 /* See if there is a special busy message */
214                                 if (ast_exists_extension(in, in->context, in->exten, in->priority + 101, in->cid.cid_num)) 
215                                         in->priority+=100;
216                         } else {
217                                 if (option_verbose > 2)
218                                         ast_verbose( VERBOSE_PREFIX_2 "No one is available to answer at this time (%d, %d/%d/%d)\n", numlines, numbusy, numcongestion, numnochan);
219                         }
220                         *to = 0;
221                         return NULL;
222                 }
223                 winner = ast_waitfor_n(watchers, pos, to);
224                 o = outgoing;
225                 while(o) {
226                         if (o->stillgoing && o->chan && (o->chan->_state == AST_STATE_UP)) {
227                                 if (!peer) {
228                                         if (option_verbose > 2)
229                                                 ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name);
230                                         peer = o->chan;
231                                         *allowredir_in = o->allowredirect_in;
232                                         *allowredir_out = o->allowredirect_out;
233                                         *allowdisconnect_in = o->allowdisconnect_in;
234                                         *allowdisconnect_out = o->allowdisconnect_out;
235                                 }
236                         } else if (o->chan && (o->chan == winner)) {
237                                 if (!ast_strlen_zero(o->chan->call_forward)) {
238                                         char tmpchan[256]="";
239                                         char *stuff;
240                                         char *tech;
241                                         strncpy(tmpchan, o->chan->call_forward, sizeof(tmpchan) - 1);
242                                         if ((stuff = strchr(tmpchan, '/'))) {
243                                                 *stuff = '\0';
244                                                 stuff++;
245                                                 tech = tmpchan;
246                                         } else {
247                                                 snprintf(tmpchan, sizeof(tmpchan), "%s@%s", o->chan->call_forward, o->chan->context);
248                                                 stuff = tmpchan;
249                                                 tech = "Local";
250                                         }
251                                         /* Before processing channel, go ahead and check for forwarding */
252                                         if (option_verbose > 2)
253                                                 ast_verbose(VERBOSE_PREFIX_3 "Now forwarding %s to '%s/%s' (thanks to %s)\n", in->name, tech, stuff, o->chan->name);
254                                         /* Setup parameters */
255                                         o->chan = ast_request(tech, in->nativeformats, stuff, &cause);
256                                         if (!o->chan) {
257                                                 ast_log(LOG_NOTICE, "Unable to create local channel for call forward to '%s/%s' (cause = %d)\n", tech, stuff, cause);
258                                                 o->stillgoing = 0;
259                                                 HANDLE_CAUSE(cause, in);
260                                         } else {
261                                                 if (o->chan->cid.cid_num)
262                                                         free(o->chan->cid.cid_num);
263                                                 o->chan->cid.cid_num = NULL;
264                                                 if (o->chan->cid.cid_name)
265                                                         free(o->chan->cid.cid_name);
266                                                 o->chan->cid.cid_name = NULL;
267
268                                                 if (o->forcecallerid) {
269                                                         char *newcid = NULL;
270
271                                                         if (strlen(in->macroexten))
272                                                                 newcid = in->macroexten;
273                                                         else
274                                                                 newcid = in->exten;
275                                                         o->chan->cid.cid_num = strdup(newcid);
276                                                         strncpy(o->chan->accountcode, winner->accountcode, sizeof(o->chan->accountcode) - 1);
277                                                         o->chan->cdrflags = winner->cdrflags;
278                                                         if (!o->chan->cid.cid_num)
279                                                                 ast_log(LOG_WARNING, "Out of memory\n");
280                                                 } else {
281                                                         if (in->cid.cid_num) {
282                                                                 o->chan->cid.cid_num = strdup(in->cid.cid_num);
283                                                                 if (!o->chan->cid.cid_num)
284                                                                         ast_log(LOG_WARNING, "Out of memory\n");        
285                                                         }
286                                                         if (in->cid.cid_name) {
287                                                                 o->chan->cid.cid_name = strdup(in->cid.cid_name);
288                                                                 if (!o->chan->cid.cid_name)
289                                                                         ast_log(LOG_WARNING, "Out of memory\n");        
290                                                         }
291                                                         strncpy(o->chan->accountcode, in->accountcode, sizeof(o->chan->accountcode) - 1);
292                                                         o->chan->cdrflags = in->cdrflags;
293                                                 }
294
295                                                 if (in->cid.cid_ani) {
296                                                         if (o->chan->cid.cid_ani)
297                                                                 free(o->chan->cid.cid_ani);
298                                                         o->chan->cid.cid_ani = malloc(strlen(in->cid.cid_ani) + 1);
299                                                         if (o->chan->cid.cid_ani)
300                                                                 strncpy(o->chan->cid.cid_ani, in->cid.cid_ani, strlen(in->cid.cid_ani) + 1);
301                                                         else
302                                                                 ast_log(LOG_WARNING, "Out of memory\n");
303                                                 }
304                                                 if (o->chan->cid.cid_rdnis) 
305                                                         free(o->chan->cid.cid_rdnis);
306                                                 if (!ast_strlen_zero(in->macroexten))
307                                                         o->chan->cid.cid_rdnis = strdup(in->macroexten);
308                                                 else
309                                                         o->chan->cid.cid_rdnis = strdup(in->exten);
310                                                 if (ast_call(o->chan, tmpchan, 0)) {
311                                                         ast_log(LOG_NOTICE, "Failed to dial on local channel for call forward to '%s'\n", tmpchan);
312                                                         o->stillgoing = 0;
313                                                         ast_hangup(o->chan);
314                                                         o->chan = NULL;
315                                                         numnochan++;
316                                                 }
317                                         }
318                                         /* Hangup the original channel now, in case we needed it */
319                                         ast_hangup(winner);
320                                         continue;
321                                 }
322                                 f = ast_read(winner);
323                                 if (f) {
324                                         if (f->frametype == AST_FRAME_CONTROL) {
325                                                 switch(f->subclass) {
326                                             case AST_CONTROL_ANSWER:
327                                                         /* This is our guy if someone answered. */
328                                                         if (!peer) {
329                                                                 if (option_verbose > 2)
330                                                                         ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name);
331                                                                 peer = o->chan;
332                                                                 *allowredir_in = o->allowredirect_in;
333                                                                 *allowredir_out = o->allowredirect_out;
334                                                                 *allowdisconnect_in = o->allowdisconnect_in;
335                                                                 *allowdisconnect_out = o->allowdisconnect_out;
336                                                         }
337                                                         break;
338                                                 case AST_CONTROL_BUSY:
339                                                         if (option_verbose > 2)
340                                                                 ast_verbose( VERBOSE_PREFIX_3 "%s is busy\n", o->chan->name);
341                                                         in->hangupcause = o->chan->hangupcause;
342                                                         ast_hangup(o->chan);
343                                                         o->chan = NULL;
344                                                         o->stillgoing = 0;
345                                                         HANDLE_CAUSE(AST_CAUSE_BUSY, in);
346                                                         break;
347                                                 case AST_CONTROL_CONGESTION:
348                                                         if (option_verbose > 2)
349                                                                 ast_verbose( VERBOSE_PREFIX_3 "%s is circuit-busy\n", o->chan->name);
350                                                         in->hangupcause = o->chan->hangupcause;
351                                                         ast_hangup(o->chan);
352                                                         o->chan = NULL;
353                                                         o->stillgoing = 0;
354                                                         HANDLE_CAUSE(AST_CAUSE_CONGESTION, in);
355                                                         break;
356                                                 case AST_CONTROL_RINGING:
357                                                         if (option_verbose > 2)
358                                                                 ast_verbose( VERBOSE_PREFIX_3 "%s is ringing\n", o->chan->name);
359                                                         if (!(*sentringing) && !outgoing->musiconhold) {
360                                                                 ast_indicate(in, AST_CONTROL_RINGING);
361                                                                 (*sentringing)++;
362                                                         }
363                                                         break;
364                                                 case AST_CONTROL_PROGRESS:
365                                                         if (option_verbose > 2)
366                                                                 ast_verbose ( VERBOSE_PREFIX_3 "%s is making progress passing it to %s\n", o->chan->name,in->name);
367                                                         if (!outgoing->ringbackonly)
368                                                                 ast_indicate(in, AST_CONTROL_PROGRESS);
369                                                         break;
370                                                 case AST_CONTROL_OFFHOOK:
371                                                 case AST_CONTROL_FLASH:
372                                                         /* Ignore going off hook and flash */
373                                                         break;
374                                                 case -1:
375                                                         if (!outgoing->ringbackonly && !outgoing->musiconhold) {
376                                                                 if (option_verbose > 2)
377                                                                         ast_verbose( VERBOSE_PREFIX_3 "%s stopped sounds\n", o->chan->name);
378                                                                 ast_indicate(in, -1);
379                                                                 (*sentringing) = 0;
380                                                         }
381                                                         break;
382                                                 default:
383                                                         ast_log(LOG_DEBUG, "Dunno what to do with control type %d\n", f->subclass);
384                                                 }
385                                         } else if (single && (f->frametype == AST_FRAME_VOICE) && 
386                                                                 !(outgoing->ringbackonly || outgoing->musiconhold)) {
387                                                 if (ast_write(in, f)) 
388                                                         ast_log(LOG_WARNING, "Unable to forward frame\n");
389                                         } else if (single && (f->frametype == AST_FRAME_IMAGE) && 
390                                                                 !(outgoing->ringbackonly || outgoing->musiconhold)) {
391                                                 if (ast_write(in, f))
392                                                         ast_log(LOG_WARNING, "Unable to forward image\n");
393                                         }
394                                         ast_frfree(f);
395                                 } else {
396                                         in->hangupcause = o->chan->hangupcause;
397                                         ast_hangup(o->chan);
398                                         o->chan = NULL;
399                                         o->stillgoing = 0;
400                                 }
401                         }
402                         o = o->next;
403                 }
404                 if (winner == in) {
405                         f = ast_read(in);
406 #if 0
407                         if (f && (f->frametype != AST_FRAME_VOICE))
408                                         printf("Frame type: %d, %d\n", f->frametype, f->subclass);
409                         else if (!f || (f->frametype != AST_FRAME_VOICE))
410                                 printf("Hangup received on %s\n", in->name);
411 #endif
412                         if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP))) {
413                                 /* Got hung up */
414                                 *to=-1;
415                                 strncpy(status, "CANCEL", statussize - 1);
416                                 return NULL;
417                         }
418                         if (f && (f->frametype == AST_FRAME_DTMF) && *allowdisconnect_out &&
419                                 (f->subclass == '*')) {
420                             if (option_verbose > 3)
421                                 ast_verbose(VERBOSE_PREFIX_3 "User hit %c to disconnect call.\n", f->subclass);
422                                 *to=0;
423                                 strcpy(status, "CANCEL");
424                                 return NULL;
425                         }
426                         if (single && ((f->frametype == AST_FRAME_VOICE) || (f->frametype == AST_FRAME_DTMF)))  {
427                                 if (ast_write(outgoing->chan, f))
428                                         ast_log(LOG_WARNING, "Unable to forward voice\n");
429                                 ast_frfree(f);
430                         }
431                 }
432                 if (!*to && (option_verbose > 2))
433                         ast_verbose( VERBOSE_PREFIX_3 "Nobody picked up in %d ms\n", orig);
434         }
435
436         return peer;
437         
438 }
439
440 static int dial_exec(struct ast_channel *chan, void *data)
441 {
442         int res=-1;
443         struct localuser *u;
444         char *info, *peers, *timeout, *tech, *number, *rest, *cur;
445         char  privdb[256] = "", *s;
446         char  announcemsg[256] = "", *ann;
447         struct localuser *outgoing=NULL, *tmp;
448         struct ast_channel *peer;
449         int to;
450         int allowredir_in=0;
451         int allowredir_out=0;
452         int allowdisconnect_in=0;
453         int allowdisconnect_out=0;
454         int hasmacro = 0;
455         int privacy=0;
456         int announce=0;
457         int resetcdr=0;
458         int numbusy = 0;
459         int numcongestion = 0;
460         int numnochan = 0;
461         int cause;
462         char numsubst[AST_MAX_EXTENSION];
463         char restofit[AST_MAX_EXTENSION];
464         char *transfer = NULL;
465         char *newnum;
466         char *l;
467         char *url=NULL; /* JDG */
468         struct ast_var_t *current;
469         struct varshead *headp, *newheadp;
470         struct ast_var_t *newvar;
471         int go_on=0;
472         unsigned int calldurationlimit=0;
473         char *cdl;
474         time_t now;
475         struct ast_bridge_config config;
476         long timelimit = 0;
477         long play_warning = 0;
478         long warning_freq=0;
479         char *warning_sound=NULL;
480         char *end_sound=NULL;
481         char *start_sound=NULL;
482         char *limitptr;
483         char limitdata[256];
484         char *sdtmfptr;
485         char sdtmfdata[256] = "";
486         char *stack,*var;
487         char *mac = NULL, macroname[256] = "";
488         char status[256]="";
489         char toast[80];
490         int play_to_caller=0,play_to_callee=0;
491         int playargs=0, sentringing=0, moh=0;
492         char *varname;
493         int vartype;
494         char *outbound_group = NULL;
495
496         int digit = 0;
497         time_t start_time, answer_time, end_time;
498
499         if (!data) {
500                 ast_log(LOG_WARNING, "Dial requires an argument (technology1/number1&technology2/number2...|optional timeout|options)\n");
501                 return -1;
502         }
503
504         if (!(info = ast_strdupa(data))) {
505                 ast_log(LOG_WARNING, "Unable to dupe data :(\n");
506                 return -1;
507         }
508         LOCAL_USER_ADD(u);
509         
510         peers = info;
511         if (peers) {
512                 
513                 timeout = strchr(info, '|');
514                 if (timeout) {
515                         *timeout = '\0';
516                         timeout++;
517                         transfer = strchr(timeout, '|');
518                         if (transfer) {
519                                 *transfer = '\0';
520                                 transfer++;
521                                 /* JDG */
522                                 url = strchr(transfer, '|');
523                                 if (url) {
524                                         *url = '\0';
525                                         url++;
526                                         if (option_debug)
527                                                 ast_log(LOG_DEBUG, "DIAL WITH URL=%s_\n", url);
528                                 } else 
529                                         if (option_debug) {
530                                                 ast_log(LOG_DEBUG, "SIMPLE DIAL (NO URL)\n");
531                                         }
532                                 /* /JDG */
533                         }
534                 }
535         } else
536                 timeout = NULL;
537         if (!peers || ast_strlen_zero(peers)) {
538                 ast_log(LOG_WARNING, "Dial argument takes format (technology1/number1&technology2/number2...|optional timeout)\n");
539                 goto out;
540         }
541         
542
543         if (transfer) {
544
545                 /* Extract call duration limit */
546                 if ((cdl = strstr(transfer, "S("))) {
547                         calldurationlimit=atoi(cdl+2);
548                         if (option_verbose > 2)
549                                 ast_verbose(VERBOSE_PREFIX_3 "Setting call duration limit to %i seconds.\n",calldurationlimit);                 
550                 } 
551
552                 /* DTMF SCRIPT*/
553                 if ((sdtmfptr = strstr(transfer, "D("))) {
554                         strncpy(sdtmfdata, sdtmfptr + 2, sizeof(sdtmfdata) - 1);
555                         /* Overwrite with X's what was the sdtmf info */
556                         while (*sdtmfptr && (*sdtmfptr != ')')) 
557                                 *(sdtmfptr++) = 'X';
558                         if (*sdtmfptr)
559                                 *sdtmfptr = 'X';
560                         /* Now find the end  */
561                         sdtmfptr = strchr(sdtmfdata, ')');
562                         if (sdtmfptr)
563                                 *sdtmfptr = '\0';
564                         else 
565                                 ast_log(LOG_WARNING, "D( Data lacking trailing ')'\n");
566                 }
567                 
568                 /* XXX LIMIT SUPPORT */
569                 if ((limitptr = strstr(transfer, "L("))) {
570                         strncpy(limitdata, limitptr + 2, sizeof(limitdata) - 1);
571                         /* Overwrite with X's what was the limit info */
572                         while(*limitptr && (*limitptr != ')')) 
573                                 *(limitptr++) = 'X';
574                         if (*limitptr)
575                                 *limitptr = 'X';
576                         /* Now find the end */
577                         limitptr = strchr(limitdata, ')');
578                         if (limitptr)
579                                 *limitptr = '\0';
580                         else
581                                 ast_log(LOG_WARNING, "Limit Data lacking trailing ')'\n");
582
583                         var = pbx_builtin_getvar_helper(chan,"LIMIT_PLAYAUDIO_CALLER");
584                         play_to_caller = var ? ast_true(var) : 1;
585                   
586                         var = pbx_builtin_getvar_helper(chan,"LIMIT_PLAYAUDIO_CALLEE");
587                         play_to_callee = var ? ast_true(var) : 0;
588                   
589                         if (!play_to_caller && !play_to_callee)
590                                 play_to_caller=1;
591                   
592                         var = pbx_builtin_getvar_helper(chan,"LIMIT_WARNING_FILE");
593                         warning_sound = var ? var : "timeleft";
594
595                         var = pbx_builtin_getvar_helper(chan,"LIMIT_TIMEOUT_FILE");
596                         end_sound = var ? var : NULL;
597
598                         var = pbx_builtin_getvar_helper(chan,"LIMIT_CONNECT_FILE");
599                         start_sound = var ? var : NULL;
600
601                         var=stack=limitdata;
602
603                         var = strsep(&stack, ":");
604                         if (var) {
605                                 timelimit = atol(var);
606                                 playargs++;
607                                 var = strsep(&stack, ":");
608                                 if (var) {
609                                         play_warning = atol(var);
610                                         playargs++;
611                                         var = strsep(&stack, ":");
612                                         if(var) {
613                                                 warning_freq = atol(var);
614                                                 playargs++;
615                                         }
616                                 }
617                         }
618                   
619                         if (!timelimit) {
620                                 timelimit=play_to_caller=play_to_callee=play_warning=warning_freq=0;
621                                 warning_sound=NULL;
622                         }
623                         /* undo effect of S(x) in case they are both used */
624                         calldurationlimit=0; 
625                         /* more efficient do it like S(x) does since no advanced opts*/
626                         if (!play_warning && !start_sound && !end_sound && timelimit) { 
627                                 calldurationlimit=timelimit/1000;
628                                 timelimit=play_to_caller=play_to_callee=play_warning=warning_freq=0;
629                         } else if (option_verbose > 2) {
630                                 ast_verbose(VERBOSE_PREFIX_3"Limit Data:\n");
631                                 ast_verbose(VERBOSE_PREFIX_3"timelimit=%ld\n",timelimit);
632                                 ast_verbose(VERBOSE_PREFIX_3"play_warning=%ld\n",play_warning);
633                                 ast_verbose(VERBOSE_PREFIX_3"play_to_caller=%s\n",play_to_caller ? "yes" : "no");
634                                 ast_verbose(VERBOSE_PREFIX_3"play_to_callee=%s\n",play_to_callee ? "yes" : "no");
635                                 ast_verbose(VERBOSE_PREFIX_3"warning_freq=%ld\n",warning_freq);
636                                 ast_verbose(VERBOSE_PREFIX_3"start_sound=%s\n",start_sound ? start_sound : "UNDEF");
637                                 ast_verbose(VERBOSE_PREFIX_3"warning_sound=%s\n",warning_sound ? warning_sound : "UNDEF");
638                                 ast_verbose(VERBOSE_PREFIX_3"end_sound=%s\n",end_sound ? end_sound : "UNDEF");
639                         }
640                 }
641                 
642                 /* XXX ANNOUNCE SUPPORT */
643                 if ((ann = strstr(transfer, "A("))) {
644                         announce = 1;
645                         strncpy(announcemsg, ann + 2, sizeof(announcemsg) - 1);
646                         /* Overwrite with X's what was the announce info */
647                         while(*ann && (*ann != ')')) 
648                                 *(ann++) = 'X';
649                         if (*ann)
650                                 *ann = 'X';
651                         /* Now find the end of the privdb */
652                         ann = strchr(announcemsg, ')');
653                         if (ann)
654                                 *ann = '\0';
655                         else {
656                                 ast_log(LOG_WARNING, "Transfer with Announce spec lacking trailing ')'\n");
657                                 announce = 0;
658                         }
659                 }
660                 
661                 /* Get the macroname from the dial option string */
662                 if ((mac = strstr(transfer, "M("))) {
663                         hasmacro = 1;
664                         strncpy(macroname, mac + 2, sizeof(macroname) - 1);
665                         while (*mac && (*mac != ')'))
666                                 *(mac++) = 'X';
667                         if (*mac)
668                                 *mac = 'X';
669                         else {
670                                 ast_log(LOG_WARNING, "Could not find macro to which we should jump.\n");
671                                 hasmacro = 0;
672                         }
673                         mac = strchr(macroname, ')');
674                         if (mac)
675                                 *mac = '\0';
676                         else {
677                                 ast_log(LOG_WARNING, "Macro flag set without trailing ')'\n");
678                                 hasmacro = 0;
679                         }
680                 }
681                 /* Extract privacy info from transfer */
682                 if ((s = strstr(transfer, "P("))) {
683                         privacy = 1;
684                         strncpy(privdb, s + 2, sizeof(privdb) - 1);
685                         /* Overwrite with X's what was the privacy info */
686                         while(*s && (*s != ')')) 
687                                 *(s++) = 'X';
688                         if (*s)
689                                 *s = 'X';
690                         /* Now find the end of the privdb */
691                         s = strchr(privdb, ')');
692                         if (s)
693                                 *s = '\0';
694                         else {
695                                 ast_log(LOG_WARNING, "Transfer with privacy lacking trailing ')'\n");
696                                 privacy = 0;
697                         }
698                 } else if (strchr(transfer, 'P')) {
699                         /* No specified privdb */
700                         privacy = 1;
701                 } else if (strchr(transfer, 'C')) {
702                         resetcdr = 1;
703                 }
704         }
705         if (resetcdr && chan->cdr)
706                 ast_cdr_reset(chan->cdr, 0);
707         if (ast_strlen_zero(privdb) && privacy) {
708                 /* If privdb is not specified and we are using privacy, copy from extension */
709                 strncpy(privdb, chan->exten, sizeof(privdb) - 1);
710         }
711         if (privacy) {
712                 l = chan->cid.cid_num;
713                 if (!l)
714                         l = "";
715                 ast_log(LOG_NOTICE, "Privacy DB is '%s', privacy is %d, clid is '%s'\n", privdb, privacy, l);
716         }
717
718         /* If a channel group has been specified, get it for use when we create peer channels */
719         outbound_group = pbx_builtin_getvar_helper(chan, "OUTBOUND_GROUP");
720
721         cur = peers;
722         do {
723                 /* Remember where to start next time */
724                 rest = strchr(cur, '&');
725                 if (rest) {
726                         *rest = 0;
727                         rest++;
728                 }
729                 /* Get a technology/[device:]number pair */
730                 tech = cur;
731                 number = strchr(tech, '/');
732                 if (!number) {
733                         ast_log(LOG_WARNING, "Dial argument takes format (technology1/[device:]number1&technology2/[device:]number2...|optional timeout)\n");
734                         goto out;
735                 }
736                 *number = '\0';
737                 number++;
738                 tmp = malloc(sizeof(struct localuser));
739                 if (!tmp) {
740                         ast_log(LOG_WARNING, "Out of memory\n");
741                         goto out;
742                 }
743                 memset(tmp, 0, sizeof(struct localuser));
744                 if (transfer) {
745                         if (strchr(transfer, 't'))
746                                 tmp->allowredirect_in = 1;
747                         else    tmp->allowredirect_in = 0;
748                         if (strchr(transfer, 'T'))
749                                 tmp->allowredirect_out = 1;
750                         else    tmp->allowredirect_out = 0;
751                         if (strchr(transfer, 'r'))
752                                 tmp->ringbackonly = 1;
753                         else    tmp->ringbackonly = 0;
754                         if (strchr(transfer, 'm'))
755                                 tmp->musiconhold = 1;
756                         else    tmp->musiconhold = 0;
757                         if (strchr(transfer, 'H'))
758                                 allowdisconnect_out = tmp->allowdisconnect_out = 1;
759                         else    allowdisconnect_out = tmp->allowdisconnect_out = 0;
760                         if(strchr(transfer, 'h'))
761                                 allowdisconnect_in = tmp->allowdisconnect_in = 1;
762                         else    allowdisconnect_in = tmp->allowdisconnect_in = 0;
763                         if(strchr(transfer, 'g'))
764                                 go_on=1;
765                         if (strchr(transfer, 'f'))
766                                 tmp->forcecallerid = 1;
767                         else    tmp->forcecallerid = 0;
768                 }
769                 strncpy(numsubst, number, sizeof(numsubst)-1);
770                 /* If we're dialing by extension, look at the extension to know what to dial */
771                 if ((newnum = strstr(numsubst, "BYEXTENSION"))) {
772                         strncpy(restofit, newnum + strlen("BYEXTENSION"), sizeof(restofit)-1);
773                         snprintf(newnum, sizeof(numsubst) - (newnum - numsubst), "%s%s", chan->exten,restofit);
774                         if (option_debug)
775                                 ast_log(LOG_DEBUG, "Dialing by extension %s\n", numsubst);
776                 }
777                 /* Request the peer */
778                 tmp->chan = ast_request(tech, chan->nativeformats, numsubst, &cause);
779                 if (!tmp->chan) {
780                         /* If we can't, just go on to the next call */
781                         ast_log(LOG_NOTICE, "Unable to create channel of type '%s' (cause %d)\n", tech, cause);
782                         HANDLE_CAUSE(cause, chan);
783                         cur = rest;
784                         continue;
785                 }
786                 if (!ast_strlen_zero(tmp->chan->call_forward)) {
787                         char tmpchan[256]="";
788                         char *stuff;
789                         char *tech;
790                         strncpy(tmpchan, tmp->chan->call_forward, sizeof(tmpchan) - 1);
791                         if ((stuff = strchr(tmpchan, '/'))) {
792                                 *stuff = '\0';
793                                 stuff++;
794                                 tech = tmpchan;
795                         } else {
796                                 snprintf(tmpchan, sizeof(tmpchan), "%s@%s", tmp->chan->call_forward, tmp->chan->context);
797                                 stuff = tmpchan;
798                                 tech = "Local";
799                         }
800                         /* Before processing channel, go ahead and check for forwarding */
801                         if (option_verbose > 2)
802                                 ast_verbose(VERBOSE_PREFIX_3 "Forwarding %s to '%s/%s' (thanks to %s)\n", chan->name, tech, stuff, tmp->chan->name);
803                         /* Setup parameters */
804                         ast_hangup(tmp->chan);
805                         tmp->chan = ast_request(tech, chan->nativeformats, stuff, &cause);
806                         if (!tmp->chan) {
807                                 ast_log(LOG_NOTICE, "Unable to create local channel for call forward to '%s/%s' (cause %d)\n", tech, stuff, cause);
808                                 HANDLE_CAUSE(cause, chan);
809                                 cur = rest;
810                                 continue;
811                         }
812                 }
813
814                 /* Contitionally copy channel variables to the newly created channel */
815                 headp = &chan->varshead;
816                 AST_LIST_TRAVERSE(headp, current, entries) {
817                         varname = ast_var_full_name(current);
818                         vartype = 0;
819                         if (varname) {
820                                 if (varname[0] == '_') {
821                                         vartype = 1;
822                                         if (varname[1] == '_')
823                                                 vartype = 2;
824                                 }
825                         }
826                         if (vartype == 1) {
827                                 newvar = ast_var_assign((char*)&(varname[1]), 
828                                                                                                 ast_var_value(current));
829                                 newheadp = &tmp->chan->varshead;
830                                 AST_LIST_INSERT_HEAD(newheadp, newvar, entries);
831                                 if (option_debug)
832                                         ast_log(LOG_DEBUG, "Copying soft-transferable variable %s.\n", 
833                                                                                                 ast_var_name(newvar));
834                         } else if (vartype == 2) {
835                                 newvar = ast_var_assign(ast_var_full_name(current), 
836                                                                                                 ast_var_value(current));
837                                 newheadp = &tmp->chan->varshead;
838                                 AST_LIST_INSERT_HEAD(newheadp, newvar, entries);
839                                 if (option_debug)
840                                         ast_log(LOG_DEBUG, "Copying hard-transferable variable %s.\n", 
841                                                                                                 ast_var_name(newvar));
842                         } else {
843                                 if (option_debug)
844                                         ast_log(LOG_DEBUG, "Not copying variable %s.\n", 
845                                                                                                 ast_var_name(current));
846                         }
847                 }
848
849                 tmp->chan->appl = "AppDial";
850                 tmp->chan->data = "(Outgoing Line)";
851                 tmp->chan->whentohangup = 0;
852                 if (tmp->chan->cid.cid_num)
853                         free(tmp->chan->cid.cid_num);
854                 tmp->chan->cid.cid_num = NULL;
855                 if (tmp->chan->cid.cid_name)
856                         free(tmp->chan->cid.cid_name);
857                 tmp->chan->cid.cid_name = NULL;
858                 if (tmp->chan->cid.cid_ani)
859                         free(tmp->chan->cid.cid_ani);
860                 tmp->chan->cid.cid_ani = NULL;
861
862                 if (chan->cid.cid_num) 
863                         tmp->chan->cid.cid_num = strdup(chan->cid.cid_num);
864                 if (chan->cid.cid_name) 
865                         tmp->chan->cid.cid_name = strdup(chan->cid.cid_name);
866                 if (chan->cid.cid_ani) 
867                         tmp->chan->cid.cid_ani = strdup(chan->cid.cid_ani);
868                 
869                 /* Copy language from incoming to outgoing */
870                 strncpy(tmp->chan->language, chan->language, sizeof(tmp->chan->language) - 1);
871                 strncpy(tmp->chan->accountcode, chan->accountcode, sizeof(tmp->chan->accountcode) - 1);
872                 tmp->chan->cdrflags = chan->cdrflags;
873                 if (ast_strlen_zero(tmp->chan->musicclass))
874                         strncpy(tmp->chan->musicclass, chan->musicclass, sizeof(tmp->chan->musicclass) - 1);
875                 if (chan->cid.cid_rdnis)
876                         tmp->chan->cid.cid_rdnis = strdup(chan->cid.cid_rdnis);
877                 /* Pass callingpres setting */
878                 tmp->chan->cid.cid_pres = chan->cid.cid_pres;
879                 /* Pass type of number */
880                 tmp->chan->cid.cid_ton = chan->cid.cid_ton;
881                 /* Pass type of tns */
882                 tmp->chan->cid.cid_tns = chan->cid.cid_tns;
883                 /* Presense of ADSI CPE on outgoing channel follows ours */
884                 tmp->chan->adsicpe = chan->adsicpe;
885                 /* pass the digital flag */
886                 ast_dup_flag(tmp->chan, chan, AST_FLAG_DIGITAL);
887
888                 /* If we have an outbound group, set this peer channel to it */
889                 if (outbound_group)
890                         ast_app_group_set_channel(tmp->chan, outbound_group);
891
892                 /* Place the call, but don't wait on the answer */
893                 res = ast_call(tmp->chan, numsubst, 0);
894
895                 /* Save the info in cdr's that we called them */
896                 if (chan->cdr)
897                         ast_cdr_setdestchan(chan->cdr, tmp->chan->name);
898
899                 /* check the results of ast_call */
900                 if (res) {
901                         /* Again, keep going even if there's an error */
902                         if (option_debug)
903                                 ast_log(LOG_DEBUG, "ast call on peer returned %d\n", res);
904                         else if (option_verbose > 2)
905                                 ast_verbose(VERBOSE_PREFIX_3 "Couldn't call %s\n", numsubst);
906                         ast_hangup(tmp->chan);
907                         tmp->chan = NULL;
908                         cur = rest;
909                         continue;
910                 } else
911                         if (option_verbose > 2)
912                                 ast_verbose(VERBOSE_PREFIX_3 "Called %s\n", numsubst);
913                 /* Put them in the list of outgoing thingies...  We're ready now. 
914                    XXX If we're forcibly removed, these outgoing calls won't get
915                    hung up XXX */
916                 tmp->stillgoing = -1;
917                 tmp->next = outgoing;
918                 outgoing = tmp;
919                 /* If this line is up, don't try anybody else */
920                 if (outgoing->chan->_state == AST_STATE_UP)
921                         break;
922                 cur = rest;
923         } while(cur);
924         
925         if (timeout && !ast_strlen_zero(timeout)) {
926                 to = atoi(timeout);
927                 if (to > 0)
928                         to *= 1000;
929                 else
930                         ast_log(LOG_WARNING, "Invalid timeout specified: '%s'\n", timeout);
931         } else
932                 to = -1;
933
934         if (outgoing) {
935                 /* Our status will at least be NOANSWER */
936                 strncpy(status, "NOANSWER", sizeof(status) - 1);
937                 if (outgoing->musiconhold) {
938                         moh=1;
939                         ast_moh_start(chan, NULL);
940                 } else if (outgoing->ringbackonly) {
941                         ast_indicate(chan, AST_CONTROL_RINGING);
942                         sentringing++;
943                 }
944         } else
945                 strncpy(status, "CHANUNAVAIL", sizeof(status) - 1);
946
947         time(&start_time);
948         peer = wait_for_answer(chan, outgoing, &to, &allowredir_in, &allowredir_out, &allowdisconnect_in, &allowdisconnect_out, &sentringing, status, sizeof(status), numbusy, numnochan, numcongestion);
949
950         if (!peer) {
951                 if (to) 
952                         /* Musta gotten hung up */
953                         res = -1;
954                  else 
955                         /* Nobody answered, next please? */
956                         res=0;
957                 
958                 goto out;
959         }
960         if (peer) {
961                 time(&answer_time);
962 #ifdef OSP_SUPPORT
963                 /* Once call is answered, ditch the OSP Handle */
964                 pbx_builtin_setvar_helper(chan, "OSPHANDLE", "");
965 #endif          
966                 strncpy(status, "ANSWER", sizeof(status) - 1);
967                 /* Ah ha!  Someone answered within the desired timeframe.  Of course after this
968                    we will always return with -1 so that it is hung up properly after the 
969                    conversation.  */
970                 hanguptree(outgoing, peer);
971                 outgoing = NULL;
972                 /* If appropriate, log that we have a destination channel */
973                 if (chan->cdr)
974                         ast_cdr_setdestchan(chan->cdr, peer->name);
975                 if (peer->name)
976                         pbx_builtin_setvar_helper(chan, "DIALEDPEERNAME", peer->name);
977                 if (numsubst)
978                         pbx_builtin_setvar_helper(chan, "DIALEDPEERNUMBER", numsubst);
979                 /* JDG: sendurl */
980                 if( url && !ast_strlen_zero(url) && ast_channel_supports_html(peer) ) {
981                         ast_log(LOG_DEBUG, "app_dial: sendurl=%s.\n", url);
982                         ast_channel_sendurl( peer, url );
983                 } /* /JDG */
984                 if (announce && announcemsg) {
985                         // Start autoservice on the other chan
986                         res = ast_autoservice_start(chan);
987                         // Now Stream the File
988                         if (!res)
989                                 res = ast_streamfile(peer,announcemsg,peer->language);
990                         if (!res) {
991                                 digit = ast_waitstream(peer, AST_DIGIT_ANY); 
992                         }
993                         // Ok, done. stop autoservice
994                         res = ast_autoservice_stop(chan);
995                         if (digit > 0 && !res)
996                                 res = ast_senddigit(chan, digit); 
997                         else
998                                 res = digit;
999
1000                 } else
1001                         res = 0;
1002
1003                 if (hasmacro && macroname) {
1004                         void *app = NULL;
1005
1006                         res = ast_autoservice_start(chan);
1007                         if (res) {
1008                                 ast_log(LOG_ERROR, "Unable to start autoservice on calling channel\n");
1009                                 res = -1;
1010                         }
1011
1012                         app = pbx_findapp("Macro");
1013
1014                         if (app && !res) {
1015                                 res = pbx_exec(peer, app, macroname, 1);
1016                                 ast_log(LOG_DEBUG, "Macro exited with status %d\n", res);
1017                                 res = 0;
1018                         } else {
1019                                 ast_log(LOG_ERROR, "Could not find application Macro\n");
1020                                 res = -1;
1021                         }
1022
1023                         if (ast_autoservice_stop(chan) < 0) {
1024                                 ast_log(LOG_ERROR, "Could not stop autoservice on calling channel\n");
1025                                 res = -1;
1026                         }
1027                 }
1028
1029                 if (!res) {
1030                         if (calldurationlimit > 0) {
1031                                 time(&now);
1032                                 chan->whentohangup = now + calldurationlimit;
1033                         }
1034                         if (!ast_strlen_zero(sdtmfdata)) 
1035                                 res = ast_dtmf_stream(peer,chan,sdtmfdata,250);
1036                 }
1037                 
1038                 if (!res) {
1039                         memset(&config,0,sizeof(struct ast_bridge_config));
1040                         config.play_to_caller=play_to_caller;
1041                         config.play_to_callee=play_to_callee;
1042                         config.allowredirect_in = allowredir_in;
1043                         config.allowredirect_out = allowredir_out;
1044                         config.allowdisconnect_in = allowdisconnect_in;
1045                         config.allowdisconnect_out = allowdisconnect_out;
1046                         config.timelimit = timelimit;
1047                         config.play_warning = play_warning;
1048                         config.warning_freq = warning_freq;
1049                         config.warning_sound = warning_sound;
1050                         config.end_sound = end_sound;
1051                         config.start_sound = start_sound;
1052                         if (moh) {
1053                                 moh = 0;
1054                                 ast_moh_stop(chan);
1055                         } else if (sentringing) {
1056                                 sentringing = 0;
1057                                 ast_indicate(chan, -1);
1058                         }
1059                         /* Be sure no generators are left on it */
1060                         ast_deactivate_generator(chan);
1061                         /* Make sure channels are compatible */
1062                         res = ast_channel_make_compatible(chan, peer);
1063                         if (res < 0) {
1064                                 ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", chan->name, peer->name);
1065                                 ast_hangup(peer);
1066                                 return -1;
1067                         }
1068                         res = ast_bridge_call(chan,peer,&config);
1069                         time(&end_time);
1070                         snprintf(toast, sizeof(toast), "%ld", (long)(end_time - start_time));
1071                         pbx_builtin_setvar_helper(chan, "DIALEDTIME", toast);
1072                         snprintf(toast, sizeof(toast), "%ld", (long)(end_time - answer_time));
1073                         pbx_builtin_setvar_helper(chan, "ANSWEREDTIME", toast);
1074                         
1075                 } else 
1076                         res = -1;
1077                 
1078                 if (res != AST_PBX_NO_HANGUP_PEER) {
1079                         if (!chan->_softhangup)
1080                                 chan->hangupcause = peer->hangupcause;
1081                         ast_hangup(peer);
1082                 }
1083         }       
1084 out:
1085         if (moh) {
1086                 moh = 0;
1087                 ast_moh_stop(chan);
1088         } else if (sentringing) {
1089                 sentringing = 0;
1090                 ast_indicate(chan, -1);
1091         }
1092         hanguptree(outgoing, NULL);
1093         pbx_builtin_setvar_helper(chan, "DIALSTATUS", status);
1094         ast_log(LOG_DEBUG, "Exiting with DIALSTATUS=%s.\n", status);
1095         
1096         LOCAL_USER_REMOVE(u);
1097         
1098         if((go_on>0) && (!chan->_softhangup))
1099             res=0;
1100             
1101         return res;
1102 }
1103
1104 int unload_module(void)
1105 {
1106         STANDARD_HANGUP_LOCALUSERS;
1107         return ast_unregister_application(app);
1108 }
1109
1110 int load_module(void)
1111 {
1112         int res;
1113         res = ast_register_application(app, dial_exec, synopsis, descrip);
1114         return res;
1115 }
1116
1117 char *description(void)
1118 {
1119         return tdesc;
1120 }
1121
1122 int usecount(void)
1123 {
1124         int res;
1125         STANDARD_USECOUNT(res);
1126         return res;
1127 }
1128
1129 char *key()
1130 {
1131         return ASTERISK_GPL_KEY;
1132 }