add missing newlines, fix misspelling of nonexistent (bug #4027)
[asterisk/asterisk.git] / res / res_features.c
1 /*
2  * Asterisk -- A telephony toolkit for Linux.
3  *
4  * Routines implementing call parking
5  * 
6  * Copyright (C) 1999 - 2005, Digium, Inc.
7  *
8  * Mark Spencer <markster@digium.com>
9  *
10  * This program is free software, distributed under the terms of
11  * the GNU General Public License
12  */
13
14 #include <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/app.h>
23 #include <asterisk/say.h>
24 #include <asterisk/features.h>
25 #include <asterisk/musiconhold.h>
26 #include <asterisk/config.h>
27 #include <asterisk/cli.h>
28 #include <asterisk/manager.h>
29 #include <asterisk/utils.h>
30 #include <asterisk/adsi.h>
31 #include <pthread.h>
32 #include <stdlib.h>
33 #include <errno.h>
34 #include <unistd.h>
35 #include <string.h>
36 #include <stdlib.h>
37 #include <stdio.h>
38 #include <sys/time.h>
39 #include <sys/signal.h>
40 #include <netinet/in.h>
41
42 #define DEFAULT_PARK_TIME 45000
43 #define DEFAULT_TRANSFER_DIGIT_TIMEOUT 3000
44 #define DEFAULT_FEATURE_DIGIT_TIMEOUT 500
45
46 static char *parkedcall = "ParkedCall";
47
48 /* No more than 45 seconds parked before you do something with them */
49 static int parkingtime = DEFAULT_PARK_TIME;
50
51 /* Context for which parking is made accessible */
52 static char parking_con[AST_MAX_EXTENSION] = "parkedcalls";
53
54 /* Context for dialback for parking (KLUDGE) */
55 static char parking_con_dial[AST_MAX_EXTENSION] = "park-dial";
56
57 /* Extension you type to park the call */
58 static char parking_ext[AST_MAX_EXTENSION] = "700";
59
60 static char pickup_ext[AST_MAX_EXTENSION] = "*8";
61
62 /* Default sounds */
63 static char courtesytone[256] = "";
64 static char xfersound[256] = "beep";
65 static char xferfailsound[256] = "pbx-invalid";
66
67 /* First available extension for parking */
68 static int parking_start = 701;
69
70 /* Last available extension for parking */
71 static int parking_stop = 750;
72
73 static int adsipark = 0;
74
75 static int transferdigittimeout = DEFAULT_TRANSFER_DIGIT_TIMEOUT;
76 static int featuredigittimeout = DEFAULT_FEATURE_DIGIT_TIMEOUT;
77
78 /* Default courtesy tone played when party joins conference */
79
80 /* Registrar for operations */
81 static char *registrar = "res_features";
82
83 static char *synopsis = "Answer a parked call";
84
85 static char *descrip = "ParkedCall(exten):"
86 "Used to connect to a parked call.  This application is always\n"
87 "registered internally and does not need to be explicitly added\n"
88 "into the dialplan, although you should include the 'parkedcalls'\n"
89 "context.\n";
90
91 static char *parkcall = "Park";
92
93 static char *synopsis2 = "Park yourself";
94
95 static char *descrip2 = "Park(exten):"
96 "Used to park yourself (typically in combination with a supervised\n"
97 "transfer to know the parking space). This application is always\n"
98 "registered internally and does not need to be explicitly added\n"
99 "into the dialplan, although you should include the 'parkedcalls'\n"
100 "context.\n";
101
102 static struct ast_app *monitor_app=NULL;
103 static int monitor_ok=1;
104
105 struct parkeduser {
106         struct ast_channel *chan;
107         struct timeval start;
108         int parkingnum;
109         /* Where to go if our parking time expires */
110         char context[AST_MAX_EXTENSION];
111         char exten[AST_MAX_EXTENSION];
112         int priority;
113         int parkingtime;
114         int notquiteyet;
115         char peername[1024];
116         unsigned char moh_trys;
117         struct parkeduser *next;
118 };
119
120 static struct parkeduser *parkinglot;
121
122 AST_MUTEX_DEFINE_STATIC(parking_lock);
123
124 static pthread_t parking_thread;
125
126 STANDARD_LOCAL_USER;
127
128 LOCAL_USER_DECL;
129
130 char *ast_parking_ext(void)
131 {
132         return parking_ext;
133 }
134
135 char *ast_pickup_ext(void)
136 {
137         return pickup_ext;
138 }
139
140 struct ast_bridge_thread_obj 
141 {
142         struct ast_bridge_config bconfig;
143         struct ast_channel *chan;
144         struct ast_channel *peer;
145 };
146
147 static void *ast_bridge_call_thread(void *data) 
148 {
149         struct ast_bridge_thread_obj *tobj = data;
150         tobj->chan->appl = "Transferred Call";
151         tobj->chan->data = tobj->peer->name;
152         tobj->peer->appl = "Transferred Call";
153         tobj->peer->data = tobj->chan->name;
154         if (tobj->chan->cdr) {
155                 ast_cdr_reset(tobj->chan->cdr,0);
156                 ast_cdr_setdestchan(tobj->chan->cdr, tobj->peer->name);
157         }
158         if (tobj->peer->cdr) {
159                 ast_cdr_reset(tobj->peer->cdr,0);
160                 ast_cdr_setdestchan(tobj->peer->cdr, tobj->chan->name);
161         }
162
163
164         ast_bridge_call(tobj->peer, tobj->chan, &tobj->bconfig);
165         ast_hangup(tobj->chan);
166         ast_hangup(tobj->peer);
167         tobj->chan = tobj->peer = NULL;
168         free(tobj);
169         tobj=NULL;
170         return NULL;
171 }
172
173 static void ast_bridge_call_thread_launch(void *data) 
174 {
175         pthread_t thread;
176         pthread_attr_t attr;
177         int result;
178
179         result = pthread_attr_init(&attr);
180         pthread_attr_setschedpolicy(&attr, SCHED_RR);
181         pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
182         result = ast_pthread_create(&thread, &attr,ast_bridge_call_thread, data);
183         result = pthread_attr_destroy(&attr);
184 }
185
186
187
188 static int adsi_announce_park(struct ast_channel *chan, int parkingnum)
189 {
190         int res;
191         int justify[5] = {ADSI_JUST_CENT, ADSI_JUST_CENT, ADSI_JUST_CENT, ADSI_JUST_CENT};
192         char tmp[256] = "";
193         char *message[5] = {NULL, NULL, NULL, NULL, NULL};
194
195         snprintf(tmp, sizeof(tmp), "Parked on %d", parkingnum);
196         message[0] = tmp;
197         res = adsi_load_session(chan, NULL, 0, 1);
198         if (res == -1) {
199                 return res;
200         }
201         return adsi_print(chan, message, justify, 1);
202 }
203
204 int ast_park_call(struct ast_channel *chan, struct ast_channel *peer, int timeout, int *extout)
205 {
206         /* We put the user in the parking list, then wake up the parking thread to be sure it looks
207            after these channels too */
208         struct parkeduser *pu, *cur;
209         int x;
210         char exten[AST_MAX_EXTENSION];
211         struct ast_context *con;
212         pu = malloc(sizeof(struct parkeduser));
213         if (pu) {
214                 memset(pu,0,sizeof(struct parkeduser));
215                 ast_mutex_lock(&parking_lock);
216                 for (x=parking_start;x<=parking_stop;x++) {
217                         cur = parkinglot;
218                         while(cur) {
219                                 if (cur->parkingnum == x) 
220                                         break;
221                                 cur = cur->next;
222                         }
223                         if (!cur)
224                                 break;
225                 }
226                 if (x <= parking_stop) {
227                         chan->appl = "Parked Call";
228                         chan->data = NULL; 
229
230                         pu->chan = chan;
231                         /* Start music on hold */
232                         if (chan != peer) {
233                                 ast_indicate(pu->chan, AST_CONTROL_HOLD);
234                                 ast_moh_start(pu->chan, NULL);
235                         }
236                         gettimeofday(&pu->start, NULL);
237                         pu->parkingnum = x;
238                         if (timeout > 0)
239                                 pu->parkingtime = timeout;
240                         else
241                                 pu->parkingtime = parkingtime;
242                         if (extout)
243                                 *extout = x;
244                         if (peer) {
245                                 strncpy(pu->peername,peer->name,sizeof(pu->peername) - 1);
246                         }
247                         /* Remember what had been dialed, so that if the parking
248                            expires, we try to come back to the same place */
249                         if (!ast_strlen_zero(chan->macrocontext))
250                                 strncpy(pu->context, chan->macrocontext, sizeof(pu->context)-1);
251                         else
252                                 strncpy(pu->context, chan->context, sizeof(pu->context)-1);
253                         if (!ast_strlen_zero(chan->macroexten))
254                                 strncpy(pu->exten, chan->macroexten, sizeof(pu->exten)-1);
255                         else
256                                 strncpy(pu->exten, chan->exten, sizeof(pu->exten)-1);
257                         if (chan->macropriority)
258                                 pu->priority = chan->macropriority;
259                         else
260                                 pu->priority = chan->priority;
261                         pu->next = parkinglot;
262                         parkinglot = pu;
263                         /* If parking a channel directly, don't quiet yet get parking running on it */
264                         if (peer == chan) 
265                                 pu->notquiteyet = 1;
266                         ast_mutex_unlock(&parking_lock);
267                         /* Wake up the (presumably select()ing) thread */
268                         pthread_kill(parking_thread, SIGURG);
269                         if (option_verbose > 1) 
270                                 ast_verbose(VERBOSE_PREFIX_2 "Parked %s on %d. Will timeout back to %s,%s,%d in %d seconds\n", pu->chan->name, pu->parkingnum, pu->context, pu->exten, pu->priority, (pu->parkingtime/1000));
271
272                         manager_event(EVENT_FLAG_CALL, "ParkedCall",
273                                 "Exten: %d\r\n"
274                                 "Channel: %s\r\n"
275                                 "From: %s\r\n"
276                                 "Timeout: %ld\r\n"
277                                 "CallerID: %s\r\n"
278                                 "CallerIDName: %s\r\n\r\n"
279                                 ,pu->parkingnum, pu->chan->name, peer->name
280                                 ,(long)pu->start.tv_sec + (long)(pu->parkingtime/1000) - (long)time(NULL)
281                                 ,(pu->chan->cid.cid_num ? pu->chan->cid.cid_num : "<unknown>")
282                                 ,(pu->chan->cid.cid_name ? pu->chan->cid.cid_name : "<unknown>")
283                                 );
284
285                         if (peer) {
286                                 if (adsipark && adsi_available(peer)) {
287                                         adsi_announce_park(peer, pu->parkingnum);
288                                 }
289                                 if (adsipark && adsi_available(peer)) {
290                                         adsi_unload_session(peer);
291                                 }
292                         }
293                         con = ast_context_find(parking_con);
294                         if (!con) {
295                                 con = ast_context_create(NULL,parking_con, registrar);
296                                 if (!con) {
297                                         ast_log(LOG_ERROR, "Parking context '%s' does not exist and unable to create\n", parking_con);
298                                 }
299                         }
300                         if (con) {
301                                 snprintf(exten, sizeof(exten), "%d", x);
302                                 ast_add_extension2(con, 1, exten, 1, NULL, NULL, parkedcall, strdup(exten), free, registrar);
303                         }
304                         if (peer) ast_say_digits(peer, pu->parkingnum, "", peer->language);
305                         if (pu->notquiteyet) {
306                                 /* Wake up parking thread if we're really done */
307                                 ast_moh_start(pu->chan, NULL);
308                                 pu->notquiteyet = 0;
309                                 pthread_kill(parking_thread, SIGURG);
310                         }
311                         return 0;
312                 } else {
313                         ast_log(LOG_WARNING, "No more parking spaces\n");
314                         free(pu);
315                         ast_mutex_unlock(&parking_lock);
316                         return -1;
317                 }
318         } else {
319                 ast_log(LOG_WARNING, "Out of memory\n");
320                 return -1;
321         }
322         return 0;
323 }
324
325 int ast_masq_park_call(struct ast_channel *rchan, struct ast_channel *peer, int timeout, int *extout)
326 {
327         struct ast_channel *chan;
328         struct ast_frame *f;
329         /* Make a new, fake channel that we'll use to masquerade in the real one */
330         chan = ast_channel_alloc(0);
331         if (chan) {
332                 /* Let us keep track of the channel name */
333                 snprintf(chan->name, sizeof (chan->name), "Parked/%s",rchan->name);
334                 /* Make formats okay */
335                 chan->readformat = rchan->readformat;
336                 chan->writeformat = rchan->writeformat;
337                 ast_channel_masquerade(chan, rchan);
338                 /* Setup the extensions and such */
339                 strncpy(chan->context, rchan->context, sizeof(chan->context) - 1);
340                 strncpy(chan->exten, rchan->exten, sizeof(chan->exten) - 1);
341                 chan->priority = rchan->priority;
342                 /* Make the masq execute */
343                 f = ast_read(chan);
344                 if (f)
345                         ast_frfree(f);
346                 ast_park_call(chan, peer, timeout, extout);
347         } else {
348                 ast_log(LOG_WARNING, "Unable to create parked channel\n");
349                 return -1;
350         }
351         return 0;
352 }
353
354
355 #define FEATURE_RETURN_HANGUP           -1
356 #define FEATURE_RETURN_SUCCESSBREAK      0
357 #define FEATURE_RETURN_PBX_KEEPALIVE    AST_PBX_KEEPALIVE
358 #define FEATURE_RETURN_NO_HANGUP_PEER   AST_PBX_NO_HANGUP_PEER
359 #define FEATURE_RETURN_PASSDIGITS        21
360 #define FEATURE_RETURN_STOREDIGITS       22
361 #define FEATURE_RETURN_SUCCESS           23
362
363 #define FEATURE_SENSE_CHAN      (1 << 0)
364 #define FEATURE_SENSE_PEER      (1 << 1)
365 #define FEATURE_MAX_LEN         11
366
367 static int builtin_automonitor(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, char *code, int sense)
368 {
369         char *touch_monitor = NULL, *caller_chan_id = NULL, *callee_chan_id = NULL, *args = NULL;
370         int x = 0;
371         size_t len;
372         struct ast_channel *caller_chan = NULL, *callee_chan = NULL;
373
374
375         if(sense == 2) {
376                 caller_chan = peer;
377                 callee_chan = chan;
378         } else {
379                 callee_chan = peer;
380                 caller_chan = chan;
381         }
382         
383         if (!monitor_ok) {
384                 ast_log(LOG_ERROR,"Cannot record the call. The monitor application is disabled.\n");
385                 return -1;
386         }
387
388         if (!monitor_app) { 
389                 if (!(monitor_app = pbx_findapp("Monitor"))) {
390                         monitor_ok=0;
391                         ast_log(LOG_ERROR,"Cannot record the call. The monitor application is disabled.\n");
392                         return -1;
393                 }
394         }
395         if (!ast_strlen_zero(courtesytone)) {
396                 if (ast_autoservice_start(callee_chan))
397                         return -1;
398                 if (!ast_streamfile(caller_chan, courtesytone, caller_chan->language)) {
399                         if (ast_waitstream(caller_chan, "") < 0) {
400                                 ast_log(LOG_WARNING, "Failed to play courtesy tone!\n");
401                                 ast_autoservice_stop(callee_chan);
402                                 return -1;
403                         }
404                 }
405                 if (ast_autoservice_stop(callee_chan))
406                         return -1;
407         }
408         
409         if (callee_chan->monitor) {
410                 if (option_verbose > 3)
411                         ast_verbose(VERBOSE_PREFIX_3 "User hit '%s' to stop recording call.\n", code);
412                 ast_monitor_stop(callee_chan, 1);
413                 return FEATURE_RETURN_SUCCESS;
414         }
415
416         if (caller_chan && callee_chan) {
417                 touch_monitor = pbx_builtin_getvar_helper(caller_chan, "TOUCH_MONITOR");
418                 if (!touch_monitor)
419                         touch_monitor = pbx_builtin_getvar_helper(callee_chan, "TOUCH_MONITOR");
420                 
421                 if (touch_monitor) {
422                         len = strlen(touch_monitor) + 50;
423                         args = alloca(len);
424                         snprintf(args, len, "WAV|auto-%ld-%s|m", time(NULL), touch_monitor);
425                 } else {
426                         caller_chan_id = ast_strdupa(caller_chan->cid.cid_num ? caller_chan->cid.cid_num : caller_chan->name);
427                         callee_chan_id = ast_strdupa(callee_chan->cid.cid_num ? callee_chan->cid.cid_num : callee_chan->name);
428                         len = strlen(caller_chan_id) + strlen(callee_chan_id) + 50;
429                         args = alloca(len);
430                         snprintf(args, len, "WAV|auto-%ld-%s-%s|m", time(NULL), caller_chan_id, callee_chan_id);
431                 }
432
433                 for( x = 0; x < strlen(args); x++)
434                         if (args[x] == '/')
435                                 args[x] = '-';
436                 
437                 if (option_verbose > 3)
438                         ast_verbose(VERBOSE_PREFIX_3 "User hit '%s' to record call. filename: %s\n", code, args);
439
440                 pbx_exec(callee_chan, monitor_app, args, 1);
441                 
442                 return FEATURE_RETURN_SUCCESS;
443         }
444         
445         ast_log(LOG_NOTICE,"Cannot record the call. One or both channels have gone away.\n");   
446         return -1;
447 }
448
449 static int builtin_disconnect(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, char *code, int sense)
450 {
451         if (option_verbose > 3)
452                 ast_verbose(VERBOSE_PREFIX_3 "User hit '%s' to disconnect call.\n", code);
453         return FEATURE_RETURN_HANGUP;
454 }
455
456 static int builtin_blindtransfer(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, char *code, int sense)
457 {
458         struct ast_channel *transferer;
459         struct ast_channel *transferee;
460         char *transferer_real_context;
461         char newext[256];
462         int res;
463
464         if (sense == FEATURE_SENSE_PEER) {
465                 transferer = peer;
466                 transferee = chan;
467         } else {
468                 transferer = chan;
469                 transferee = peer;
470         }
471         if (!(transferer_real_context=pbx_builtin_getvar_helper(transferee, "TRANSFER_CONTEXT")) &&
472            !(transferer_real_context=pbx_builtin_getvar_helper(transferer, "TRANSFER_CONTEXT"))) {
473                 /* Use the non-macro context to transfer the call */
474                 if (!ast_strlen_zero(transferer->macrocontext))
475                         transferer_real_context = transferer->macrocontext;
476                 else
477                         transferer_real_context = transferer->context;
478         }
479         /* Start autoservice on chan while we talk
480            to the originator */
481         ast_indicate(transferee, AST_CONTROL_HOLD);
482         ast_autoservice_start(transferee);
483         ast_moh_start(transferee, NULL);
484
485         memset(newext, 0, sizeof(newext));
486         
487         /* Transfer */
488         if ((res=ast_streamfile(transferer, "pbx-transfer", transferer->language))) {
489                 ast_moh_stop(transferee);
490                 ast_autoservice_stop(transferee);
491                 ast_indicate(transferee, AST_CONTROL_UNHOLD);
492                 return res;
493         }
494         if ((res=ast_waitstream(transferer, AST_DIGIT_ANY)) < 0) {
495                 ast_moh_stop(transferee);
496                 ast_autoservice_stop(transferee);
497                 ast_indicate(transferee, AST_CONTROL_UNHOLD);
498                 return res;
499         } else if (res > 0) {
500                 /* If they've typed a digit already, handle it */
501                 newext[0] = (char) res;
502         }
503
504         ast_stopstream(transferer);
505         res = ast_app_dtget(transferer, transferer_real_context, newext, sizeof(newext), 100, transferdigittimeout);
506         if (res < 0) {
507                 ast_moh_stop(transferee);
508                 ast_autoservice_stop(transferee);
509                 ast_indicate(transferee, AST_CONTROL_UNHOLD);
510                 return res;
511         }
512         if (!strcmp(newext, ast_parking_ext())) {
513                 ast_moh_stop(transferee);
514
515                 res = ast_autoservice_stop(transferee);
516                 ast_indicate(transferee, AST_CONTROL_UNHOLD);
517                 if (res)
518                         res = -1;
519                 else if (!ast_park_call(transferee, transferer, 0, NULL)) {
520                         /* We return non-zero, but tell the PBX not to hang the channel when
521                            the thread dies -- We have to be careful now though.  We are responsible for 
522                            hanging up the channel, else it will never be hung up! */
523
524                         if (transferer==peer)
525                                 res=AST_PBX_KEEPALIVE;
526                         else
527                                 res=AST_PBX_NO_HANGUP_PEER;
528                         return res;
529                 } else {
530                         ast_log(LOG_WARNING, "Unable to park call %s\n", transferee->name);
531                 }
532                 /* XXX Maybe we should have another message here instead of invalid extension XXX */
533         } else if (ast_exists_extension(transferee, transferer_real_context, newext, 1, transferer->cid.cid_num)) {
534                 pbx_builtin_setvar_helper(peer, "BLINDTRANSFER", chan->name);
535                 pbx_builtin_setvar_helper(chan, "BLINDTRANSFER", peer->name);
536                 ast_moh_stop(transferee);
537                 res=ast_autoservice_stop(transferee);
538                 ast_indicate(transferee, AST_CONTROL_UNHOLD);
539                 if (!transferee->pbx) {
540                         /* Doh!  Use our handy async_goto functions */
541                         if (option_verbose > 2) 
542                                 ast_verbose(VERBOSE_PREFIX_3 "Transferring %s to '%s' (context %s) priority 1\n"
543                                                                 ,transferee->name, newext, transferer_real_context);
544                         if (ast_async_goto(transferee, transferer_real_context, newext, 1))
545                                 ast_log(LOG_WARNING, "Async goto failed :-(\n");
546                         res = -1;
547                 } else {
548                         /* Set the channel's new extension, since it exists, using transferer context */
549                         strncpy(transferee->exten, newext, sizeof(transferee->exten)-1);
550                         strncpy(transferee->context, transferer_real_context, sizeof(transferee->context)-1);
551                         transferee->priority = 0;
552                 }
553                 return res;
554         } else {
555                 if (option_verbose > 2) 
556                         ast_verbose(VERBOSE_PREFIX_3 "Unable to find extension '%s' in context '%s'\n", newext, transferer_real_context);
557         }
558         if (!ast_strlen_zero(xferfailsound))
559                 res = ast_streamfile(transferer, xferfailsound, transferee->language);
560         else
561                 res = 0;
562         if (res) {
563                 ast_moh_stop(transferee);
564                 ast_autoservice_stop(transferee);
565                 ast_indicate(transferee, AST_CONTROL_UNHOLD);
566                 return res;
567         }
568         res = ast_waitstream(transferer, AST_DIGIT_ANY);
569         ast_stopstream(transferer);
570         ast_moh_stop(transferee);
571         res = ast_autoservice_stop(transferee);
572         ast_indicate(transferee, AST_CONTROL_UNHOLD);
573         if (res) {
574                 if (option_verbose > 1)
575                         ast_verbose(VERBOSE_PREFIX_2 "Hungup during autoservice stop on '%s'\n", transferee->name);
576                 return res;
577         }
578         return FEATURE_RETURN_SUCCESS;
579 }
580
581 static int builtin_atxfer(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, char *code, int sense)
582 {
583         struct ast_channel *transferer;
584         struct ast_channel *transferee;
585         struct ast_channel *newchan, *xferchan=NULL;
586         int outstate=0;
587         struct ast_bridge_config bconfig;
588         char *transferer_real_context;
589         char xferto[256],dialstr[265];
590         char *cid_num;
591         char *cid_name;
592         int res;
593         struct ast_frame *f = NULL;
594         struct ast_bridge_thread_obj *tobj;
595
596         ast_log(LOG_DEBUG, "Executing Attended Transfer %s, %s (sense=%d) XXX\n", chan->name, peer->name, sense);
597         if (sense == FEATURE_SENSE_PEER) {
598                 transferer = peer;
599                 transferee = chan;
600         } else {
601                 transferer = chan;
602                 transferee = peer;
603         }
604         if (!(transferer_real_context=pbx_builtin_getvar_helper(transferee, "TRANSFER_CONTEXT")) &&
605            !(transferer_real_context=pbx_builtin_getvar_helper(transferer, "TRANSFER_CONTEXT"))) {
606                 /* Use the non-macro context to transfer the call */
607                 if (!ast_strlen_zero(transferer->macrocontext))
608                         transferer_real_context = transferer->macrocontext;
609                 else
610                         transferer_real_context = transferer->context;
611         }
612         /* Start autoservice on chan while we talk
613            to the originator */
614         ast_indicate(transferee, AST_CONTROL_HOLD);
615         ast_autoservice_start(transferee);
616         ast_moh_start(transferee, NULL);
617         memset(xferto, 0, sizeof(xferto));
618         /* Transfer */
619         if ((res=ast_streamfile(transferer, "pbx-transfer", transferer->language))) {
620                 ast_moh_stop(transferee);
621                 ast_autoservice_stop(transferee);
622                 ast_indicate(transferee, AST_CONTROL_UNHOLD);
623                 return res;
624         }
625         if ((res=ast_waitstream(transferer, AST_DIGIT_ANY)) < 0) {
626                 ast_moh_stop(transferee);
627                 ast_autoservice_stop(transferee);
628                 ast_indicate(transferee, AST_CONTROL_UNHOLD);
629                 return res;
630         } else if(res > 0) {
631                 /* If they've typed a digit already, handle it */
632                 xferto[0] = (char) res;
633         }
634         if ((ast_app_dtget(transferer, transferer_real_context, xferto, sizeof(xferto), 100, transferdigittimeout))) {
635                 cid_num = transferer->cid.cid_num;
636                 cid_name = transferer->cid.cid_name;
637                 if (ast_exists_extension(transferer, transferer_real_context,xferto, 1, cid_num)) {
638                         snprintf(dialstr, sizeof(dialstr), "%s@%s/n", xferto, transferer_real_context);
639                         if ((newchan = ast_request_and_dial("Local", ast_best_codec(transferer->nativeformats), dialstr,30000, &outstate, cid_num, cid_name))) {
640                                 res = ast_channel_make_compatible(transferer, newchan);
641                                 if (res < 0) {
642                                         ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", transferer->name, newchan->name);
643                                         ast_hangup(newchan);
644                                         return -1;
645                                 }
646                                 memset(&bconfig,0,sizeof(struct ast_bridge_config));
647                                 ast_set_flag(&(bconfig.features_caller), AST_FEATURE_DISCONNECT);
648                                 ast_set_flag(&(bconfig.features_callee), AST_FEATURE_DISCONNECT);
649                                 res = ast_bridge_call(transferer,newchan,&bconfig);
650                                 if (newchan->_softhangup || newchan->_state != AST_STATE_UP) {
651                                         ast_hangup(newchan);
652                                         if (f) {
653                                                 ast_frfree(f);
654                                                 f = NULL;
655                                         }
656                                         if (!ast_strlen_zero(xfersound) && !ast_streamfile(transferer, xfersound, transferer->language)) {
657                                                 if (ast_waitstream(transferer, "") < 0) {
658                                                         ast_log(LOG_WARNING, "Failed to play courtesy tone!\n");
659                                                 }
660                                         }
661                                         ast_moh_stop(transferee);
662                                         ast_autoservice_stop(transferee);
663                                         ast_indicate(transferee, AST_CONTROL_UNHOLD);
664                                         transferer->_softhangup = 0;
665                                         return FEATURE_RETURN_SUCCESS;
666                                 }
667                                 
668                                 res = ast_channel_make_compatible(transferee, newchan);
669                                 if (res < 0) {
670                                         ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", transferee->name, newchan->name);
671                                         ast_hangup(newchan);
672                                         return -1;
673                                 }
674                                 
675                                 
676                                 ast_moh_stop(transferee);
677                                 
678                                 if ((ast_autoservice_stop(transferee) < 0)
679                                    ||(ast_waitfordigit(transferee,100) < 0)
680                                    || (ast_waitfordigit(newchan,100) < 0) 
681                                    || ast_check_hangup(transferee) 
682                                    || ast_check_hangup(newchan)) {
683                                         ast_hangup(newchan);
684                                         res = -1;
685                                         return -1;
686                                 }
687
688                                 if ((xferchan = ast_channel_alloc(0))) {
689                                         snprintf(xferchan->name, sizeof (xferchan->name), "Transfered/%s",transferee->name);
690                                         /* Make formats okay */
691                                         xferchan->readformat = transferee->readformat;
692                                         xferchan->writeformat = transferee->writeformat;
693                                         ast_channel_masquerade(xferchan, transferee);
694                                         ast_explicit_goto(xferchan, transferee->context, transferee->exten, transferee->priority);
695                                         xferchan->_state = AST_STATE_UP;
696                                         ast_clear_flag(xferchan, AST_FLAGS_ALL);        
697                                         xferchan->_softhangup = 0;
698
699                                         if ((f = ast_read(xferchan))) {
700                                                 ast_frfree(f);
701                                                 f = NULL;
702                                         }
703                                         
704                                 } else {
705                                         ast_hangup(newchan);
706                                         return -1;
707                                 }
708
709                                 newchan->_state = AST_STATE_UP;
710                                 ast_clear_flag(newchan, AST_FLAGS_ALL); 
711                                 newchan->_softhangup = 0;
712
713                                 tobj = malloc(sizeof(struct ast_bridge_thread_obj));
714                                 if (tobj) {
715                                         memset(tobj,0,sizeof(struct ast_bridge_thread_obj));
716                                         tobj->chan = xferchan;
717                                         tobj->peer = newchan;
718                                         tobj->bconfig = *config;
719         
720                                         if (!ast_strlen_zero(xfersound) && !ast_streamfile(newchan, xfersound, newchan->language)) {
721                                                 if (ast_waitstream(newchan, "") < 0) {
722                                                         ast_log(LOG_WARNING, "Failed to play courtesy tone!\n");
723                                                 }
724                                         }
725                                         ast_bridge_call_thread_launch(tobj);
726                                 } else {
727                                         ast_log(LOG_WARNING, "Out of memory!\n");
728                                         ast_hangup(xferchan);
729                                         ast_hangup(newchan);
730                                 }
731                                 return -1;
732                                 
733                         } else {
734                                 ast_log(LOG_WARNING, "Unable to create channel Local/%s do you have chan_local?\n",dialstr);
735                                 ast_moh_stop(transferee);
736                                 ast_autoservice_stop(transferee);
737                                 ast_indicate(transferee, AST_CONTROL_UNHOLD);
738                                 if (!ast_strlen_zero(xferfailsound)) {
739                                         res = ast_streamfile(transferer, xferfailsound, transferer->language);
740                                         if (!res && (ast_waitstream(transferer, "") < 0)) {
741                                                 return -1;
742                                         }
743                                 }
744                                 return -1;
745                         }
746                 } else {
747                         ast_log(LOG_WARNING, "Extension %s does not exist in context %s\n",xferto,transferer_real_context);
748                         ast_moh_stop(transferee);
749                         ast_autoservice_stop(transferee);
750                         ast_indicate(transferee, AST_CONTROL_UNHOLD);
751                         res = ast_streamfile(transferer, "beeperr", transferer->language);
752                         if (!res && (ast_waitstream(transferer, "") < 0)) {
753                                 return -1;
754                         }
755                 }
756         }  else {
757                 ast_log(LOG_WARNING, "Did not read data.\n");
758                 res = ast_streamfile(transferer, "beeperr", transferer->language);
759                 if (ast_waitstream(transferer, "") < 0) {
760                         return -1;
761                 }
762         }
763         ast_moh_stop(transferee);
764         ast_autoservice_stop(transferee);
765         ast_indicate(transferee, AST_CONTROL_UNHOLD);
766
767         return FEATURE_RETURN_SUCCESS;
768 }
769
770 struct ast_call_feature {
771         int feature_mask;
772         char *fname;
773         char *sname;
774         char exten[FEATURE_MAX_LEN];
775         char default_exten[FEATURE_MAX_LEN];
776         int (*operation)(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, char *code, int sense);
777         unsigned int flags;
778 };
779
780 /* add atxfer and automon as undefined so you can only use em if you configure them */
781 #define FEATURES_COUNT (sizeof(builtin_features) / sizeof(builtin_features[0]))
782 struct ast_call_feature builtin_features[] = 
783 {
784         { AST_FEATURE_REDIRECT, "Blind Transfer", "blindxfer", "#", "#", builtin_blindtransfer, AST_FEATURE_FLAG_NEEDSDTMF },
785         { AST_FEATURE_REDIRECT, "Attended Transfer", "atxfer", "", "", builtin_atxfer, AST_FEATURE_FLAG_NEEDSDTMF },
786         { AST_FEATURE_AUTOMON, "One Touch Monitor", "automon", "", "", builtin_automonitor, AST_FEATURE_FLAG_NEEDSDTMF },
787         { AST_FEATURE_DISCONNECT, "Disconnect Call", "disconnect", "*", "*", builtin_disconnect, AST_FEATURE_FLAG_NEEDSDTMF },
788 };
789
790 static void unmap_features(void)
791 {
792         int x;
793         for (x=0;x<FEATURES_COUNT;x++)
794                 strcpy(builtin_features[x].exten, builtin_features[x].default_exten);
795 }
796
797 static int remap_feature(const char *name, const char *value)
798 {
799         int x;
800         int res = -1;
801         for (x=0;x<FEATURES_COUNT;x++) {
802                 if (!strcasecmp(name, builtin_features[x].sname)) {
803                         strncpy(builtin_features[x].exten, value, sizeof(builtin_features[x].exten) - 1);
804                         if (option_verbose > 1)
805                                 ast_verbose(VERBOSE_PREFIX_2 "Remapping feature %s (%s) to sequence '%s'\n", builtin_features[x].fname, builtin_features[x].sname, builtin_features[x].exten);
806                         res = 0;
807                 } else if (!strcmp(value, builtin_features[x].exten)) 
808                         ast_log(LOG_WARNING, "Sequence '%s' already mapped to function %s (%s) while assigning to %s\n", value, builtin_features[x].fname, builtin_features[x].sname, name);
809         }
810         return res;
811 }
812
813 static int ast_feature_interpret(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, char *code, int sense)
814 {
815         int x;
816         struct ast_flags features;
817         int res = FEATURE_RETURN_PASSDIGITS;
818
819         if (sense == FEATURE_SENSE_CHAN)
820                 ast_copy_flags(&features, &(config->features_caller), AST_FLAGS_ALL);   
821         else
822                 ast_copy_flags(&features, &(config->features_callee), AST_FLAGS_ALL);   
823         ast_log(LOG_DEBUG, "Feature interpret: chan=%s, peer=%s, sense=%d, features=%d\n", chan->name, peer->name, sense, features.flags);
824         for (x=0;x<FEATURES_COUNT;x++) {
825                 if ((ast_test_flag(&features, builtin_features[x].feature_mask)) &&
826                     !ast_strlen_zero(builtin_features[x].exten)) {
827                         /* Feature is up for consideration */
828                         if (!strcmp(builtin_features[x].exten, code)) {
829                                 res = builtin_features[x].operation(chan, peer, config, code, sense);
830                                 break;
831                         } else if (!strncmp(builtin_features[x].exten, code, strlen(code))) {
832                                 if (res == FEATURE_RETURN_PASSDIGITS)
833                                         res = FEATURE_RETURN_STOREDIGITS;
834                         }
835                 }
836         }
837         return res;
838 }
839
840 static void set_config_flags(struct ast_bridge_config *config)
841 {
842         int x;
843         ast_clear_flag(config, AST_FLAGS_ALL);  
844         for (x=0;x<FEATURES_COUNT;x++) {
845                 if (ast_test_flag(&(config->features_caller), builtin_features[x].feature_mask)) {
846                         if (ast_test_flag(builtin_features + x, AST_FEATURE_FLAG_NEEDSDTMF))
847                                 ast_set_flag(config, AST_BRIDGE_DTMF_CHANNEL_0);
848                 }
849                 if (ast_test_flag(&(config->features_callee), builtin_features[x].feature_mask)) {
850                         if (ast_test_flag(builtin_features + x, AST_FEATURE_FLAG_NEEDSDTMF))
851                                 ast_set_flag(config, AST_BRIDGE_DTMF_CHANNEL_1);
852                 }
853         }
854 }
855
856 int ast_bridge_call(struct ast_channel *chan,struct ast_channel *peer,struct ast_bridge_config *config)
857 {
858         /* Copy voice back and forth between the two channels.  Give the peer
859            the ability to transfer calls with '#<extension' syntax. */
860         struct ast_frame *f;
861         struct ast_channel *who;
862         char chan_featurecode[FEATURE_MAX_LEN + 1]="";
863         char peer_featurecode[FEATURE_MAX_LEN + 1]="";
864         int res;
865         int diff;
866         int hasfeatures=0;
867         int hadfeatures=0;
868         struct ast_option_header *aoh;
869         struct timeval start, end;
870         struct ast_bridge_config backup_config;
871         int allowdisconnect_in,allowdisconnect_out,allowredirect_in,allowredirect_out;
872         char *monitor_exec;
873
874         memset(&backup_config, 0, sizeof(backup_config));
875
876         if (chan && peer) {
877                 pbx_builtin_setvar_helper(chan, "BRIDGEPEER", peer->name);
878                 pbx_builtin_setvar_helper(peer, "BRIDGEPEER", chan->name);
879         } else if (chan)
880                 pbx_builtin_setvar_helper(chan, "BLINDTRANSFER", NULL);
881
882         if (monitor_ok) {
883                 if (!monitor_app) { 
884                         if (!(monitor_app = pbx_findapp("Monitor")))
885                                 monitor_ok=0;
886                 }
887                 if ((monitor_exec = pbx_builtin_getvar_helper(chan, "AUTO_MONITOR"))) 
888                         pbx_exec(chan, monitor_app, monitor_exec, 1);
889                 else if ((monitor_exec = pbx_builtin_getvar_helper(peer, "AUTO_MONITOR")))
890                         pbx_exec(peer, monitor_app, monitor_exec, 1);
891         }
892         
893         allowdisconnect_in = ast_test_flag(&(config->features_callee), AST_FEATURE_DISCONNECT);
894         allowdisconnect_out = ast_test_flag(&(config->features_caller), AST_FEATURE_DISCONNECT);
895         allowredirect_in = ast_test_flag(&(config->features_callee), AST_FEATURE_REDIRECT);
896         allowredirect_out = ast_test_flag(&(config->features_caller), AST_FEATURE_REDIRECT);
897         set_config_flags(config);
898         config->firstpass = 1;
899
900         /* Answer if need be */
901         if (ast_answer(chan))
902                 return -1;
903         peer->appl = "Bridged Call";
904         peer->data = chan->name;
905         /* copy the userfield from the B-leg to A-leg if applicable */
906         if (chan->cdr && peer->cdr && !ast_strlen_zero(peer->cdr->userfield)) {
907                 char tmp[256];
908                 if (!ast_strlen_zero(chan->cdr->userfield)) {
909                         snprintf(tmp, sizeof(tmp), "%s;%s",chan->cdr->userfield, peer->cdr->userfield);
910                         ast_cdr_appenduserfield(chan, tmp);
911                 } else
912                         ast_cdr_setuserfield(chan, peer->cdr->userfield);
913                 /* free the peer's cdr without ast_cdr_free complaining */
914                 free(peer->cdr);
915                 peer->cdr = NULL;
916         }
917         for (;;) {
918                 if (config->timelimit)
919                         gettimeofday(&start, NULL);
920                 res = ast_channel_bridge(chan,peer,config,&f, &who);
921                 if (config->timelimit) {
922                         /* Update time limit for next pass */
923                         gettimeofday(&end, NULL);
924                         diff = (end.tv_sec - start.tv_sec) * 1000;
925                         diff += (end.tv_usec - start.tv_usec) / 1000;
926                         config->timelimit -= diff;
927                         if (hasfeatures) {
928                                 /* Running on backup config, meaning a feature might be being
929                                    activated, but that's no excuse to keep things going 
930                                    indefinitely! */
931                                 if (backup_config.timelimit && ((backup_config.timelimit -= diff) <= 0)) {
932                                         ast_log(LOG_DEBUG, "Timed out, realtime this time!\n");
933                                         config->timelimit = 0;
934                                         who = chan;
935                                         if (f)
936                                                 ast_frfree(f);
937                                         f = NULL;
938                                         res = 0;
939                                 } else if (config->timelimit <= 0) {
940                                         /* Not *really* out of time, just out of time for
941                                            digits to come in for features. */
942                                         ast_log(LOG_DEBUG, "Timed out for feature!\n");
943                                         if (!ast_strlen_zero(peer_featurecode)) {
944                                                 ast_dtmf_stream(chan, peer, peer_featurecode, 0);
945                                                 memset(peer_featurecode, 0, sizeof(peer_featurecode));
946                                         }
947                                         if (!ast_strlen_zero(chan_featurecode)) {
948                                                 ast_dtmf_stream(peer, chan, chan_featurecode, 0);
949                                                 memset(chan_featurecode, 0, sizeof(chan_featurecode));
950                                         }
951                                         if (f)
952                                                 ast_frfree(f);
953                                         hasfeatures = !ast_strlen_zero(chan_featurecode) || !ast_strlen_zero(peer_featurecode);
954                                         if (!hasfeatures) {
955                                                 /* Restore original (possibly time modified) bridge config */
956                                                 memcpy(config, &backup_config, sizeof(struct ast_bridge_config));
957                                                 memset(&backup_config, 0, sizeof(backup_config));
958                                         }
959                                         hadfeatures = hasfeatures;
960                                         /* Continue as we were */
961                                         continue;
962                                 }
963                         } else {
964                                 if (config->timelimit <=0) {
965                                         /* We ran out of time */
966                                         config->timelimit = 0;
967                                         who = chan;
968                                         if (f)
969                                                 ast_frfree(f);
970                                         f = NULL;
971                                         res = 0;
972                                 }
973                         }
974                 }
975                 if (res < 0) {
976                         ast_log(LOG_WARNING, "Bridge failed on channels %s and %s\n", chan->name, peer->name);
977                         return -1;
978                 }
979                 
980                 if (!f || ((f->frametype == AST_FRAME_CONTROL) && ((f->subclass == AST_CONTROL_HANGUP) || (f->subclass == AST_CONTROL_BUSY) || 
981                         (f->subclass == AST_CONTROL_CONGESTION)))) {
982                                 res = -1;
983                                 break;
984                 }
985                 if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_RINGING)) {
986                         if (who == chan)
987                                 ast_indicate(peer, AST_CONTROL_RINGING);
988                         else
989                                 ast_indicate(chan, AST_CONTROL_RINGING);
990                 }
991                 if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == -1)) {
992                         if (who == chan)
993                                 ast_indicate(peer, -1);
994                         else
995                                 ast_indicate(chan, -1);
996                 }
997                 if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_FLASH)) {
998                         if (who == chan)
999                                 ast_indicate(peer, AST_CONTROL_FLASH);
1000                         else
1001                                 ast_indicate(chan, AST_CONTROL_FLASH);
1002                 }
1003                 if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_OPTION)) {
1004                         aoh = f->data;
1005                         /* Forward option Requests */
1006                         if (aoh && (aoh->flag == AST_OPTION_FLAG_REQUEST)) {
1007                                 if (who == chan)
1008                                         ast_channel_setoption(peer, ntohs(aoh->option), aoh->data, f->datalen - sizeof(struct ast_option_header), 0);
1009                                 else
1010                                         ast_channel_setoption(chan, ntohs(aoh->option), aoh->data, f->datalen - sizeof(struct ast_option_header), 0);
1011                         }
1012                 }
1013                 /* check for '*', if we find it it's time to disconnect */
1014                 if (f && (f->frametype == AST_FRAME_DTMF)) {
1015                         char *featurecode;
1016                         int sense;
1017                         struct ast_channel *other;
1018                         hadfeatures = hasfeatures;
1019                         /* This cannot overrun because the longest feature is one shorter than our buffer */
1020                         if (who == chan) {
1021                                 other = peer;
1022                                 sense = FEATURE_SENSE_CHAN;
1023                                 featurecode = chan_featurecode;
1024                         } else  {
1025                                 other = chan;
1026                                 sense = FEATURE_SENSE_PEER;
1027                                 featurecode = peer_featurecode;
1028                         }
1029                         featurecode[strlen(featurecode)] = f->subclass;
1030                         config->timelimit = backup_config.timelimit;
1031                         res = ast_feature_interpret(chan, peer, config, featurecode, sense);
1032                         switch(res) {
1033                         case FEATURE_RETURN_PASSDIGITS:
1034                                 ast_dtmf_stream(other, who, featurecode, 0);
1035                                 /* Fall through */
1036                         case FEATURE_RETURN_SUCCESS:
1037                                 memset(featurecode, 0, sizeof(chan_featurecode));
1038                                 break;
1039                         }
1040                         if (res >= FEATURE_RETURN_PASSDIGITS) {
1041                                 res = 0;
1042                         } else {
1043                                 ast_frfree(f);
1044                                 break;
1045                         }
1046                         hasfeatures = !ast_strlen_zero(chan_featurecode) || !ast_strlen_zero(peer_featurecode);
1047                         if (hadfeatures && !hasfeatures) {
1048                                 /* Restore backup */
1049                                 memcpy(config, &backup_config, sizeof(struct ast_bridge_config));
1050                                 memset(&backup_config, 0, sizeof(struct ast_bridge_config));
1051                         } else if (hasfeatures) {
1052                                 if (!hadfeatures) {
1053                                         /* Backup configuration */
1054                                         memcpy(&backup_config, config, sizeof(struct ast_bridge_config));
1055                                         /* Setup temporary config options */
1056                                         config->play_warning = 0;
1057                                         ast_clear_flag(&(config->features_caller), AST_FEATURE_PLAY_WARNING);
1058                                         ast_clear_flag(&(config->features_callee),AST_FEATURE_PLAY_WARNING);
1059                                         config->warning_freq = 0;
1060                                         config->warning_sound = NULL;
1061                                         config->end_sound = NULL;
1062                                         config->start_sound = NULL;
1063                                         config->firstpass = 0;
1064                                 }
1065                                 config->timelimit = featuredigittimeout;
1066                                 ast_log(LOG_DEBUG, "Set time limit to %ld\n", config->timelimit);
1067                         }
1068                 }
1069                 if (f)
1070                         ast_frfree(f);
1071         }
1072         return res;
1073 }
1074
1075 static void *do_parking_thread(void *ignore)
1076 {
1077         int ms, tms, max;
1078         struct parkeduser *pu, *pl, *pt = NULL;
1079         struct timeval tv;
1080         struct ast_frame *f;
1081         char exten[AST_MAX_EXTENSION];
1082         char *peername,*cp;
1083         char returnexten[AST_MAX_EXTENSION];
1084         struct ast_context *con;
1085         int x;
1086         fd_set rfds, efds;
1087         fd_set nrfds, nefds;
1088         FD_ZERO(&rfds);
1089         FD_ZERO(&efds);
1090
1091         for (;;) {
1092                 ms = -1;
1093                 max = -1;
1094                 ast_mutex_lock(&parking_lock);
1095                 pl = NULL;
1096                 pu = parkinglot;
1097                 FD_ZERO(&nrfds);
1098                 FD_ZERO(&nefds);
1099                 while(pu) {
1100                         if (pu->notquiteyet) {
1101                                 /* Pretend this one isn't here yet */
1102                                 pl = pu;
1103                                 pu = pu->next;
1104                                 continue;
1105                         }
1106                         gettimeofday(&tv, NULL);
1107                         tms = (tv.tv_sec - pu->start.tv_sec) * 1000 + (tv.tv_usec - pu->start.tv_usec) / 1000;
1108                         if (tms > pu->parkingtime) {
1109                                 /* Stop music on hold */
1110                                 ast_moh_stop(pu->chan);
1111                                 ast_indicate(pu->chan, AST_CONTROL_UNHOLD);
1112                                 /* Get chan, exten from derived kludge */
1113                                 if (pu->peername[0]) {
1114                                         peername = ast_strdupa(pu->peername);
1115                                         cp = strrchr(peername, '-');
1116                                         if (cp) 
1117                                                 *cp = 0;
1118                                         con = ast_context_find(parking_con_dial);
1119                                         if (!con) {
1120                                                 con = ast_context_create(NULL, parking_con_dial, registrar);
1121                                                 if (!con) {
1122                                                         ast_log(LOG_ERROR, "Parking dial context '%s' does not exist and unable to create\n", parking_con_dial);
1123                                                 }
1124                                         }
1125                                         if (con) {
1126                                                 snprintf(returnexten, sizeof(returnexten), "%s||t", peername);
1127                                                 ast_add_extension2(con, 1, peername, 1, NULL, NULL, "Dial", strdup(returnexten), free, registrar);
1128                                         }
1129                                         strncpy(pu->chan->exten, peername, sizeof(pu->chan->exten) - 1);
1130                                         strncpy(pu->chan->context, parking_con_dial, sizeof(pu->chan->context) - 1);
1131                                         pu->chan->priority = 1;
1132
1133                                 } else {
1134                                         /* They've been waiting too long, send them back to where they came.  Theoretically they
1135                                            should have their original extensions and such, but we copy to be on the safe side */
1136                                         strncpy(pu->chan->exten, pu->exten, sizeof(pu->chan->exten)-1);
1137                                         strncpy(pu->chan->context, pu->context, sizeof(pu->chan->context)-1);
1138                                         pu->chan->priority = pu->priority;
1139                                 }
1140
1141                                 manager_event(EVENT_FLAG_CALL, "ParkedCallTimeOut",
1142                                         "Exten: %d\r\n"
1143                                         "Channel: %s\r\n"
1144                                         "CallerID: %s\r\n"
1145                                         "CallerIDName: %s\r\n\r\n"
1146                                         ,pu->parkingnum, pu->chan->name
1147                                         ,(pu->chan->cid.cid_num ? pu->chan->cid.cid_num : "<unknown>")
1148                                         ,(pu->chan->cid.cid_name ? pu->chan->cid.cid_name : "<unknown>")
1149                                         );
1150
1151                                 if (option_verbose > 1) 
1152                                         ast_verbose(VERBOSE_PREFIX_2 "Timeout for %s parked on %d. Returning to %s,%s,%d\n", pu->chan->name, pu->parkingnum, pu->chan->context, pu->chan->exten, pu->chan->priority);
1153                                 /* Start up the PBX, or hang them up */
1154                                 if (ast_pbx_start(pu->chan))  {
1155                                         ast_log(LOG_WARNING, "Unable to restart the PBX for user on '%s', hanging them up...\n", pu->chan->name);
1156                                         ast_hangup(pu->chan);
1157                                 }
1158                                 /* And take them out of the parking lot */
1159                                 if (pl) 
1160                                         pl->next = pu->next;
1161                                 else
1162                                         parkinglot = pu->next;
1163                                 pt = pu;
1164                                 pu = pu->next;
1165                                 con = ast_context_find(parking_con);
1166                                 if (con) {
1167                                         snprintf(exten, sizeof(exten), "%d", pt->parkingnum);
1168                                         if (ast_context_remove_extension2(con, exten, 1, NULL))
1169                                                 ast_log(LOG_WARNING, "Whoa, failed to remove the extension!\n");
1170                                 } else
1171                                         ast_log(LOG_WARNING, "Whoa, no parking context?\n");
1172                                 free(pt);
1173                         } else {
1174                                 for (x=0; x<AST_MAX_FDS; x++) {
1175                                         if ((pu->chan->fds[x] > -1) && (FD_ISSET(pu->chan->fds[x], &rfds) || FD_ISSET(pu->chan->fds[x], &efds))) {
1176                                                 if (FD_ISSET(pu->chan->fds[x], &efds))
1177                                                         ast_set_flag(pu->chan, AST_FLAG_EXCEPTION);
1178                                                 else
1179                                                         ast_clear_flag(pu->chan, AST_FLAG_EXCEPTION);
1180                                                 pu->chan->fdno = x;
1181                                                 /* See if they need servicing */
1182                                                 f = ast_read(pu->chan);
1183                                                 if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass ==  AST_CONTROL_HANGUP))) {
1184
1185                                                         manager_event(EVENT_FLAG_CALL, "ParkedCallGiveUp",
1186                                                                 "Exten: %d\r\n"
1187                                                                 "Channel: %s\r\n"
1188                                                                 "CallerID: %s\r\n"
1189                                                                 "CallerIDName: %s\r\n\r\n"
1190                                                                 ,pu->parkingnum, pu->chan->name
1191                                                                 ,(pu->chan->cid.cid_num ? pu->chan->cid.cid_num : "<unknown>")
1192                                                                 ,(pu->chan->cid.cid_name ? pu->chan->cid.cid_name : "<unknown>")
1193                                                                 );
1194
1195                                                         /* There's a problem, hang them up*/
1196                                                         if (option_verbose > 1) 
1197                                                                 ast_verbose(VERBOSE_PREFIX_2 "%s got tired of being parked\n", pu->chan->name);
1198                                                         ast_hangup(pu->chan);
1199                                                         /* And take them out of the parking lot */
1200                                                         if (pl) 
1201                                                                 pl->next = pu->next;
1202                                                         else
1203                                                                 parkinglot = pu->next;
1204                                                         pt = pu;
1205                                                         pu = pu->next;
1206                                                         con = ast_context_find(parking_con);
1207                                                         if (con) {
1208                                                                 snprintf(exten, sizeof(exten), "%d", pt->parkingnum);
1209                                                                 if (ast_context_remove_extension2(con, exten, 1, NULL))
1210                                                                         ast_log(LOG_WARNING, "Whoa, failed to remove the extension!\n");
1211                                                         } else
1212                                                                 ast_log(LOG_WARNING, "Whoa, no parking context?\n");
1213                                                         free(pt);
1214                                                         break;
1215                                                 } else {
1216                                                         /* XXX Maybe we could do something with packets, like dial "0" for operator or something XXX */
1217                                                         ast_frfree(f);
1218                                                         if (pu->moh_trys < 3 && !pu->chan->generatordata) {
1219                                                                 ast_log(LOG_DEBUG, "MOH on parked call stopped by outside source.  Restarting.\n");
1220                                                                 ast_moh_start(pu->chan, NULL);
1221                                                                 pu->moh_trys++;
1222                                                         }
1223                                                         goto std;       /* XXX Ick: jumping into an else statement??? XXX */
1224                                                 }
1225                                         }
1226                                 }
1227                                 if (x >= AST_MAX_FDS) {
1228 std:                                    for (x=0; x<AST_MAX_FDS; x++) {
1229                                                 /* Keep this one for next one */
1230                                                 if (pu->chan->fds[x] > -1) {
1231                                                         FD_SET(pu->chan->fds[x], &nrfds);
1232                                                         FD_SET(pu->chan->fds[x], &nefds);
1233                                                         if (pu->chan->fds[x] > max)
1234                                                                 max = pu->chan->fds[x];
1235                                                 }
1236                                         }
1237                                         /* Keep track of our longest wait */
1238                                         if ((tms < ms) || (ms < 0))
1239                                                 ms = tms;
1240                                         pl = pu;
1241                                         pu = pu->next;
1242                                 }
1243                         }
1244                 }
1245                 ast_mutex_unlock(&parking_lock);
1246                 rfds = nrfds;
1247                 efds = nefds;
1248                 tv.tv_sec = ms / 1000;
1249                 tv.tv_usec = (ms % 1000) * 1000;
1250                 /* Wait for something to happen */
1251                 ast_select(max + 1, &rfds, NULL, &efds, (ms > -1) ? &tv : NULL);
1252                 pthread_testcancel();
1253         }
1254         return NULL;    /* Never reached */
1255 }
1256
1257 static int park_call_exec(struct ast_channel *chan, void *data)
1258 {
1259         /* Data is unused at the moment but could contain a parking
1260            lot context eventually */
1261         int res=0;
1262         struct localuser *u;
1263         LOCAL_USER_ADD(u);
1264         /* Setup the exten/priority to be s/1 since we don't know
1265            where this call should return */
1266         strcpy(chan->exten, "s");
1267         chan->priority = 1;
1268         if (chan->_state != AST_STATE_UP)
1269                 res = ast_answer(chan);
1270         if (!res)
1271                 res = ast_safe_sleep(chan, 1000);
1272         if (!res)
1273                 res = ast_park_call(chan, chan, 0, NULL);
1274         LOCAL_USER_REMOVE(u);
1275         if (!res)
1276                 res = AST_PBX_KEEPALIVE;
1277         return res;
1278 }
1279
1280 static int park_exec(struct ast_channel *chan, void *data)
1281 {
1282         int res=0;
1283         struct localuser *u;
1284         struct ast_channel *peer=NULL;
1285         struct parkeduser *pu, *pl=NULL;
1286         char exten[AST_MAX_EXTENSION];
1287         struct ast_context *con;
1288         int park;
1289         int dres;
1290         struct ast_bridge_config config;
1291
1292         if (!data) {
1293                 ast_log(LOG_WARNING, "Park requires an argument (extension number)\n");
1294                 return -1;
1295         }
1296         LOCAL_USER_ADD(u);
1297         park = atoi((char *)data);
1298         ast_mutex_lock(&parking_lock);
1299         pu = parkinglot;
1300         while(pu) {
1301                 if (pu->parkingnum == park) {
1302                         if (pl)
1303                                 pl->next = pu->next;
1304                         else
1305                                 parkinglot = pu->next;
1306                         break;
1307                 }
1308                 pl = pu;
1309                 pu = pu->next;
1310         }
1311         ast_mutex_unlock(&parking_lock);
1312         if (pu) {
1313                 peer = pu->chan;
1314                 con = ast_context_find(parking_con);
1315                 if (con) {
1316                         snprintf(exten, sizeof(exten), "%d", pu->parkingnum);
1317                         if (ast_context_remove_extension2(con, exten, 1, NULL))
1318                                 ast_log(LOG_WARNING, "Whoa, failed to remove the extension!\n");
1319                 } else
1320                         ast_log(LOG_WARNING, "Whoa, no parking context?\n");
1321
1322                 manager_event(EVENT_FLAG_CALL, "UnParkedCall",
1323                         "Exten: %d\r\n"
1324                         "Channel: %s\r\n"
1325                         "From: %s\r\n"
1326                         "CallerID: %s\r\n"
1327                         "CallerIDName: %s\r\n\r\n"
1328                         ,pu->parkingnum, pu->chan->name, chan->name
1329                         ,(pu->chan->cid.cid_num ? pu->chan->cid.cid_num : "<unknown>")
1330                         ,(pu->chan->cid.cid_name ? pu->chan->cid.cid_name : "<unknown>")
1331                         );
1332
1333                 free(pu);
1334         }
1335         /* JK02: it helps to answer the channel if not already up */
1336         if (chan->_state != AST_STATE_UP) {
1337                 ast_answer(chan);
1338         }
1339
1340         if (peer) {
1341                 /* Play a courtesy beep in the calling channel to prefix the bridge connecting */       
1342                 if (!ast_strlen_zero(courtesytone)) {
1343                         if (!ast_streamfile(chan, courtesytone, chan->language)) {
1344                                 if (ast_waitstream(chan, "") < 0) {
1345                                         ast_log(LOG_WARNING, "Failed to play courtesy tone!\n");
1346                                         ast_hangup(peer);
1347                                         return -1;
1348                                 }
1349                         }
1350                 }
1351  
1352                 ast_moh_stop(peer);
1353                 ast_indicate(peer, AST_CONTROL_UNHOLD);
1354                 res = ast_channel_make_compatible(chan, peer);
1355                 if (res < 0) {
1356                         ast_log(LOG_WARNING, "Could not make channels %s and %s compatible for bridge\n", chan->name, peer->name);
1357                         ast_hangup(peer);
1358                         return -1;
1359                 }
1360                 /* This runs sorta backwards, since we give the incoming channel control, as if it
1361                    were the person called. */
1362                 if (option_verbose > 2) 
1363                         ast_verbose(VERBOSE_PREFIX_3 "Channel %s connected to parked call %d\n", chan->name, park);
1364
1365                 memset(&config,0,sizeof(struct ast_bridge_config));
1366                 ast_set_flag(&(config.features_callee), AST_FEATURE_REDIRECT);
1367                 ast_set_flag(&(config.features_caller), AST_FEATURE_REDIRECT);
1368                 config.timelimit = 0;
1369                 config.play_warning = 0;
1370                 config.warning_freq = 0;
1371                 config.warning_sound=NULL;
1372                 res = ast_bridge_call(chan,peer,&config);
1373
1374                 /* Simulate the PBX hanging up */
1375                 if (res != AST_PBX_NO_HANGUP_PEER)
1376                         ast_hangup(peer);
1377                 return res;
1378         } else {
1379                 /* XXX Play a message XXX */
1380                 dres = ast_streamfile(chan, "pbx-invalidpark", chan->language);
1381                 if (!dres)
1382                         dres = ast_waitstream(chan, "");
1383                  else {
1384                         ast_log(LOG_WARNING, "ast_streamfile of %s failed on %s\n", "pbx-invalidpark", chan->name);
1385                         dres = 0;
1386                 }
1387                 if (option_verbose > 2) 
1388                         ast_verbose(VERBOSE_PREFIX_3 "Channel %s tried to talk to nonexistent parked call %d\n", chan->name, park);
1389                 res = -1;
1390         }
1391         LOCAL_USER_REMOVE(u);
1392         return res;
1393 }
1394
1395 static int handle_showfeatures(int fd, int argc, char *argv[])
1396 {
1397         int i;
1398         int fcount;
1399         char format[] = "%-25s %-7s %-7s\n";
1400
1401         ast_cli(fd, format, "Feature", "Default", "Current");
1402         ast_cli(fd, format, "-------", "-------", "-------");
1403
1404         ast_cli(fd, format, "Pickup", "*8", ast_pickup_ext());          /* default hardcoded above, so we'll hardcode it here */
1405
1406         fcount = sizeof(builtin_features) / sizeof(builtin_features[0]);
1407
1408         for (i = 0; i < fcount; i++)
1409         {
1410                 ast_cli(fd, format, builtin_features[i].fname, builtin_features[i].default_exten, builtin_features[i].exten);
1411         }
1412
1413         return RESULT_SUCCESS;
1414 }
1415
1416 static char showfeatures_help[] =
1417 "Usage: show features\n"
1418 "       Lists currently configured features.\n";
1419
1420 static struct ast_cli_entry showfeatures =
1421 { { "show", "features", NULL }, handle_showfeatures, "Lists configured features", showfeatures_help };
1422
1423 static int handle_parkedcalls(int fd, int argc, char *argv[])
1424 {
1425         struct parkeduser *cur;
1426
1427         ast_cli(fd, "%4s %25s (%-15s %-12s %-4s) %-6s \n", "Num", "Channel"
1428                 , "Context", "Extension", "Pri", "Timeout");
1429
1430         ast_mutex_lock(&parking_lock);
1431
1432         cur = parkinglot;
1433         while(cur) {
1434                 ast_cli(fd, "%4d %25s (%-15s %-12s %-4d) %6lds\n"
1435                         ,cur->parkingnum, cur->chan->name, cur->context, cur->exten
1436                         ,cur->priority, cur->start.tv_sec + (cur->parkingtime/1000) - time(NULL));
1437
1438                 cur = cur->next;
1439         }
1440
1441         ast_mutex_unlock(&parking_lock);
1442
1443         return RESULT_SUCCESS;
1444 }
1445
1446 static char showparked_help[] =
1447 "Usage: show parkedcalls\n"
1448 "       Lists currently parked calls.\n";
1449
1450 static struct ast_cli_entry showparked =
1451 { { "show", "parkedcalls", NULL }, handle_parkedcalls, "Lists parked calls", showparked_help };
1452 /* Dump lot status */
1453 static int manager_parking_status( struct mansession *s, struct message *m )
1454 {
1455         struct parkeduser *cur;
1456         char *id = astman_get_header(m,"ActionID");
1457         char idText[256] = "";
1458
1459         if (id && !ast_strlen_zero(id))
1460                 snprintf(idText,256,"ActionID: %s\r\n",id);
1461
1462         astman_send_ack(s, m, "Parked calls will follow");
1463
1464         ast_mutex_lock(&parking_lock);
1465
1466         cur=parkinglot;
1467         while(cur) {
1468                         ast_mutex_lock(&s->lock);
1469                 ast_cli(s->fd, "Event: ParkedCall\r\n"
1470                         "Exten: %d\r\n"
1471                         "Channel: %s\r\n"
1472                         "Timeout: %ld\r\n"
1473                         "CallerID: %s\r\n"
1474                         "CallerIDName: %s\r\n"
1475                         "%s"
1476                         "\r\n"
1477                         ,cur->parkingnum, cur->chan->name
1478                         ,(long)cur->start.tv_sec + (long)(cur->parkingtime/1000) - (long)time(NULL)
1479                         ,(cur->chan->cid.cid_num ? cur->chan->cid.cid_num : "")
1480                         ,(cur->chan->cid.cid_name ? cur->chan->cid.cid_name : "")
1481                         ,idText);
1482                         ast_mutex_unlock(&s->lock);
1483
1484             cur = cur->next;
1485         }
1486
1487         ast_cli(s->fd,
1488         "Event: ParkedCallsComplete\r\n"
1489         "%s"
1490         "\r\n",idText);
1491
1492         ast_mutex_unlock(&parking_lock);
1493
1494         return RESULT_SUCCESS;
1495 }
1496
1497
1498 static int load_config(void) 
1499 {
1500         int start = 0, end = 0;
1501         struct ast_context *con = NULL;
1502         struct ast_config *cfg = NULL;
1503         struct ast_variable *var = NULL;
1504         
1505         transferdigittimeout = DEFAULT_TRANSFER_DIGIT_TIMEOUT;
1506         featuredigittimeout = DEFAULT_FEATURE_DIGIT_TIMEOUT;
1507
1508         cfg = ast_config_load("features.conf");
1509         if (!cfg) {
1510                 cfg = ast_config_load("parking.conf");
1511                 if (cfg)
1512                         ast_log(LOG_NOTICE, "parking.conf is deprecated in favor of 'features.conf'.  Please rename it.\n");
1513         }
1514         if (cfg) {
1515                 var = ast_variable_browse(cfg, "general");
1516                 while(var) {
1517                         if (!strcasecmp(var->name, "parkext")) {
1518                                 strncpy(parking_ext, var->value, sizeof(parking_ext) - 1);
1519                         } else if (!strcasecmp(var->name, "context")) {
1520                                 strncpy(parking_con, var->value, sizeof(parking_con) - 1);
1521                         } else if (!strcasecmp(var->name, "parkingtime")) {
1522                                 if ((sscanf(var->value, "%d", &parkingtime) != 1) || (parkingtime < 1)) {
1523                                         ast_log(LOG_WARNING, "%s is not a valid parkingtime\n", var->value);
1524                                         parkingtime = DEFAULT_PARK_TIME;
1525                                 } else
1526                                         parkingtime = parkingtime * 1000;
1527                         } else if (!strcasecmp(var->name, "parkpos")) {
1528                                 if (sscanf(var->value, "%i-%i", &start, &end) != 2) {
1529                                         ast_log(LOG_WARNING, "Format for parking positions is a-b, where a and b are numbers at line %d of parking.conf\n", var->lineno);
1530                                 } else {
1531                                         parking_start = start;
1532                                         parking_stop = end;
1533                                 }
1534                         } else if (!strcasecmp(var->name, "adsipark")) {
1535                                 adsipark = ast_true(var->value);
1536                         } else if (!strcasecmp(var->name, "transferdigittimeout")) {
1537                                 if ((sscanf(var->value, "%d", &transferdigittimeout) != 1) || (transferdigittimeout < 1)) {
1538                                         ast_log(LOG_WARNING, "%s is not a valid transferdigittimeout\n", var->value);
1539                                         transferdigittimeout = DEFAULT_TRANSFER_DIGIT_TIMEOUT;
1540                                 } else
1541                                         transferdigittimeout = transferdigittimeout * 1000;
1542                         } else if (!strcasecmp(var->name, "featuredigittimeout")) {
1543                                 if ((sscanf(var->value, "%d", &featuredigittimeout) != 1) || (featuredigittimeout < 1)) {
1544                                         ast_log(LOG_WARNING, "%s is not a valid featuredigittimeout\n", var->value);
1545                                         featuredigittimeout = DEFAULT_FEATURE_DIGIT_TIMEOUT;
1546                                 }
1547                         } else if (!strcasecmp(var->name, "courtesytone")) {
1548                                 strncpy(courtesytone, var->value, sizeof(courtesytone) - 1);
1549                         } else if (!strcasecmp(var->name, "xfersound")) {
1550                                 strncpy(xfersound, var->value, sizeof(xfersound) - 1);
1551                         } else if (!strcasecmp(var->name, "xferfailsound")) {
1552                                 strncpy(xferfailsound, var->value, sizeof(xferfailsound) - 1);
1553                         } else if (!strcasecmp(var->name, "pickupexten")) {
1554                                 strncpy(pickup_ext, var->value, sizeof(pickup_ext) - 1);
1555                         }
1556                         var = var->next;
1557                 }
1558                 unmap_features();
1559                 var = ast_variable_browse(cfg, "featuremap");
1560                 while(var) {
1561                         if (remap_feature(var->name, var->value))
1562                                 ast_log(LOG_NOTICE, "Unknown feature '%s'\n", var->name);
1563                         var = var->next;
1564                 }
1565                 ast_config_destroy(cfg);
1566         }
1567         
1568         if (con)
1569                 ast_context_remove_extension2(con, ast_parking_ext(), 1, registrar);
1570         
1571         if (!(con = ast_context_find(parking_con))) {
1572                 if (!(con = ast_context_create(NULL, parking_con, registrar))) {
1573                         ast_log(LOG_ERROR, "Parking context '%s' does not exist and unable to create\n", parking_con);
1574                         return -1;
1575                 }
1576         }
1577         return ast_add_extension2(con, 1, ast_parking_ext(), 1, NULL, NULL, parkcall, strdup(""),free, registrar);
1578 }
1579
1580 int reload(void) {
1581         return load_config();
1582 }
1583
1584 int load_module(void)
1585 {
1586         int res;
1587         if ((res = load_config()))
1588                 return res;
1589         ast_cli_register(&showparked);
1590         ast_cli_register(&showfeatures);
1591         ast_pthread_create(&parking_thread, NULL, do_parking_thread, NULL);
1592         res = ast_register_application(parkedcall, park_exec, synopsis, descrip);
1593         if (!res)
1594                 res = ast_register_application(parkcall, park_call_exec, synopsis2, descrip2);
1595         if (!res) {
1596                 ast_manager_register( "ParkedCalls", 0, manager_parking_status, "List parked calls" );
1597         }
1598         return res;
1599 }
1600
1601 int ast_pickup_call(struct ast_channel *chan)
1602 {
1603         struct ast_channel *cur;
1604         int res = -1;
1605         cur = ast_channel_walk_locked(NULL);
1606         while(cur) {
1607                 if (!cur->pbx && 
1608                         (cur != chan) &&
1609                         (chan->pickupgroup & cur->callgroup) &&
1610                         ((cur->_state == AST_STATE_RINGING) ||
1611                          (cur->_state == AST_STATE_RING))) {
1612                                 break;
1613                 }
1614                 ast_mutex_unlock(&cur->lock);
1615                 cur = ast_channel_walk_locked(cur);
1616         }
1617         if (cur) {
1618                 if (option_debug)
1619                         ast_log(LOG_DEBUG, "Call pickup on chan '%s' by '%s'\n",cur->name, chan->name);
1620                 res = ast_answer(chan);
1621                 if (res)
1622                         ast_log(LOG_WARNING, "Unable to answer '%s'\n", chan->name);
1623                 res = ast_queue_control(chan, AST_CONTROL_ANSWER);
1624                 if (res)
1625                         ast_log(LOG_WARNING, "Unable to queue answer on '%s'\n", chan->name);
1626                 res = ast_channel_masquerade(cur, chan);
1627                 if (res)
1628                         ast_log(LOG_WARNING, "Unable to masquerade '%s' into '%s'\n", chan->name, cur->name);           /* Done */
1629                 ast_mutex_unlock(&cur->lock);
1630         } else  {
1631                 if (option_debug)
1632                         ast_log(LOG_DEBUG, "No call pickup possible...\n");
1633         }
1634         return res;
1635 }
1636
1637 int unload_module(void)
1638 {
1639         STANDARD_HANGUP_LOCALUSERS;
1640
1641         ast_manager_unregister( "ParkedCalls" );
1642         ast_cli_unregister(&showfeatures);
1643         ast_cli_unregister(&showparked);
1644         ast_unregister_application(parkcall);
1645         return ast_unregister_application(parkedcall);
1646 }
1647
1648 char *description(void)
1649 {
1650         return "Call Parking Resource";
1651 }
1652
1653 int usecount(void)
1654 {
1655         /* Never allow parking to be unloaded because it will
1656            unresolve needed symbols in the dialer */
1657 #if 0
1658         int res;
1659         STANDARD_USECOUNT(res);
1660         return res;
1661 #else
1662         return 1;
1663 #endif
1664 }
1665
1666 char *key()
1667 {
1668         return ASTERISK_GPL_KEY;
1669 }