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