Merge gryn's transfer digit timeout patch (bug #2184)
[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, Mark Spencer
7  *
8  * Mark Spencer <markster@linux-support.net>
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/channel_pvt.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 <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 #define DEFAULT_PARK_TIME 45000
41 #define DEFAULT_TRANSFER_DIGIT_TIMEOUT 3000
42
43 static char *parkedcall = "ParkedCall";
44
45 /* No more than 45 seconds parked before you do something with them */
46 static int parkingtime = DEFAULT_PARK_TIME;
47
48 /* Context for which parking is made accessible */
49 static char parking_con[AST_MAX_EXTENSION] = "parkedcalls";
50
51 /* Extension you type to park the call */
52 static char parking_ext[AST_MAX_EXTENSION] = "700";
53
54 static char pickup_ext[AST_MAX_EXTENSION] = "*8";
55
56 /* First available extension for parking */
57 static int parking_start = 701;
58
59 /* Last available extension for parking */
60 static int parking_stop = 750;
61
62 static int transferdigittimeout = DEFAULT_TRANSFER_DIGIT_TIMEOUT;
63
64 /* Registrar for operations */
65 static char *registrar = "res_parking";
66
67 static char *synopsis = "Answer a parked call";
68
69 static char *descrip = "ParkedCall(exten):"
70 "Used to connect to a parked call.  This Application is always\n"
71 "registered internally and does not need to be explicitly added\n"
72 "into the dialplan, although you should include the 'parkedcalls'\n"
73 "context.\n";
74
75 struct parkeduser {
76         struct ast_channel *chan;
77         struct timeval start;
78         int parkingnum;
79         /* Where to go if our parking time expires */
80         char context[AST_MAX_EXTENSION];
81         char exten[AST_MAX_EXTENSION];
82         int priority;
83         int parkingtime;
84         struct parkeduser *next;
85 };
86
87 static struct parkeduser *parkinglot;
88
89 AST_MUTEX_DEFINE_STATIC(parking_lock);
90
91 static pthread_t parking_thread;
92
93 STANDARD_LOCAL_USER;
94
95 LOCAL_USER_DECL;
96
97 char *ast_parking_ext(void)
98 {
99         return parking_ext;
100 }
101
102 char *ast_pickup_ext(void)
103 {
104         return pickup_ext;
105 }
106
107 int ast_park_call(struct ast_channel *chan, struct ast_channel *peer, int timeout, int *extout)
108 {
109         /* We put the user in the parking list, then wake up the parking thread to be sure it looks
110            after these channels too */
111         struct parkeduser *pu, *cur;
112         int x;
113         pu = malloc(sizeof(struct parkeduser));
114         if (pu) {
115                 ast_mutex_lock(&parking_lock);
116                 for (x=parking_start;x<=parking_stop;x++) {
117                         cur = parkinglot;
118                         while(cur) {
119                                 if (cur->parkingnum == x) 
120                                         break;
121                                 cur = cur->next;
122                         }
123                         if (!cur)
124                                 break;
125                 }
126                 if (x <= parking_stop) {
127                         chan->appl = "Parked Call";
128                         chan->data = NULL; 
129
130                         pu->chan = chan;
131                         /* Start music on hold */
132                         ast_moh_start(pu->chan, NULL);
133                         gettimeofday(&pu->start, NULL);
134                         pu->parkingnum = x;
135                         if (timeout > 0)
136                                 pu->parkingtime = timeout;
137                         else
138                                 pu->parkingtime = parkingtime;
139                         if (extout)
140                                 *extout = x;
141                         /* Remember what had been dialed, so that if the parking
142                            expires, we try to come back to the same place */
143                         if (strlen(chan->macrocontext))
144                                 strncpy(pu->context, chan->macrocontext, sizeof(pu->context)-1);
145                         else
146                                 strncpy(pu->context, chan->context, sizeof(pu->context)-1);
147                         if (strlen(chan->macroexten))
148                                 strncpy(pu->exten, chan->macroexten, sizeof(pu->exten)-1);
149                         else
150                                 strncpy(pu->exten, chan->exten, sizeof(pu->exten)-1);
151                         if (chan->macropriority)
152                                 pu->priority = chan->macropriority;
153                         else
154                                 pu->priority = chan->priority;
155                         pu->next = parkinglot;
156                         parkinglot = pu;
157                         ast_mutex_unlock(&parking_lock);
158                         /* Wake up the (presumably select()ing) thread */
159                         pthread_kill(parking_thread, SIGURG);
160                         if (option_verbose > 1) 
161                                 ast_verbose(VERBOSE_PREFIX_2 "Parked %s on %d\n", pu->chan->name, pu->parkingnum);
162
163                         manager_event(EVENT_FLAG_CALL, "ParkedCall",
164                                 "Exten: %d\r\n"
165                                 "Channel: %s\r\n"
166                                 "From: %s\r\n"
167                                 "Timeout: %ld\r\n"
168                                 "CallerID: %s\r\n"
169                                 ,pu->parkingnum, pu->chan->name, peer->name
170                                 ,(long)pu->start.tv_sec + (long)(pu->parkingtime/1000) - (long)time(NULL)
171                                 ,(pu->chan->callerid ? pu->chan->callerid : "")
172                                 );
173
174                         if (peer)
175                                 ast_say_digits(peer, pu->parkingnum, "", peer->language);
176                         return 0;
177                 } else {
178                         ast_log(LOG_WARNING, "No more parking spaces\n");
179                         free(pu);
180                         ast_mutex_unlock(&parking_lock);
181                         return -1;
182                 }
183         } else {
184                 ast_log(LOG_WARNING, "Out of memory\n");
185                 return -1;
186         }
187         return 0;
188 }
189
190 int ast_masq_park_call(struct ast_channel *rchan, struct ast_channel *peer, int timeout, int *extout)
191 {
192         struct ast_channel *chan;
193         struct ast_frame *f;
194         /* Make a new, fake channel that we'll use to masquerade in the real one */
195         chan = ast_channel_alloc(0);
196         if (chan) {
197                 /* Let us keep track of the channel name */
198                 snprintf(chan->name, sizeof (chan->name), "Parked/%s",rchan->name);
199                 /* Make formats okay */
200                 chan->readformat = rchan->readformat;
201                 chan->writeformat = rchan->writeformat;
202                 ast_channel_masquerade(chan, rchan);
203                 /* Setup the extensions and such */
204                 strncpy(chan->context, rchan->context, sizeof(chan->context) - 1);
205                 strncpy(chan->exten, rchan->exten, sizeof(chan->exten) - 1);
206                 chan->priority = rchan->priority;
207                 /* Make the masq execute */
208                 f = ast_read(chan);
209                 if (f)
210                         ast_frfree(f);
211                 ast_park_call(chan, peer, timeout, extout);
212         } else {
213                 ast_log(LOG_WARNING, "Unable to create parked channel\n");
214                 return -1;
215         }
216         return 0;
217 }
218
219 int ast_bridge_call(struct ast_channel *chan,struct ast_channel *peer,struct ast_bridge_config *config)
220 {
221         /* Copy voice back and forth between the two channels.  Give the peer
222            the ability to transfer calls with '#<extension' syntax. */
223         int len;
224         struct ast_frame *f;
225         struct ast_channel *who;
226         char newext[256], *ptr;
227         int res;
228         struct ast_option_header *aoh;
229         struct ast_channel *transferer;
230         struct ast_channel *transferee;
231         char *transferer_real_context;
232         int allowdisconnect_in,allowdisconnect_out,allowredirect_in,allowredirect_out;
233
234         allowdisconnect_in = config->allowdisconnect_in;
235         allowdisconnect_out = config->allowdisconnect_out;
236         allowredirect_in = config->allowredirect_in;
237         allowredirect_out = config->allowredirect_out;
238
239         /* Answer if need be */
240         if (ast_answer(chan))
241                 return -1;
242         peer->appl = "Bridged Call";
243         peer->data = chan->name;
244         /* copy the userfield from the B-leg to A-leg if applicable */
245         if (chan->cdr && peer->cdr && strlen(peer->cdr->userfield)) {
246                 char tmp[256];
247                 if (strlen(chan->cdr->userfield)) {
248                         snprintf(tmp, sizeof(tmp), "%s;%s",chan->cdr->userfield, peer->cdr->userfield);
249                         ast_cdr_appenduserfield(chan, tmp);
250                 } else
251                         ast_cdr_setuserfield(chan, peer->cdr->userfield);
252                 /* free the peer's cdr without ast_cdr_free complaining */
253                 free(peer->cdr);
254                 peer->cdr = NULL;
255         }
256         for (;;) {
257           res = ast_channel_bridge(chan,peer,config,&f, &who);
258                 if (res < 0) {
259                         ast_log(LOG_WARNING, "Bridge failed on channels %s and %s\n", chan->name, peer->name);
260                         return -1;
261                 }
262                 
263                 if (!f || ((f->frametype == AST_FRAME_CONTROL) && ((f->subclass == AST_CONTROL_HANGUP) || (f->subclass == AST_CONTROL_BUSY) || 
264                         (f->subclass == AST_CONTROL_CONGESTION)))) {
265                                 res = -1;
266                                 break;
267                 }
268                 if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_RINGING)) {
269                         if (who == chan)
270                                 ast_indicate(peer, AST_CONTROL_RINGING);
271                         else
272                                 ast_indicate(chan, AST_CONTROL_RINGING);
273                 }
274                 if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == -1)) {
275                         if (who == chan)
276                                 ast_indicate(peer, -1);
277                         else
278                                 ast_indicate(chan, -1);
279                 }
280                 if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_OPTION)) {
281                         aoh = f->data;
282                         /* Forward option Requests */
283                         if (aoh && (aoh->flag == AST_OPTION_FLAG_REQUEST)) {
284                                 if (who == chan)
285                                         ast_channel_setoption(peer, ntohs(aoh->option), aoh->data, f->datalen - sizeof(struct ast_option_header), 0);
286                                 else
287                                         ast_channel_setoption(chan, ntohs(aoh->option), aoh->data, f->datalen - sizeof(struct ast_option_header), 0);
288                         }
289                 }
290                 /* check for '*', if we find it it's time to disconnect */
291                 if (f && (f->frametype == AST_FRAME_DTMF) &&
292                         (((who == chan) && allowdisconnect_out) || ((who == peer) && allowdisconnect_in)) &&
293                         (f->subclass == '*')) {
294                         
295                         if (option_verbose > 3)
296                                 ast_verbose(VERBOSE_PREFIX_3 "User hit %c to disconnect call.\n", f->subclass);
297                         res = -1;
298                         break;
299                 }
300
301                 if ((f->frametype == AST_FRAME_DTMF) &&
302                         ((allowredirect_in && who == peer) || (allowredirect_out && who == chan)) &&
303                         (f->subclass == '#')) {
304                                 if(allowredirect_in &&  who == peer) {
305                                         transferer = peer;
306                                         transferee = chan;
307                                 }
308                                 else {
309                                         transferer = chan;
310                                         transferee = peer;
311                                 }
312                                 if(!(transferer_real_context=pbx_builtin_getvar_helper(transferee, "TRANSFER_CONTEXT")) &&
313                                    !(transferer_real_context=pbx_builtin_getvar_helper(transferer, "TRANSFER_CONTEXT"))) {
314                                         /* Use the non-macro context to transfer the call */
315                                         if(strlen(transferer->macrocontext))
316                                                 transferer_real_context=transferer->macrocontext;
317                                         else
318                                                 transferer_real_context=transferer->context;
319                                 }
320                                 /* Start autoservice on chan while we talk
321                                    to the originator */
322                                 ast_autoservice_start(transferee);
323                                 ast_moh_start(transferee, NULL);
324
325                                 memset(newext, 0, sizeof(newext));
326                                 ptr = newext;
327
328                                         /* Transfer */
329                                 if ((res=ast_streamfile(transferer, "pbx-transfer", transferer->language))) {
330                                         ast_moh_stop(transferee);
331                                         ast_autoservice_stop(transferee);
332                                         break;
333                                 }
334                                 if ((res=ast_waitstream(transferer, AST_DIGIT_ANY)) < 0) {
335                                         ast_moh_stop(transferee);
336                                         ast_autoservice_stop(transferee);
337                                         break;
338                                 }
339                                 ast_stopstream(transferer);
340                                 if (res > 0) {
341                                         /* If they've typed a digit already, handle it */
342                                         newext[0] = res;
343                                         ptr++;
344                                         len --;
345                                 }
346                                 res = 0;
347                                 while(strlen(newext) < sizeof(newext) - 1) {
348                                         res = ast_waitfordigit(transferer, transferdigittimeout);
349                                         if (res < 1) 
350                                                 break;
351                                         if (res == '#')
352                                                 break;
353                                         *(ptr++) = res;
354                                         if (!ast_matchmore_extension(transferer, transferer_real_context
355                                                                 , newext, 1, transferer->callerid)) {
356                                                 break;
357                                         }
358                                 }
359
360                                 if (res < 0) {
361                                         ast_moh_stop(transferee);
362                                         ast_autoservice_stop(transferee);
363                                         break;
364                                 }
365                                 if (!strcmp(newext, ast_parking_ext())) {
366                                         ast_moh_stop(transferee);
367
368                                         if (ast_autoservice_stop(transferee))
369                                                 res = -1;
370                                         else if (!ast_park_call(transferee, transferer, 0, NULL)) {
371                                                 /* We return non-zero, but tell the PBX not to hang the channel when
372                                                    the thread dies -- We have to be careful now though.  We are responsible for 
373                                                    hanging up the channel, else it will never be hung up! */
374
375                                                 if(transferer==peer)
376                                                         res=AST_PBX_KEEPALIVE;
377                                                 else
378                                                         res=AST_PBX_NO_HANGUP_PEER;
379                                                 break;
380                                         } else {
381                                                 ast_log(LOG_WARNING, "Unable to park call %s\n", transferee->name);
382                                         }
383                                         /* XXX Maybe we should have another message here instead of invalid extension XXX */
384                                 } else if (ast_exists_extension(transferee, transferer_real_context, newext, 1, transferer->callerid)) {
385                                         ast_moh_stop(transferee);
386                                         res=ast_autoservice_stop(transferee);
387                                         if (!transferee->pbx) {
388                                                 /* Doh!  Use our handy async_goto funcitons */
389                                                 if (option_verbose > 2) 
390                                                         ast_verbose(VERBOSE_PREFIX_3 "Transferring %s to '%s' (context %s) priority 1\n"
391                                                                 ,transferee->name, newext, transferer_real_context);
392                                                 if (ast_async_goto(transferee, transferer_real_context, newext, 1))
393                                                         ast_log(LOG_WARNING, "Async goto fialed :(\n");
394                                                 res = -1;
395                                         } else {
396                                                 /* Set the channel's new extension, since it exists, using transferer context */
397                                                 strncpy(transferee->exten, newext, sizeof(transferee->exten)-1);
398                                                 strncpy(transferee->context, transferer_real_context, sizeof(transferee->context)-1);
399                                                 transferee->priority = 0;
400                                                 ast_frfree(f);
401                                         }
402                                         break;
403                                 } else {
404                                         if (option_verbose > 2) 
405                                                 ast_verbose(VERBOSE_PREFIX_3 "Unable to find extension '%s' in context '%s'\n", newext, transferer_real_context);
406                                 }
407                                 res = ast_streamfile(transferer, "pbx-invalid", transferee->language);
408                                 if (res) {
409                                         ast_moh_stop(transferee);
410                                         ast_autoservice_stop(transferee);
411                                         break;
412                                 }
413                                 res = ast_waitstream(transferer, AST_DIGIT_ANY);
414                                 ast_stopstream(transferer);
415                                 ast_moh_stop(transferee);
416                                 res = ast_autoservice_stop(transferee);
417                                 if (res) {
418                                         if (option_verbose > 1)
419                                                 ast_verbose(VERBOSE_PREFIX_2 "Hungup during autoservice stop on '%s'\n", transferee->name);
420                                 }
421                         } else {
422             if (f && (f->frametype == AST_FRAME_DTMF)) {
423                   if (who == peer)
424                         ast_write(chan, f);
425                   else
426                         ast_write(peer, f);
427             }            
428 #if 1
429                                 ast_log(LOG_DEBUG, "Read from %s (%d,%d)\n", who->name, f->frametype, f->subclass);
430 #endif
431                         }
432          if (f)
433                ast_frfree(f);
434         }
435         return res;
436 }
437
438 static void *do_parking_thread(void *ignore)
439 {
440         int ms, tms, max;
441         struct parkeduser *pu, *pl, *pt = NULL;
442         struct timeval tv;
443         struct ast_frame *f;
444         int x;
445         fd_set rfds, efds;
446         fd_set nrfds, nefds;
447         FD_ZERO(&rfds);
448         FD_ZERO(&efds);
449         for (;;) {
450                 ms = -1;
451                 max = -1;
452                 ast_mutex_lock(&parking_lock);
453                 pl = NULL;
454                 pu = parkinglot;
455                 gettimeofday(&tv, NULL);
456                 FD_ZERO(&nrfds);
457                 FD_ZERO(&nefds);
458                 while(pu) {
459                         tms = (tv.tv_sec - pu->start.tv_sec) * 1000 + (tv.tv_usec - pu->start.tv_usec) / 1000;
460                         if (tms > pu->parkingtime) {
461                                 /* They've been waiting too long, send them back to where they came.  Theoretically they
462                                    should have their original extensions and such, but we copy to be on the safe side */
463                                 strncpy(pu->chan->exten, pu->exten, sizeof(pu->chan->exten)-1);
464                                 strncpy(pu->chan->context, pu->context, sizeof(pu->chan->context)-1);
465                                 pu->chan->priority = pu->priority;
466                                 /* Stop music on hold */
467                                 ast_moh_stop(pu->chan);
468                                 /* Start up the PBX, or hang them up */
469                                 if (ast_pbx_start(pu->chan))  {
470                                         ast_log(LOG_WARNING, "Unable to restart the PBX for user on '%s', hanging them up...\n", pu->chan->name);
471                                         ast_hangup(pu->chan);
472                                 }
473                                 /* And take them out of the parking lot */
474                                 if (pl) 
475                                         pl->next = pu->next;
476                                 else
477                                         parkinglot = pu->next;
478                                 pt = pu;
479                                 pu = pu->next;
480                                 free(pt);
481                         } else {
482                                 for (x=0;x<AST_MAX_FDS;x++) {
483                                         if ((pu->chan->fds[x] > -1) && (FD_ISSET(pu->chan->fds[x], &rfds) || FD_ISSET(pu->chan->fds[x], &efds))) {
484                                                 if (FD_ISSET(pu->chan->fds[x], &efds))
485                                                         pu->chan->exception = 1;
486                                                 pu->chan->fdno = x;
487                                                 /* See if they need servicing */
488                                                 f = ast_read(pu->chan);
489                                                 if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass ==  AST_CONTROL_HANGUP))) {
490                                                         /* There's a problem, hang them up*/
491                                                         if (option_verbose > 1) 
492                                                                 ast_verbose(VERBOSE_PREFIX_2 "%s got tired of being parked\n", pu->chan->name);
493                                                         ast_hangup(pu->chan);
494                                                         /* And take them out of the parking lot */
495                                                         if (pl) 
496                                                                 pl->next = pu->next;
497                                                         else
498                                                                 parkinglot = pu->next;
499                                                         pt = pu;
500                                                         pu = pu->next;
501                                                         free(pt);
502                                                         break;
503                                                 } else {
504                                                         /* XXX Maybe we could do something with packets, like dial "0" for operator or something XXX */
505                                                         ast_frfree(f);
506                                                         goto std;       /* XXX Ick: jumping into an else statement??? XXX */
507                                                 }
508                                         }
509                                 }
510                                 if (x >= AST_MAX_FDS) {
511 std:                                    for (x=0;x<AST_MAX_FDS;x++) {
512                                                 /* Keep this one for next one */
513                                                 if (pu->chan->fds[x] > -1) {
514                                                         FD_SET(pu->chan->fds[x], &nrfds);
515                                                         FD_SET(pu->chan->fds[x], &nefds);
516                                                         if (pu->chan->fds[x] > max)
517                                                                 max = pu->chan->fds[x];
518                                                 }
519                                         }
520                                         /* Keep track of our longest wait */
521                                         if ((tms < ms) || (ms < 0))
522                                                 ms = tms;
523                                         pl = pu;
524                                         pu = pu->next;
525                                 }
526                         }
527                 }
528                 ast_mutex_unlock(&parking_lock);
529                 rfds = nrfds;
530                 efds = nefds;
531                 tv.tv_sec = ms / 1000;
532                 tv.tv_usec = (ms % 1000) * 1000;
533                 /* Wait for something to happen */
534                 ast_select(max + 1, &rfds, NULL, &efds, (ms > -1) ? &tv : NULL);
535                 pthread_testcancel();
536         }
537         return NULL;    /* Never reached */
538 }
539
540 static int park_exec(struct ast_channel *chan, void *data)
541 {
542         int res=0;
543         struct localuser *u;
544         struct ast_channel *peer=NULL;
545         struct parkeduser *pu, *pl=NULL;
546         int park;
547         int dres;
548         struct ast_bridge_config config;
549
550         if (!data) {
551                 ast_log(LOG_WARNING, "Park requires an argument (extension number)\n");
552                 return -1;
553         }
554         LOCAL_USER_ADD(u);
555         park = atoi((char *)data);
556         ast_mutex_lock(&parking_lock);
557         pu = parkinglot;
558         while(pu) {
559                 if (pu->parkingnum == park) {
560                         if (pl)
561                                 pl->next = pu->next;
562                         else
563                                 parkinglot = pu->next;
564                         break;
565                 }
566                 pl = pu;
567                 pu = pu->next;
568         }
569         ast_mutex_unlock(&parking_lock);
570         if (pu) {
571                 peer = pu->chan;
572                 free(pu);
573         }
574         /* JK02: it helps to answer the channel if not already up */
575         if (chan->_state != AST_STATE_UP) {
576                 ast_answer(chan);
577         }
578
579         if (peer) {
580                 ast_moh_stop(peer);
581                 res = ast_channel_make_compatible(chan, peer);
582                 if (res < 0) {
583                         ast_log(LOG_WARNING, "Could not make channels %s and %s compatible for bridge\n", chan->name, peer->name);
584                         ast_hangup(peer);
585                         return -1;
586                 }
587                 /* This runs sorta backwards, since we give the incoming channel control, as if it
588                    were the person called. */
589                 if (option_verbose > 2) 
590                         ast_verbose(VERBOSE_PREFIX_3 "Channel %s connected to parked call %d\n", chan->name, park);
591
592                 memset(&config,0,sizeof(struct ast_bridge_config));
593                 config.allowredirect_in = 1;
594                 config.allowredirect_out = 1;
595                 config.allowdisconnect_out = 0;
596                 config.allowdisconnect_in = 0;
597                 config.timelimit = 0;
598                 config.play_warning = 0;
599                 config.warning_freq = 0;
600                 config.warning_sound=NULL;
601                 res = ast_bridge_call(chan,peer,&config);
602
603                 /* Simulate the PBX hanging up */
604                 if (res != AST_PBX_NO_HANGUP_PEER)
605                         ast_hangup(peer);
606                 return res;
607         } else {
608                 /* XXX Play a message XXX */
609           dres = ast_streamfile(chan, "pbx-invalidpark", chan->language);
610           if (!dres)
611             dres = ast_waitstream(chan, "");
612           else {
613             ast_log(LOG_WARNING, "ast_streamfile of %s failed on %s\n", "pbx-invalidpark", chan->name);
614             dres = 0;
615           }
616                 if (option_verbose > 2) 
617                         ast_verbose(VERBOSE_PREFIX_3 "Channel %s tried to talk to non-existant parked call %d\n", chan->name, park);
618                 res = -1;
619         }
620         LOCAL_USER_REMOVE(u);
621         return res;
622 }
623
624 static int handle_parkedcalls(int fd, int argc, char *argv[])
625 {
626         struct parkeduser *cur;
627
628         ast_cli(fd, "%4s %25s (%-15s %-12s %-4s) %-6s \n", "Num", "Channel"
629                 , "Context", "Extension", "Pri", "Timeout");
630
631         ast_mutex_lock(&parking_lock);
632
633         cur=parkinglot;
634         while(cur) {
635                 ast_cli(fd, "%4d %25s (%-15s %-12s %-4d) %6lds\n"
636                         ,cur->parkingnum, cur->chan->name, cur->context, cur->exten
637                         ,cur->priority, cur->start.tv_sec + (cur->parkingtime/1000) - time(NULL));
638
639                 cur = cur->next;
640         }
641
642         ast_mutex_unlock(&parking_lock);
643
644         return RESULT_SUCCESS;
645 }
646
647 static char showparked_help[] =
648 "Usage: show parkedcalls\n"
649 "       Lists currently parked calls.\n";
650
651 static struct ast_cli_entry showparked =
652 { { "show", "parkedcalls", NULL }, handle_parkedcalls, "Lists parked calls", showparked_help };
653 /* Dump lot status */
654 static int manager_parking_status( struct mansession *s, struct message *m )
655 {
656         struct parkeduser *cur;
657         char *id = astman_get_header(m,"ActionID");
658         char idText[256] = "";
659
660         if (id && !ast_strlen_zero(id))
661                 snprintf(idText,256,"ActionID: %s\r\n",id);
662
663         astman_send_ack(s, m, "Parked calls will follow");
664
665         ast_mutex_lock(&parking_lock);
666
667         cur=parkinglot;
668         while(cur) {
669                 ast_cli(s->fd, "Event: ParkedCall\r\n"
670                         "Exten: %d\r\n"
671                         "Channel: %s\r\n"
672                         "Timeout: %ld\r\n"
673                         "CallerID: %s\r\n"
674                         "%s"
675                         "\r\n"
676                         ,cur->parkingnum, cur->chan->name
677                         ,(long)cur->start.tv_sec + (long)(cur->parkingtime/1000) - (long)time(NULL)
678                         ,(cur->chan->callerid ? cur->chan->callerid : "")
679                         ,idText);
680
681                 cur = cur->next;
682         }
683
684         ast_cli(s->fd,
685         "Event: ParkedCallsComplete\r\n"
686         "%s"
687         "\r\n",idText);
688
689         ast_mutex_unlock(&parking_lock);
690
691         return RESULT_SUCCESS;
692 }
693
694
695
696 int load_module(void)
697 {
698         int res;
699         int x;
700         int start, end;
701         struct ast_context *con;
702         char exten[AST_MAX_EXTENSION];
703         struct ast_config *cfg;
704         struct ast_variable *var;
705
706         ast_cli_register(&showparked);
707
708         cfg = ast_load("features.conf");
709         if (!cfg) {
710                 cfg = ast_load("parking.conf");
711                 if (cfg)
712                         ast_log(LOG_NOTICE, "parking.conf is deprecated in favor of 'features.conf'.  Please rename it.\n");
713         }
714         if (cfg) {
715                 var = ast_variable_browse(cfg, "general");
716                 while(var) {
717                         if (!strcasecmp(var->name, "parkext")) {
718                                 strncpy(parking_ext, var->value, sizeof(parking_ext) - 1);
719                         } else if (!strcasecmp(var->name, "context")) {
720                                 strncpy(parking_con, var->value, sizeof(parking_con) - 1);
721                         } else if (!strcasecmp(var->name, "parkingtime")) {
722                                 if ((sscanf(var->value, "%d", &parkingtime) != 1) || (parkingtime < 1)) {
723                                         ast_log(LOG_WARNING, "%s is not a valid parkingtime\n", var->value);
724                                         parkingtime = DEFAULT_PARK_TIME;
725                                 } else
726                                         parkingtime = parkingtime * 1000;
727                         } else if (!strcasecmp(var->name, "parkpos")) {
728                                 if (sscanf(var->value, "%i-%i", &start, &end) != 2) {
729                                         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);
730                                 } else {
731                                         parking_start = start;
732                                         parking_stop = end;
733                                 }
734                         } else if(!strcasecmp(var->name, "transferdigittimeout")) {
735                                 if ((sscanf(var->value, "%d", &transferdigittimeout) != 1) || (transferdigittimeout < 1)) {
736                                         ast_log(LOG_WARNING, "%s is not a valid transferdigittimeout\n", var->value);
737                                         transferdigittimeout = DEFAULT_TRANSFER_DIGIT_TIMEOUT;
738                                 } else
739                                         transferdigittimeout = transferdigittimeout * 1000;
740                         }
741                         var = var->next;
742                 }
743                 ast_destroy(cfg);
744         }
745         con = ast_context_find(parking_con);
746         if (!con) {
747                 con = ast_context_create(NULL,parking_con, registrar);
748                 if (!con) {
749                         ast_log(LOG_ERROR, "Parking context '%s' does not exist and unable to create\n", parking_con);
750                         return -1;
751                 }
752         }
753         for(x=parking_start; x<=parking_stop;x++) {
754                 snprintf(exten, sizeof(exten), "%d", x);
755                 ast_add_extension2(con, 1, exten, 1, NULL, parkedcall, strdup(exten), free, registrar);
756         }
757         pthread_create(&parking_thread, NULL, do_parking_thread, NULL);
758         res = ast_register_application(parkedcall, park_exec, synopsis, descrip);
759         if (!res) {
760                 ast_manager_register( "ParkedCalls", 0, manager_parking_status, "List parked calls" );
761         }
762         return res;
763 }
764
765 int ast_pickup_call(struct ast_channel *chan)
766 {
767         struct ast_channel *cur;
768         int res = -1;
769         cur = ast_channel_walk_locked(NULL);
770         while(cur) {
771                 if (!cur->pbx && 
772                         (cur != chan) &&
773                         (chan->pickupgroup & cur->callgroup) &&
774                         ((cur->_state == AST_STATE_RINGING) ||
775                          (cur->_state == AST_STATE_RING))) {
776                                 break;
777                 }
778                 ast_mutex_unlock(&cur->lock);
779                 cur = ast_channel_walk_locked(cur);
780         }
781         if (cur) {
782                 ast_log(LOG_DEBUG, "Call pickup on chan '%s' by '%s'\n",cur->name, chan->name);
783                 res = ast_answer(chan);
784                 if (res)
785                         ast_log(LOG_WARNING, "Unable to answer '%s'\n", chan->name);
786                 res = ast_queue_control(chan, AST_CONTROL_ANSWER);
787                 if (res)
788                         ast_log(LOG_WARNING, "Unable to queue answer on '%s'\n", chan->name);
789                 res = ast_channel_masquerade(cur, chan);
790                 if (res)
791                         ast_log(LOG_WARNING, "Unable to masquerade '%s' into '%s'\n", chan->name, cur->name);           /* Done */
792                 ast_mutex_unlock(&cur->lock);
793         } else  {
794                 ast_log(LOG_DEBUG, "No call pickup possible...\n");
795         }
796         return res;
797 }
798
799 int unload_module(void)
800 {
801         STANDARD_HANGUP_LOCALUSERS;
802
803         ast_manager_unregister( "ParkedCalls" );
804         ast_cli_unregister(&showparked);
805
806         return ast_unregister_application(parkedcall);
807 }
808
809 char *description(void)
810 {
811         return "Call Parking Resource";
812 }
813
814 int usecount(void)
815 {
816         /* Never allow parking to be unloaded because it will
817            unresolve needed symbols in the dialer */
818 #if 0
819         int res;
820         STANDARD_USECOUNT(res);
821         return res;
822 #else
823         return 1;
824 #endif
825 }
826
827 char *key()
828 {
829         return ASTERISK_GPL_KEY;
830 }