Fixed call parking, added separate paramater to allow/disallow call parking on
[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-2004, Digium, Inc.
7  *
8  * Mark Spencer <markster@digium.com>
9  *
10  * This program is free software, distributed under the terms of
11  * the GNU General Public License
12  */
13
14 #include <asterisk/lock.h>
15 #include <asterisk/file.h>
16 #include <asterisk/logger.h>
17 #include <asterisk/channel.h>
18 #include <asterisk/pbx.h>
19 #include <asterisk/options.h>
20 #include <asterisk/module.h>
21 #include <asterisk/translate.h>
22 #include <asterisk/say.h>
23 #include <asterisk/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 <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
45 static char *parkedcall = "ParkedCall";
46
47 /* No more than 45 seconds parked before you do something with them */
48 static int parkingtime = DEFAULT_PARK_TIME;
49
50 /* Context for which parking is made accessible */
51 static char parking_con[AST_MAX_EXTENSION] = "parkedcalls";
52
53 /* Context for dialback for parking (KLUDGE) */
54 static char parking_con_dial[AST_MAX_EXTENSION] = "park-dial";
55
56 /* Extension you type to park the call */
57 static char parking_ext[AST_MAX_EXTENSION] = "700";
58
59 static char pickup_ext[AST_MAX_EXTENSION] = "*8";
60
61 /* First available extension for parking */
62 static int parking_start = 701;
63
64 /* Last available extension for parking */
65 static int parking_stop = 750;
66
67 static int adsipark = 0;
68
69 static int transferdigittimeout = DEFAULT_TRANSFER_DIGIT_TIMEOUT;
70
71 /* Default courtesy tone played when party joins conference */
72 static char courtesytone[256] = "";
73
74 /* Registrar for operations */
75 static char *registrar = "res_features";
76
77 static char *synopsis = "Answer a parked call";
78
79 static char *descrip = "ParkedCall(exten):"
80 "Used to connect to a parked call.  This Application is always\n"
81 "registered internally and does not need to be explicitly added\n"
82 "into the dialplan, although you should include the 'parkedcalls'\n"
83 "context.\n";
84
85
86 static char *parkcall = "Park";
87
88 static char *synopsis2 = "Park yourself";
89
90 static char *descrip2 = "Park(exten):"
91 "Used to park yourself (typically in combination with a supervised\n"
92 "transfer to know the parking space.  This Application is always\n"
93 "registered internally and does not need to be explicitly added\n"
94 "into the dialplan, although you should include the 'parkedcalls'\n"
95 "context.\n";
96
97 static struct ast_app *monitor_app=NULL;
98 static int monitor_ok=1;
99
100 struct parkeduser {
101         struct ast_channel *chan;
102         struct timeval start;
103         int parkingnum;
104         /* Where to go if our parking time expires */
105         char context[AST_MAX_EXTENSION];
106         char exten[AST_MAX_EXTENSION];
107         int priority;
108         int parkingtime;
109         int notquiteyet;
110         char peername[1024];
111         struct parkeduser *next;
112 };
113
114 static struct parkeduser *parkinglot;
115
116 AST_MUTEX_DEFINE_STATIC(parking_lock);
117
118 static pthread_t parking_thread;
119
120 STANDARD_LOCAL_USER;
121
122 LOCAL_USER_DECL;
123
124 char *ast_parking_ext(void)
125 {
126         return parking_ext;
127 }
128
129 char *ast_pickup_ext(void)
130 {
131         return pickup_ext;
132 }
133
134 static int adsi_announce_park(struct ast_channel *chan, int parkingnum)
135 {
136         int res;
137         int justify[5] = {ADSI_JUST_CENT, ADSI_JUST_CENT, ADSI_JUST_CENT, ADSI_JUST_CENT};
138         char tmp[256] = "";
139         char *message[5] = {NULL, NULL, NULL, NULL, NULL};
140
141         snprintf(tmp, sizeof(tmp), "Parked on %d", parkingnum);
142         message[0] = tmp;
143         res = adsi_load_session(chan, NULL, 0, 1);
144         if (res == -1) {
145                 return res;
146         }
147         return adsi_print(chan, message, justify, 1);
148 }
149
150 int ast_park_call(struct ast_channel *chan, struct ast_channel *peer, int timeout, int *extout)
151 {
152         /* We put the user in the parking list, then wake up the parking thread to be sure it looks
153            after these channels too */
154         struct parkeduser *pu, *cur;
155         int x;
156         char exten[AST_MAX_EXTENSION];
157         struct ast_context *con;
158         pu = malloc(sizeof(struct parkeduser));
159         if (pu) {
160                 memset(pu,0,sizeof(struct parkeduser));
161                 ast_mutex_lock(&parking_lock);
162                 for (x=parking_start;x<=parking_stop;x++) {
163                         cur = parkinglot;
164                         while(cur) {
165                                 if (cur->parkingnum == x) 
166                                         break;
167                                 cur = cur->next;
168                         }
169                         if (!cur)
170                                 break;
171                 }
172                 if (x <= parking_stop) {
173                         chan->appl = "Parked Call";
174                         chan->data = NULL; 
175
176                         pu->chan = chan;
177                         /* Start music on hold */
178                         if (chan != peer)
179                                 ast_moh_start(pu->chan, NULL);
180                         gettimeofday(&pu->start, NULL);
181                         pu->parkingnum = x;
182                         if (timeout > 0)
183                                 pu->parkingtime = timeout;
184                         else
185                                 pu->parkingtime = parkingtime;
186                         if (extout)
187                                 *extout = x;
188                         if (peer)
189                         {
190                                 strncpy(pu->peername,peer->name,sizeof(pu->peername) - 1);
191                         }
192                         /* Remember what had been dialed, so that if the parking
193                            expires, we try to come back to the same place */
194                         if (!ast_strlen_zero(chan->macrocontext))
195                                 strncpy(pu->context, chan->macrocontext, sizeof(pu->context)-1);
196                         else
197                                 strncpy(pu->context, chan->context, sizeof(pu->context)-1);
198                         if (!ast_strlen_zero(chan->macroexten))
199                                 strncpy(pu->exten, chan->macroexten, sizeof(pu->exten)-1);
200                         else
201                                 strncpy(pu->exten, chan->exten, sizeof(pu->exten)-1);
202                         if (chan->macropriority)
203                                 pu->priority = chan->macropriority;
204                         else
205                                 pu->priority = chan->priority;
206                         pu->next = parkinglot;
207                         parkinglot = pu;
208                         /* If parking a channel directly, don't quiet yet get parking running on it */
209                         if (peer == chan) 
210                                 pu->notquiteyet = 1;
211                         ast_mutex_unlock(&parking_lock);
212                         /* Wake up the (presumably select()ing) thread */
213                         pthread_kill(parking_thread, SIGURG);
214                         if (option_verbose > 1) 
215                                 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));
216
217                         manager_event(EVENT_FLAG_CALL, "ParkedCall",
218                                 "Exten: %d\r\n"
219                                 "Channel: %s\r\n"
220                                 "From: %s\r\n"
221                                 "Timeout: %ld\r\n"
222                                 "CallerID: %s\r\n"
223                                 "CallerIDName: %s\r\n\r\n"
224                                 ,pu->parkingnum, pu->chan->name, peer->name
225                                 ,(long)pu->start.tv_sec + (long)(pu->parkingtime/1000) - (long)time(NULL)
226                                 ,(pu->chan->cid.cid_num ? pu->chan->cid.cid_num : "")
227                                 ,(pu->chan->cid.cid_name ? pu->chan->cid.cid_name : "")
228                                 );
229
230                         if (peer) {
231                                 if (adsipark && adsi_available(peer)) {
232                                         adsi_announce_park(peer, pu->parkingnum);
233                                 }
234                                 if (adsipark && adsi_available(peer)) {
235                                         adsi_unload_session(peer);
236                                 }
237                                 if (pu->notquiteyet) {
238                                         /* Wake up parking thread if we're really done */
239                                         ast_moh_start(pu->chan, NULL);
240                                         pu->notquiteyet = 0;
241                                         pthread_kill(parking_thread, SIGURG);
242                                 }
243                         }
244                         con = ast_context_find(parking_con);
245                         if (!con) {
246                                 con = ast_context_create(NULL,parking_con, registrar);
247                                 if (!con) {
248                                         ast_log(LOG_ERROR, "Parking context '%s' does not exist and unable to create\n", parking_con);
249                                 }
250                         }
251                         if (con) {
252                                 snprintf(exten, sizeof(exten), "%d", x);
253                                 ast_add_extension2(con, 1, exten, 1, NULL, NULL, parkedcall, strdup(exten), free, registrar);
254                         }
255                         if (peer) ast_say_digits(peer, pu->parkingnum, "", peer->language);
256                         return 0;
257                 } else {
258                         ast_log(LOG_WARNING, "No more parking spaces\n");
259                         free(pu);
260                         ast_mutex_unlock(&parking_lock);
261                         return -1;
262                 }
263         } else {
264                 ast_log(LOG_WARNING, "Out of memory\n");
265                 return -1;
266         }
267         return 0;
268 }
269
270 int ast_masq_park_call(struct ast_channel *rchan, struct ast_channel *peer, int timeout, int *extout)
271 {
272         struct ast_channel *chan;
273         struct ast_frame *f;
274         /* Make a new, fake channel that we'll use to masquerade in the real one */
275         chan = ast_channel_alloc(0);
276         if (chan) {
277                 /* Let us keep track of the channel name */
278                 snprintf(chan->name, sizeof (chan->name), "Parked/%s",rchan->name);
279                 /* Make formats okay */
280                 chan->readformat = rchan->readformat;
281                 chan->writeformat = rchan->writeformat;
282                 ast_channel_masquerade(chan, rchan);
283                 /* Setup the extensions and such */
284                 strncpy(chan->context, rchan->context, sizeof(chan->context) - 1);
285                 strncpy(chan->exten, rchan->exten, sizeof(chan->exten) - 1);
286                 chan->priority = rchan->priority;
287                 /* Make the masq execute */
288                 f = ast_read(chan);
289                 if (f)
290                         ast_frfree(f);
291                 ast_park_call(chan, peer, timeout, extout);
292         } else {
293                 ast_log(LOG_WARNING, "Unable to create parked channel\n");
294                 return -1;
295         }
296         return 0;
297 }
298
299 int ast_bridge_call(struct ast_channel *chan,struct ast_channel *peer,struct ast_bridge_config *config)
300 {
301         /* Copy voice back and forth between the two channels.  Give the peer
302            the ability to transfer calls with '#<extension' syntax. */
303         int len;
304         struct ast_frame *f;
305         struct ast_channel *who;
306         char newext[256], *ptr;
307         int res;
308         int diff;
309         struct ast_option_header *aoh;
310         struct ast_channel *transferer;
311         struct ast_channel *transferee;
312         struct timeval start, end;
313         char *transferer_real_context;
314         int allowdisconnect_in,allowdisconnect_out,allowredirect_in,allowredirect_out;
315         char *monitor_exec;
316
317         if (chan && peer) {
318                 pbx_builtin_setvar_helper(chan, "BRIDGEPEER", peer->name);
319                 pbx_builtin_setvar_helper(peer, "BRIDGEPEER", chan->name);
320         } else if (chan)
321                 pbx_builtin_setvar_helper(chan, "BLINDTRANSFER", NULL);
322
323         if (monitor_ok) {
324                 if (!monitor_app) { 
325                         if (!(monitor_app = pbx_findapp("Monitor")))
326                                 monitor_ok=0;
327                 }
328                 if ((monitor_exec = pbx_builtin_getvar_helper(chan, "AUTO_MONITOR"))) 
329                         pbx_exec(chan, monitor_app, monitor_exec, 1);
330                 else if ((monitor_exec = pbx_builtin_getvar_helper(peer, "AUTO_MONITOR")))
331                         pbx_exec(peer, monitor_app, monitor_exec, 1);
332         }
333         
334         allowdisconnect_in = config->allowdisconnect_in;
335         allowdisconnect_out = config->allowdisconnect_out;
336         allowredirect_in = config->allowredirect_in;
337         allowredirect_out = config->allowredirect_out;
338         config->firstpass = 1;
339
340         /* Answer if need be */
341         if (ast_answer(chan))
342                 return -1;
343         peer->appl = "Bridged Call";
344         peer->data = chan->name;
345         /* copy the userfield from the B-leg to A-leg if applicable */
346         if (chan->cdr && peer->cdr && !ast_strlen_zero(peer->cdr->userfield)) {
347                 char tmp[256];
348                 if (!ast_strlen_zero(chan->cdr->userfield)) {
349                         snprintf(tmp, sizeof(tmp), "%s;%s",chan->cdr->userfield, peer->cdr->userfield);
350                         ast_cdr_appenduserfield(chan, tmp);
351                 } else
352                         ast_cdr_setuserfield(chan, peer->cdr->userfield);
353                 /* free the peer's cdr without ast_cdr_free complaining */
354                 free(peer->cdr);
355                 peer->cdr = NULL;
356         }
357         for (;;) {
358                 if (config->timelimit)
359                         gettimeofday(&start, NULL);
360                 res = ast_channel_bridge(chan,peer,config,&f, &who);
361                 if (config->timelimit) {
362                         /* Update time limit for next pass */
363                         gettimeofday(&end, NULL);
364                         diff = (end.tv_sec - start.tv_sec) * 1000;
365                         diff += (end.tv_usec - start.tv_usec) / 1000;
366                         config->timelimit -= diff;
367                         if (config->timelimit <=0) {
368                                 /* We ran out of time */
369                                 config->timelimit = 0;
370                                 who = chan;
371                                 if (f)
372                                         ast_frfree(f);
373                                 f = NULL;
374                                 res = 0;
375                         }
376                 }
377                 if (res < 0) {
378                         ast_log(LOG_WARNING, "Bridge failed on channels %s and %s\n", chan->name, peer->name);
379                         return -1;
380                 }
381                 
382                 if (!f || ((f->frametype == AST_FRAME_CONTROL) && ((f->subclass == AST_CONTROL_HANGUP) || (f->subclass == AST_CONTROL_BUSY) || 
383                         (f->subclass == AST_CONTROL_CONGESTION)))) {
384                                 res = -1;
385                                 break;
386                 }
387                 if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_RINGING)) {
388                         if (who == chan)
389                                 ast_indicate(peer, AST_CONTROL_RINGING);
390                         else
391                                 ast_indicate(chan, AST_CONTROL_RINGING);
392                 }
393                 if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == -1)) {
394                         if (who == chan)
395                                 ast_indicate(peer, -1);
396                         else
397                                 ast_indicate(chan, -1);
398                 }
399                 if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_FLASH)) {
400                         if (who == chan)
401                                 ast_indicate(peer, AST_CONTROL_FLASH);
402                         else
403                                 ast_indicate(chan, AST_CONTROL_FLASH);
404                 }
405                 if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_OPTION)) {
406                         aoh = f->data;
407                         /* Forward option Requests */
408                         if (aoh && (aoh->flag == AST_OPTION_FLAG_REQUEST)) {
409                                 if (who == chan)
410                                         ast_channel_setoption(peer, ntohs(aoh->option), aoh->data, f->datalen - sizeof(struct ast_option_header), 0);
411                                 else
412                                         ast_channel_setoption(chan, ntohs(aoh->option), aoh->data, f->datalen - sizeof(struct ast_option_header), 0);
413                         }
414                 }
415                 /* check for '*', if we find it it's time to disconnect */
416                 if (f && (f->frametype == AST_FRAME_DTMF) &&
417                         (((who == chan) && allowdisconnect_out) || ((who == peer) && allowdisconnect_in)) &&
418                         (f->subclass == '*')) {
419                         
420                         if (option_verbose > 3)
421                                 ast_verbose(VERBOSE_PREFIX_3 "User hit %c to disconnect call.\n", f->subclass);
422                         res = -1;
423                         break;
424                 }
425
426                 if ((f->frametype == AST_FRAME_DTMF) &&
427                         ((allowredirect_in && who == peer) || (allowredirect_out && who == chan)) &&
428                         (f->subclass == '#')) {
429                                 if (allowredirect_in &&  who == peer) {
430                                         transferer = peer;
431                                         transferee = chan;
432                                 } else {
433                                         transferer = chan;
434                                         transferee = peer;
435                                 }
436                                 if (!(transferer_real_context=pbx_builtin_getvar_helper(transferee, "TRANSFER_CONTEXT")) &&
437                                    !(transferer_real_context=pbx_builtin_getvar_helper(transferer, "TRANSFER_CONTEXT"))) {
438                                         /* Use the non-macro context to transfer the call */
439                                         if (!ast_strlen_zero(transferer->macrocontext))
440                                                 transferer_real_context = transferer->macrocontext;
441                                         else
442                                                 transferer_real_context = transferer->context;
443                                 }
444                                 /* Start autoservice on chan while we talk
445                                    to the originator */
446                                 ast_autoservice_start(transferee);
447                                 ast_moh_start(transferee, NULL);
448
449                                 memset(newext, 0, sizeof(newext));
450                                 ptr = newext;
451
452                                         /* Transfer */
453                                 if ((res=ast_streamfile(transferer, "pbx-transfer", transferer->language))) {
454                                         ast_moh_stop(transferee);
455                                         ast_autoservice_stop(transferee);
456                                         break;
457                                 }
458                                 if ((res=ast_waitstream(transferer, AST_DIGIT_ANY)) < 0) {
459                                         ast_moh_stop(transferee);
460                                         ast_autoservice_stop(transferee);
461                                         break;
462                                 }
463                                 ast_stopstream(transferer);
464                                 if (res > 0) {
465                                         /* If they've typed a digit already, handle it */
466                                         newext[0] = res;
467                                         ptr++;
468                                         len --;
469                                 }
470                                 res = 0;
471                                 while (strlen(newext) < sizeof(newext) - 1) {
472                                         res = ast_waitfordigit(transferer, transferdigittimeout);
473                                         if (res < 1) 
474                                                 break;
475                                         if (res == '#')
476                                                 break;
477                                         *(ptr++) = res;
478                                         if (!ast_matchmore_extension(transferer, transferer_real_context
479                                                                 , newext, 1, transferer->cid.cid_num)) {
480                                                 break;
481                                         }
482                                 }
483
484                                 if (res < 0) {
485                                         ast_moh_stop(transferee);
486                                         ast_autoservice_stop(transferee);
487                                         break;
488                                 }
489                                 if (!strcmp(newext, ast_parking_ext())) {
490                                         ast_moh_stop(transferee);
491
492                                         if (ast_autoservice_stop(transferee))
493                                                 res = -1;
494                                         else if (!ast_park_call(transferee, transferer, 0, NULL)) {
495                                                 /* We return non-zero, but tell the PBX not to hang the channel when
496                                                    the thread dies -- We have to be careful now though.  We are responsible for 
497                                                    hanging up the channel, else it will never be hung up! */
498
499                                                 if (transferer==peer)
500                                                         res=AST_PBX_KEEPALIVE;
501                                                 else
502                                                         res=AST_PBX_NO_HANGUP_PEER;
503                                                 break;
504                                         } else {
505                                                 ast_log(LOG_WARNING, "Unable to park call %s\n", transferee->name);
506                                         }
507                                         /* XXX Maybe we should have another message here instead of invalid extension XXX */
508                                 } else if (ast_exists_extension(transferee, transferer_real_context, newext, 1, transferer->cid.cid_num)) {
509                                         pbx_builtin_setvar_helper(peer, "BLINDTRANSFER", chan->name);
510                                         pbx_builtin_setvar_helper(chan, "BLINDTRANSFER", peer->name);
511                                         ast_moh_stop(transferee);
512                                         res=ast_autoservice_stop(transferee);
513                                         if (!transferee->pbx) {
514                                                 /* Doh!  Use our handy async_goto funcitons */
515                                                 if (option_verbose > 2) 
516                                                         ast_verbose(VERBOSE_PREFIX_3 "Transferring %s to '%s' (context %s) priority 1\n"
517                                                                 ,transferee->name, newext, transferer_real_context);
518                                                 if (ast_async_goto(transferee, transferer_real_context, newext, 1))
519                                                         ast_log(LOG_WARNING, "Async goto fialed :(\n");
520                                                 res = -1;
521                                         } else {
522                                                 /* Set the channel's new extension, since it exists, using transferer context */
523                                                 strncpy(transferee->exten, newext, sizeof(transferee->exten)-1);
524                                                 strncpy(transferee->context, transferer_real_context, sizeof(transferee->context)-1);
525                                                 transferee->priority = 0;
526                                                 ast_frfree(f);
527                                         }
528                                         break;
529                                 } else {
530                                         if (option_verbose > 2) 
531                                                 ast_verbose(VERBOSE_PREFIX_3 "Unable to find extension '%s' in context '%s'\n", newext, transferer_real_context);
532                                 }
533                                 res = ast_streamfile(transferer, "pbx-invalid", transferee->language);
534                                 if (res) {
535                                         ast_moh_stop(transferee);
536                                         ast_autoservice_stop(transferee);
537                                         break;
538                                 }
539                                 res = ast_waitstream(transferer, AST_DIGIT_ANY);
540                                 ast_stopstream(transferer);
541                                 ast_moh_stop(transferee);
542                                 res = ast_autoservice_stop(transferee);
543                                 if (res) {
544                                         if (option_verbose > 1)
545                                                 ast_verbose(VERBOSE_PREFIX_2 "Hungup during autoservice stop on '%s'\n", transferee->name);
546                                 }
547                         } else {
548             if (f && (f->frametype == AST_FRAME_DTMF)) {
549                   if (who == peer)
550                         ast_write(chan, f);
551                   else
552                         ast_write(peer, f);
553             }            
554 #if 1
555                                 ast_log(LOG_DEBUG, "Read from %s (%d,%d)\n", who->name, f->frametype, f->subclass);
556 #endif
557                         }
558          if (f)
559                ast_frfree(f);
560         }
561         return res;
562 }
563
564 static void *do_parking_thread(void *ignore)
565 {
566         int ms, tms, max;
567         struct parkeduser *pu, *pl, *pt = NULL;
568         struct timeval tv;
569         struct ast_frame *f;
570         char exten[AST_MAX_EXTENSION];
571         char *peername,*cp;
572         struct ast_context *con;
573         int x;
574         int gc=0;
575         fd_set rfds, efds;
576         fd_set nrfds, nefds;
577         FD_ZERO(&rfds);
578         FD_ZERO(&efds);
579
580         for (;;) {
581                 ms = -1;
582                 max = -1;
583                 ast_mutex_lock(&parking_lock);
584                 pl = NULL;
585                 pu = parkinglot;
586                 FD_ZERO(&nrfds);
587                 FD_ZERO(&nefds);
588                 while(pu) {
589                         if (pu->notquiteyet) {
590                                 /* Pretend this one isn't here yet */
591                                 pl = pu;
592                                 pu = pu->next;
593                                 continue;
594                         }
595                         if (gc < 5 && !pu->chan->generator) {
596                                 gc++;
597                                 ast_moh_start(pu->chan,NULL);
598                         }
599                         gettimeofday(&tv, NULL);
600                         tms = (tv.tv_sec - pu->start.tv_sec) * 1000 + (tv.tv_usec - pu->start.tv_usec) / 1000;
601                         if (tms > pu->parkingtime) {
602                                 /* Stop music on hold */
603                                 ast_moh_stop(pu->chan);
604                                 /* Get chan, exten from derived kludge */
605                                 if (pu->peername[0])
606                                 {
607                                         peername = strdupa(pu->peername);
608                                         cp = strrchr(peername,'-');
609                                         if (cp) *cp = 0;
610                                         con = ast_context_find(parking_con_dial);
611                                         if (!con) {
612                                                 con = ast_context_create(NULL,parking_con_dial, registrar);
613                                                 if (!con) {
614                                                         ast_log(LOG_ERROR, "Parking dial context '%s' does not exist and unable to create\n", parking_con_dial);
615                                                 }
616                                         }
617                                         if (con) {
618                                                 ast_add_extension2(con, 1, peername, 1, NULL, NULL, "Dial", strdup(peername), free, registrar);
619                                         }
620                                         strncpy(pu->chan->exten, peername, sizeof(pu->chan->exten)-1);
621                                         strncpy(pu->chan->context, parking_con_dial, sizeof(pu->chan->context)-1);
622                                         pu->chan->priority = 1;
623
624                                 } else {
625                                         /* They've been waiting too long, send them back to where they came.  Theoretically they
626                                            should have their original extensions and such, but we copy to be on the safe side */
627                                         strncpy(pu->chan->exten, pu->exten, sizeof(pu->chan->exten)-1);
628                                         strncpy(pu->chan->context, pu->context, sizeof(pu->chan->context)-1);
629                                         pu->chan->priority = pu->priority;
630                                 }
631                                 if (option_verbose > 1) 
632                                         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);
633                                 /* Start up the PBX, or hang them up */
634                                 if (ast_pbx_start(pu->chan))  {
635                                         ast_log(LOG_WARNING, "Unable to restart the PBX for user on '%s', hanging them up...\n", pu->chan->name);
636                                         ast_hangup(pu->chan);
637                                 }
638                                 /* And take them out of the parking lot */
639                                 if (pl) 
640                                         pl->next = pu->next;
641                                 else
642                                         parkinglot = pu->next;
643                                 pt = pu;
644                                 pu = pu->next;
645                                 con = ast_context_find(parking_con);
646                                 if (con) {
647                                         snprintf(exten, sizeof(exten), "%d", pt->parkingnum);
648                                         if (ast_context_remove_extension2(con, exten, 1, NULL))
649                                                 ast_log(LOG_WARNING, "Whoa, failed to remove the extension!\n");
650                                 } else
651                                         ast_log(LOG_WARNING, "Whoa, no parking context?\n");
652                                 free(pt);
653                         } else {
654                                 for (x=0;x<AST_MAX_FDS;x++) {
655                                         if ((pu->chan->fds[x] > -1) && (FD_ISSET(pu->chan->fds[x], &rfds) || FD_ISSET(pu->chan->fds[x], &efds))) {
656                                                 if (FD_ISSET(pu->chan->fds[x], &efds))
657                                                         ast_set_flag(pu->chan, AST_FLAG_EXCEPTION);
658                                                 else
659                                                         ast_clear_flag(pu->chan, AST_FLAG_EXCEPTION);
660                                                 pu->chan->fdno = x;
661                                                 /* See if they need servicing */
662                                                 f = ast_read(pu->chan);
663                                                 if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass ==  AST_CONTROL_HANGUP))) {
664                                                         /* There's a problem, hang them up*/
665                                                         if (option_verbose > 1) 
666                                                                 ast_verbose(VERBOSE_PREFIX_2 "%s got tired of being parked\n", pu->chan->name);
667                                                         ast_hangup(pu->chan);
668                                                         /* And take them out of the parking lot */
669                                                         if (pl) 
670                                                                 pl->next = pu->next;
671                                                         else
672                                                                 parkinglot = pu->next;
673                                                         pt = pu;
674                                                         pu = pu->next;
675                                                         con = ast_context_find(parking_con);
676                                                         if (con) {
677                                                                 snprintf(exten, sizeof(exten), "%d", pt->parkingnum);
678                                                                 if (ast_context_remove_extension2(con, exten, 1, NULL))
679                                                                         ast_log(LOG_WARNING, "Whoa, failed to remove the extension!\n");
680                                                         } else
681                                                                 ast_log(LOG_WARNING, "Whoa, no parking context?\n");
682                                                         free(pt);
683                                                         break;
684                                                 } else {
685                                                         /* XXX Maybe we could do something with packets, like dial "0" for operator or something XXX */
686                                                         ast_frfree(f);
687                                                         goto std;       /* XXX Ick: jumping into an else statement??? XXX */
688                                                 }
689                                         }
690                                 }
691                                 if (x >= AST_MAX_FDS) {
692 std:                                    for (x=0;x<AST_MAX_FDS;x++) {
693                                                 /* Keep this one for next one */
694                                                 if (pu->chan->fds[x] > -1) {
695                                                         FD_SET(pu->chan->fds[x], &nrfds);
696                                                         FD_SET(pu->chan->fds[x], &nefds);
697                                                         if (pu->chan->fds[x] > max)
698                                                                 max = pu->chan->fds[x];
699                                                 }
700                                         }
701                                         /* Keep track of our longest wait */
702                                         if ((tms < ms) || (ms < 0))
703                                                 ms = tms;
704                                         pl = pu;
705                                         pu = pu->next;
706                                 }
707                         }
708                 }
709                 ast_mutex_unlock(&parking_lock);
710                 rfds = nrfds;
711                 efds = nefds;
712                 tv.tv_sec = ms / 1000;
713                 tv.tv_usec = (ms % 1000) * 1000;
714                 /* Wait for something to happen */
715                 ast_select(max + 1, &rfds, NULL, &efds, (ms > -1) ? &tv : NULL);
716                 pthread_testcancel();
717         }
718         return NULL;    /* Never reached */
719 }
720
721 static int park_call_exec(struct ast_channel *chan, void *data)
722 {
723         /* Data is unused at the moment but could contain a parking
724            lot context eventually */
725         int res=0;
726         struct localuser *u;
727         LOCAL_USER_ADD(u);
728         /* Setup the exten/priority to be s/1 since we don't know
729            where this call should return */
730         strcpy(chan->exten, "s");
731         chan->priority = 1;
732         if (chan->_state != AST_STATE_UP)
733                 res = ast_answer(chan);
734         if (!res)
735                 res = ast_safe_sleep(chan, 1000);
736         if (!res)
737                 res = ast_park_call(chan, chan, 0, NULL);
738         LOCAL_USER_REMOVE(u);
739         if (!res)
740                 res = AST_PBX_KEEPALIVE;
741         return res;
742 }
743
744 static int park_exec(struct ast_channel *chan, void *data)
745 {
746         int res=0;
747         struct localuser *u;
748         struct ast_channel *peer=NULL;
749         struct parkeduser *pu, *pl=NULL;
750         char exten[AST_MAX_EXTENSION];
751         struct ast_context *con;
752         int park;
753         int dres;
754         struct ast_bridge_config config;
755
756         if (!data) {
757                 ast_log(LOG_WARNING, "Park requires an argument (extension number)\n");
758                 return -1;
759         }
760         LOCAL_USER_ADD(u);
761         park = atoi((char *)data);
762         ast_mutex_lock(&parking_lock);
763         pu = parkinglot;
764         while(pu) {
765                 if (pu->parkingnum == park) {
766                         if (pl)
767                                 pl->next = pu->next;
768                         else
769                                 parkinglot = pu->next;
770                         break;
771                 }
772                 pl = pu;
773                 pu = pu->next;
774         }
775         ast_mutex_unlock(&parking_lock);
776         if (pu) {
777                 peer = pu->chan;
778                 con = ast_context_find(parking_con);
779                 if (con) {
780                         snprintf(exten, sizeof(exten), "%d", pu->parkingnum);
781                         if (ast_context_remove_extension2(con, exten, 1, NULL))
782                                 ast_log(LOG_WARNING, "Whoa, failed to remove the extension!\n");
783                 } else
784                         ast_log(LOG_WARNING, "Whoa, no parking context?\n");
785                 free(pu);
786         }
787         /* JK02: it helps to answer the channel if not already up */
788         if (chan->_state != AST_STATE_UP) {
789                 ast_answer(chan);
790         }
791
792         if (peer) {
793                 /* Play a courtesy beep in the calling channel to prefix the bridge connecting */       
794                 if (!ast_strlen_zero(courtesytone)) {
795                         if (!ast_streamfile(chan, courtesytone, chan->language)) {
796                                 if (ast_waitstream(chan, "") < 0) {
797                                         ast_log(LOG_WARNING, "Failed to play courtesy tone!\n");
798                                         ast_hangup(peer);
799                                         return -1;
800                                 }
801                         }
802                 }
803  
804                 ast_moh_stop(peer);
805                 res = ast_channel_make_compatible(chan, peer);
806                 if (res < 0) {
807                         ast_log(LOG_WARNING, "Could not make channels %s and %s compatible for bridge\n", chan->name, peer->name);
808                         ast_hangup(peer);
809                         return -1;
810                 }
811                 /* This runs sorta backwards, since we give the incoming channel control, as if it
812                    were the person called. */
813                 if (option_verbose > 2) 
814                         ast_verbose(VERBOSE_PREFIX_3 "Channel %s connected to parked call %d\n", chan->name, park);
815
816                 memset(&config,0,sizeof(struct ast_bridge_config));
817                 config.allowredirect_in = 1;
818                 config.allowredirect_out = 1;
819                 config.allowdisconnect_out = 0;
820                 config.allowdisconnect_in = 0;
821                 config.timelimit = 0;
822                 config.play_warning = 0;
823                 config.warning_freq = 0;
824                 config.warning_sound=NULL;
825                 res = ast_bridge_call(chan,peer,&config);
826
827                 /* Simulate the PBX hanging up */
828                 if (res != AST_PBX_NO_HANGUP_PEER)
829                         ast_hangup(peer);
830                 return res;
831         } else {
832                 /* XXX Play a message XXX */
833           dres = ast_streamfile(chan, "pbx-invalidpark", chan->language);
834           if (!dres)
835             dres = ast_waitstream(chan, "");
836           else {
837             ast_log(LOG_WARNING, "ast_streamfile of %s failed on %s\n", "pbx-invalidpark", chan->name);
838             dres = 0;
839           }
840                 if (option_verbose > 2) 
841                         ast_verbose(VERBOSE_PREFIX_3 "Channel %s tried to talk to non-existant parked call %d\n", chan->name, park);
842                 res = -1;
843         }
844         LOCAL_USER_REMOVE(u);
845         return res;
846 }
847
848 static int handle_parkedcalls(int fd, int argc, char *argv[])
849 {
850         struct parkeduser *cur;
851
852         ast_cli(fd, "%4s %25s (%-15s %-12s %-4s) %-6s \n", "Num", "Channel"
853                 , "Context", "Extension", "Pri", "Timeout");
854
855         ast_mutex_lock(&parking_lock);
856
857         cur=parkinglot;
858         while(cur) {
859                 ast_cli(fd, "%4d %25s (%-15s %-12s %-4d) %6lds\n"
860                         ,cur->parkingnum, cur->chan->name, cur->context, cur->exten
861                         ,cur->priority, cur->start.tv_sec + (cur->parkingtime/1000) - time(NULL));
862
863                 cur = cur->next;
864         }
865
866         ast_mutex_unlock(&parking_lock);
867
868         return RESULT_SUCCESS;
869 }
870
871 static char showparked_help[] =
872 "Usage: show parkedcalls\n"
873 "       Lists currently parked calls.\n";
874
875 static struct ast_cli_entry showparked =
876 { { "show", "parkedcalls", NULL }, handle_parkedcalls, "Lists parked calls", showparked_help };
877 /* Dump lot status */
878 static int manager_parking_status( struct mansession *s, struct message *m )
879 {
880         struct parkeduser *cur;
881         char *id = astman_get_header(m,"ActionID");
882         char idText[256] = "";
883
884         if (id && !ast_strlen_zero(id))
885                 snprintf(idText,256,"ActionID: %s\r\n",id);
886
887         astman_send_ack(s, m, "Parked calls will follow");
888
889         ast_mutex_lock(&parking_lock);
890
891         cur=parkinglot;
892         while(cur) {
893                         ast_mutex_lock(&s->lock);
894                 ast_cli(s->fd, "Event: ParkedCall\r\n"
895                         "Exten: %d\r\n"
896                         "Channel: %s\r\n"
897                         "Timeout: %ld\r\n"
898                         "CallerID: %s\r\n"
899                         "CallerIDName: %s\r\n"
900                         "%s"
901                         "\r\n"
902                         ,cur->parkingnum, cur->chan->name
903                         ,(long)cur->start.tv_sec + (long)(cur->parkingtime/1000) - (long)time(NULL)
904                         ,(cur->chan->cid.cid_num ? cur->chan->cid.cid_num : "")
905                         ,(cur->chan->cid.cid_name ? cur->chan->cid.cid_name : "")
906                         ,idText);
907                         ast_mutex_unlock(&s->lock);
908
909             cur = cur->next;
910         }
911
912         ast_cli(s->fd,
913         "Event: ParkedCallsComplete\r\n"
914         "%s"
915         "\r\n",idText);
916
917         ast_mutex_unlock(&parking_lock);
918
919         return RESULT_SUCCESS;
920 }
921
922
923
924 int load_module(void)
925 {
926         int res;
927         int start, end;
928         struct ast_context *con;
929         struct ast_config *cfg;
930         struct ast_variable *var;
931
932         ast_cli_register(&showparked);
933
934         cfg = ast_load("features.conf");
935         if (!cfg) {
936                 cfg = ast_load("parking.conf");
937                 if (cfg)
938                         ast_log(LOG_NOTICE, "parking.conf is deprecated in favor of 'features.conf'.  Please rename it.\n");
939         }
940         if (cfg) {
941                 var = ast_variable_browse(cfg, "general");
942                 while(var) {
943                         if (!strcasecmp(var->name, "parkext")) {
944                                 strncpy(parking_ext, var->value, sizeof(parking_ext) - 1);
945                         } else if (!strcasecmp(var->name, "context")) {
946                                 strncpy(parking_con, var->value, sizeof(parking_con) - 1);
947                         } else if (!strcasecmp(var->name, "parkingtime")) {
948                                 if ((sscanf(var->value, "%d", &parkingtime) != 1) || (parkingtime < 1)) {
949                                         ast_log(LOG_WARNING, "%s is not a valid parkingtime\n", var->value);
950                                         parkingtime = DEFAULT_PARK_TIME;
951                                 } else
952                                         parkingtime = parkingtime * 1000;
953                         } else if (!strcasecmp(var->name, "parkpos")) {
954                                 if (sscanf(var->value, "%i-%i", &start, &end) != 2) {
955                                         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);
956                                 } else {
957                                         parking_start = start;
958                                         parking_stop = end;
959                                 }
960                         } else if (!strcasecmp(var->name, "adsipark")) {
961                                 adsipark = ast_true(var->value);
962                         } else if (!strcasecmp(var->name, "transferdigittimeout")) {
963                                 if ((sscanf(var->value, "%d", &transferdigittimeout) != 1) || (transferdigittimeout < 1)) {
964                                         ast_log(LOG_WARNING, "%s is not a valid transferdigittimeout\n", var->value);
965                                         transferdigittimeout = DEFAULT_TRANSFER_DIGIT_TIMEOUT;
966                                 } else
967                                         transferdigittimeout = transferdigittimeout * 1000;
968                         } else if (!strcasecmp(var->name, "courtesytone")) {
969                                 strncpy(courtesytone, var->value, sizeof(courtesytone) - 1);
970                         } else if (!strcasecmp(var->name, "pickupexten")) {
971                                 strncpy(pickup_ext, var->value, sizeof(pickup_ext) - 1);
972                         }
973                         var = var->next;
974                 }
975                 ast_destroy(cfg);
976         }
977         con = ast_context_find(parking_con);
978         if (!con) {
979                 con = ast_context_create(NULL,parking_con, registrar);
980                 if (!con) {
981                         ast_log(LOG_ERROR, "Parking context '%s' does not exist and unable to create\n", parking_con);
982                         return -1;
983                 }
984         }
985         ast_add_extension2(con, 1, ast_parking_ext(), 1, NULL, NULL, parkcall, strdup(""),free, registrar);
986         ast_pthread_create(&parking_thread, NULL, do_parking_thread, NULL);
987         res = ast_register_application(parkedcall, park_exec, synopsis, descrip);
988         if (!res)
989                 res = ast_register_application(parkcall, park_call_exec, synopsis2, descrip2);
990         if (!res) {
991                 ast_manager_register( "ParkedCalls", 0, manager_parking_status, "List parked calls" );
992         }
993         return res;
994 }
995
996 int ast_pickup_call(struct ast_channel *chan)
997 {
998         struct ast_channel *cur;
999         int res = -1;
1000         cur = ast_channel_walk_locked(NULL);
1001         while(cur) {
1002                 if (!cur->pbx && 
1003                         (cur != chan) &&
1004                         (chan->pickupgroup & cur->callgroup) &&
1005                         ((cur->_state == AST_STATE_RINGING) ||
1006                          (cur->_state == AST_STATE_RING))) {
1007                                 break;
1008                 }
1009                 ast_mutex_unlock(&cur->lock);
1010                 cur = ast_channel_walk_locked(cur);
1011         }
1012         if (cur) {
1013                 ast_log(LOG_DEBUG, "Call pickup on chan '%s' by '%s'\n",cur->name, chan->name);
1014                 res = ast_answer(chan);
1015                 if (res)
1016                         ast_log(LOG_WARNING, "Unable to answer '%s'\n", chan->name);
1017                 res = ast_queue_control(chan, AST_CONTROL_ANSWER);
1018                 if (res)
1019                         ast_log(LOG_WARNING, "Unable to queue answer on '%s'\n", chan->name);
1020                 res = ast_channel_masquerade(cur, chan);
1021                 if (res)
1022                         ast_log(LOG_WARNING, "Unable to masquerade '%s' into '%s'\n", chan->name, cur->name);           /* Done */
1023                 ast_mutex_unlock(&cur->lock);
1024         } else  {
1025                 ast_log(LOG_DEBUG, "No call pickup possible...\n");
1026         }
1027         return res;
1028 }
1029
1030 int unload_module(void)
1031 {
1032         STANDARD_HANGUP_LOCALUSERS;
1033
1034         ast_manager_unregister( "ParkedCalls" );
1035         ast_cli_unregister(&showparked);
1036         ast_unregister_application(parkcall);
1037         return ast_unregister_application(parkedcall);
1038 }
1039
1040 char *description(void)
1041 {
1042         return "Call Parking Resource";
1043 }
1044
1045 int usecount(void)
1046 {
1047         /* Never allow parking to be unloaded because it will
1048            unresolve needed symbols in the dialer */
1049 #if 0
1050         int res;
1051         STANDARD_USECOUNT(res);
1052         return res;
1053 #else
1054         return 1;
1055 #endif
1056 }
1057
1058 char *key()
1059 {
1060         return ASTERISK_GPL_KEY;
1061 }