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