Merge Mahmut's recording patches
[asterisk/asterisk.git] / res / res_parking.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/parking.h>
25 #include <asterisk/musiconhold.h>
26 #include <asterisk/config.h>
27 #include <stdlib.h>
28 #include <errno.h>
29 #include <unistd.h>
30 #include <string.h>
31 #include <stdlib.h>
32 #include <stdio.h>
33 #include <sys/time.h>
34 #include <sys/signal.h>
35 #include <netinet/in.h>
36
37 #include <pthread.h>
38
39 #define DEFAULT_PARK_TIME 45000
40
41 static char *parkedcall = "ParkedCall";
42
43 /* No more than 45 seconds parked before you do something with them */
44 static int parkingtime = DEFAULT_PARK_TIME;
45
46 /* Context for which parking is made accessible */
47 static char parking_con[AST_MAX_EXTENSION] = "parkedcalls";
48
49 /* Extension you type to park the call */
50 static char parking_ext[AST_MAX_EXTENSION] = "700";
51
52 /* First available extension for parking */
53 static int parking_start = 701;
54
55 /* Last available extension for parking */
56 static int parking_stop = 750;
57
58 /* Registrar for operations */
59 static char *registrar = "res_parking";
60
61 static char *synopsis = "Answer a parked call";
62
63 static char *descrip = "ParkedCall(exten):"
64 "Used to connect to a parked call.  This Application is always\n"
65 "registered internally and does not need to be explicitly added\n"
66 "into the dialplan, although you should include the 'parkedcalls'\n"
67 "context.\n";
68
69 struct parkeduser {
70         struct ast_channel *chan;
71         struct timeval start;
72         int parkingnum;
73         /* Where to go if our parking time expires */
74         char context[AST_MAX_EXTENSION];
75         char exten[AST_MAX_EXTENSION];
76         int priority;
77         int parkingtime;
78         struct parkeduser *next;
79 };
80
81 static struct parkeduser *parkinglot;
82
83 static pthread_mutex_t parking_lock = AST_MUTEX_INITIALIZER;
84
85 static pthread_t parking_thread;
86
87 STANDARD_LOCAL_USER;
88
89 LOCAL_USER_DECL;
90
91 char *ast_parking_ext(void)
92 {
93         return parking_ext;
94 }
95
96 int ast_park_call(struct ast_channel *chan, struct ast_channel *peer, int timeout, int *extout)
97 {
98         /* We put the user in the parking list, then wake up the parking thread to be sure it looks
99            after these channels too */
100         struct parkeduser *pu, *cur;
101         int x;
102         pu = malloc(sizeof(struct parkeduser));
103         if (pu) {
104                 ast_pthread_mutex_lock(&parking_lock);
105                 for (x=parking_start;x<=parking_stop;x++) {
106                         cur = parkinglot;
107                         while(cur) {
108                                 if (cur->parkingnum == x) 
109                                         break;
110                                 cur = cur->next;
111                         }
112                         if (!cur)
113                                 break;
114                 }
115                 if (x <= parking_stop) {
116                         pu->chan = chan;
117                         ast_moh_start(pu->chan, NULL);
118                         gettimeofday(&pu->start, NULL);
119                         pu->parkingnum = x;
120                         if (timeout > 0)
121                                 pu->parkingtime = timeout;
122                         else
123                                 pu->parkingtime = parkingtime;
124                         if (extout)
125                                 *extout = x;
126                         /* Remember what had been dialed, so that if the parking
127                            expires, we try to come back to the same place */
128                         strncpy(pu->context, chan->context, sizeof(pu->context)-1);
129                         strncpy(pu->exten, chan->exten, sizeof(pu->exten)-1);
130                         pu->priority = chan->priority;
131                         pu->next = parkinglot;
132                         parkinglot = pu;
133                         ast_pthread_mutex_unlock(&parking_lock);
134                         /* Wake up the (presumably select()ing) thread */
135                         pthread_kill(parking_thread, SIGURG);
136                         if (option_verbose > 1) 
137                                 ast_verbose(VERBOSE_PREFIX_2 "Parked %s on %d\n", pu->chan->name, pu->parkingnum);
138                         if (peer)
139                                 ast_say_digits(peer, pu->parkingnum, "", peer->language);
140                         /* Start music on hold */
141                         return 0;
142                 } else {
143                         ast_log(LOG_WARNING, "No more parking spaces\n");
144                         free(pu);
145                         ast_pthread_mutex_unlock(&parking_lock);
146                         return -1;
147                 }
148         } else {
149                 ast_log(LOG_WARNING, "Out of memory\n");
150                 return -1;
151         }
152         return 0;
153 }
154
155 int ast_masq_park_call(struct ast_channel *rchan, struct ast_channel *peer, int timeout, int *extout)
156 {
157         struct ast_channel *chan;
158         struct ast_frame *f;
159         /* Make a new, fake channel that we'll use to masquerade in the real one */
160         chan = ast_channel_alloc(0);
161         if (chan) {
162                 /* Let us keep track of the channel name */
163                 snprintf(chan->name, sizeof (chan->name), "Parked/%s",rchan->name);
164                 /* Make formats okay */
165                 chan->readformat = rchan->readformat;
166                 chan->writeformat = rchan->writeformat;
167                 ast_channel_masquerade(chan, rchan);
168                 /* Setup the extensions and such */
169                 strncpy(chan->context, rchan->context, sizeof(chan->context) - 1);
170                 strncpy(chan->exten, rchan->exten, sizeof(chan->exten) - 1);
171                 chan->priority = rchan->priority;
172                 /* Make the masq execute */
173                 f = ast_read(chan);
174                 if (f)
175                         ast_frfree(f);
176                 ast_park_call(chan, peer, timeout, extout);
177         } else {
178                 ast_log(LOG_WARNING, "Unable to create parked channel\n");
179                 return -1;
180         }
181         return 0;
182 }
183
184 int ast_bridge_call(struct ast_channel *chan, struct ast_channel *peer, int allowredirect, int allowdisconnect)
185 {
186         /* Copy voice back and forth between the two channels.  Give the peer
187            the ability to transfer calls with '#<extension' syntax. */
188         int len;
189         struct ast_frame *f;
190         struct ast_channel *who;
191         char newext[256], *ptr;
192         int res;
193         struct ast_option_header *aoh;
194         /* Answer if need be */
195         if (ast_answer(chan))
196                 return -1;
197         peer->appl = "Bridged Call";
198         peer->data = chan->name;
199         for (;;) {
200                 res = ast_channel_bridge(chan, peer, (allowdisconnect ? AST_BRIDGE_DTMF_CHANNEL_0 : 0) + (allowredirect ? AST_BRIDGE_DTMF_CHANNEL_1 : 0), &f, &who);
201                 if (res < 0) {
202                         ast_log(LOG_WARNING, "Bridge failed on channels %s and %s\n", chan->name, peer->name);
203                         return -1;
204                 }
205                 
206                 if (!f || ((f->frametype == AST_FRAME_CONTROL) && ((f->subclass == AST_CONTROL_HANGUP) || (f->subclass == AST_CONTROL_BUSY) || 
207                         (f->subclass == AST_CONTROL_CONGESTION)))) {
208                                 res = -1;
209                                 break;
210                 }
211                 if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_RINGING)) {
212                         if (who == chan)
213                                 ast_indicate(peer, AST_CONTROL_RINGING);
214                         else
215                                 ast_indicate(chan, AST_CONTROL_RINGING);
216                 }
217                 if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == -1)) {
218                         if (who == chan)
219                                 ast_indicate(peer, -1);
220                         else
221                                 ast_indicate(chan, -1);
222                 }
223                 if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_OPTION)) {
224                         aoh = f->data;
225                         /* Forward option Requests */
226                         if (aoh && (aoh->flag == AST_OPTION_FLAG_REQUEST)) {
227                                 if (who == chan)
228                                         ast_channel_setoption(peer, ntohs(aoh->option), aoh->data, f->datalen - sizeof(struct ast_option_header), 0);
229                                 else
230                                         ast_channel_setoption(chan, ntohs(aoh->option), aoh->data, f->datalen - sizeof(struct ast_option_header), 0);
231                         }
232                 }
233             if (f && (f->frametype == AST_FRAME_DTMF) && (who == chan) && allowdisconnect &&
234                      (f->subclass == '*')) {
235                 if (option_verbose > 3)
236                         ast_verbose(VERBOSE_PREFIX_3 "User hit %c to disconnect call.\n", f->subclass);
237                         res = -1;
238                         break;
239
240                         }
241                 if ((f->frametype == AST_FRAME_DTMF) && (who == peer) && allowredirect &&
242                      (f->subclass == '#')) {
243                                 /* Start autoservice on chan while we talk
244                                    to the peer */
245                                 ast_autoservice_start(chan);
246                                 memset(newext, 0, sizeof(newext));
247                                 ptr = newext;
248                                         /* Transfer */
249                                 if ((res=ast_streamfile(peer, "pbx-transfer", peer->language))) {
250                                         ast_autoservice_stop(chan);
251                                         break;
252                                 }
253                                 if ((res=ast_waitstream(peer, AST_DIGIT_ANY)) < 0) {
254                                         ast_autoservice_stop(chan);
255                                         break;
256                                 }
257                                 ast_stopstream(peer);
258                                 if (res > 0) {
259                                         /* If they've typed a digit already, handle it */
260                                         newext[0] = res;
261                                         ptr++;
262                                         len --;
263                                 }
264                                 res = 0;
265                                 while(strlen(newext) < sizeof(newext) - 1) {
266                                         res = ast_waitfordigit(peer, 3000);
267                                         if (res < 1) 
268                                                 break;
269                                         if (res == '#')
270                                                 break;
271                                         *(ptr++) = res;
272                                         if (!ast_matchmore_extension(peer, peer->context, newext, 1, peer->callerid)) {
273                                                 break;
274                                         }
275                                 }
276
277                                 if (res < 0) {
278                                         ast_autoservice_stop(chan);
279                                         break;
280                                 }
281                                 if (!strcmp(newext, ast_parking_ext())) {
282                                         if (ast_autoservice_stop(chan))
283                                                 res = -1;
284                                         else if (!ast_park_call(chan, peer, 0, NULL)) {
285                                                 /* We return non-zero, but tell the PBX not to hang the channel when
286                                                    the thread dies -- We have to be careful now though.  We are responsible for 
287                                                    hanging up the channel, else it will never be hung up! */
288                                                 res=AST_PBX_KEEPALIVE;
289                                                 break;
290                                         } else {
291                                                 ast_log(LOG_WARNING, "Unable to park call %s\n", chan->name);
292                                         }
293                                         /* XXX Maybe we should have another message here instead of invalid extension XXX */
294                                 } else if (ast_exists_extension(chan, peer->context, newext, 1, peer->callerid)) {
295                                         res=ast_autoservice_stop(chan);
296                                         if (!chan->pbx) {
297                                                 /* Doh!  Use our handy async_goto funcitons */
298                                                 if (option_verbose > 2) 
299                                                         ast_verbose(VERBOSE_PREFIX_3 "Transferring %s to '%s' (context %s) priority 1\n", chan->name, chan->exten, chan->context);
300                                                 if (ast_async_goto(chan, peer->context, newext, 1, 1))
301                                                         ast_log(LOG_WARNING, "Async goto fialed :(\n");
302                                         } else {
303                                                 /* Set the channel's new extension, since it exists, using peer context */
304                                                 strncpy(chan->exten, newext, sizeof(chan->exten)-1);
305                                                 strncpy(chan->context, peer->context, sizeof(chan->context)-1);
306                                                 chan->priority = 0;
307                                                 ast_frfree(f);
308                                         }
309                                         break;
310                                 } else {
311                                         if (option_verbose > 2) 
312                                                 ast_verbose(VERBOSE_PREFIX_3 "Unable to find extension '%s' in context %s\n", newext, peer->context);
313                                 }
314                                 res = ast_streamfile(peer, "pbx-invalid", chan->language);
315                                 if (res) {
316                                         ast_autoservice_stop(chan);
317                                         break;
318                                 }
319                                 res = ast_waitstream(peer, AST_DIGIT_ANY);
320                                 ast_stopstream(peer);
321                                 res = ast_autoservice_stop(chan);
322                                 if (res) {
323                                         if (option_verbose > 1)
324                                                 ast_verbose(VERBOSE_PREFIX_2 "Hungup during autoservice stop on '%s'\n", chan->name);
325                                 }
326                         } else {
327             if (f && (f->frametype == AST_FRAME_DTMF)) {
328                   if (who == peer)
329                         ast_write(chan, f);
330                   else
331                         ast_write(peer, f);
332             }            
333 #if 1
334                                 ast_log(LOG_DEBUG, "Read from %s (%d,%d)\n", who->name, f->frametype, f->subclass);
335 #endif
336                         }
337          if (f)
338                ast_frfree(f);
339         }
340         return res;
341 }
342
343 static void *do_parking_thread(void *ignore)
344 {
345         int ms, tms, max;
346         struct parkeduser *pu, *pl, *pt = NULL;
347         struct timeval tv;
348         struct ast_frame *f;
349         int x;
350         fd_set rfds, efds;
351         fd_set nrfds, nefds;
352         FD_ZERO(&rfds);
353         FD_ZERO(&efds);
354         for (;;) {
355                 ms = -1;
356                 max = -1;
357                 ast_pthread_mutex_lock(&parking_lock);
358                 pl = NULL;
359                 pu = parkinglot;
360                 gettimeofday(&tv, NULL);
361                 FD_ZERO(&nrfds);
362                 FD_ZERO(&nefds);
363                 while(pu) {
364                         tms = (tv.tv_sec - pu->start.tv_sec) * 1000 + (tv.tv_usec - pu->start.tv_usec) / 1000;
365                         if (tms > pu->parkingtime) {
366                                 /* They've been waiting too long, send them back to where they came.  Theoretically they
367                                    should have their original extensions and such, but we copy to be on the safe side */
368                                 strncpy(pu->chan->exten, pu->exten, sizeof(pu->chan->exten)-1);
369                                 strncpy(pu->chan->context, pu->context, sizeof(pu->chan->context)-1);
370                                 pu->chan->priority = pu->priority;
371                                 /* Stop music on hold */
372                                 ast_moh_stop(pu->chan);
373                                 /* Start up the PBX, or hang them up */
374                                 if (ast_pbx_start(pu->chan))  {
375                                         ast_log(LOG_WARNING, "Unable to restart the PBX for user on '%s', hanging them up...\n", pu->chan->name);
376                                         ast_hangup(pu->chan);
377                                 }
378                                 /* And take them out of the parking lot */
379                                 if (pl) 
380                                         pl->next = pu->next;
381                                 else
382                                         parkinglot = pu->next;
383                                 pt = pu;
384                                 pu = pu->next;
385                                 free(pt);
386                         } else {
387                                 for (x=0;x<AST_MAX_FDS;x++) {
388                                         if ((pu->chan->fds[x] > -1) && (FD_ISSET(pu->chan->fds[x], &rfds) || FD_ISSET(pu->chan->fds[x], &efds))) {
389                                                         if (FD_ISSET(pu->chan->fds[x], &efds))
390                                                                 pu->chan->exception = 1;
391                                                         pu->chan->fdno = x;
392                                                         /* See if they need servicing */
393                                                         f = ast_read(pu->chan);
394                                                         if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass ==  AST_CONTROL_HANGUP))) {
395                                                         /* There's a problem, hang them up*/
396                                                         if (option_verbose > 1) 
397                                                                 ast_verbose(VERBOSE_PREFIX_2 "%s got tired of being parked\n", pu->chan->name);
398                                                         ast_hangup(pu->chan);
399                                                         /* And take them out of the parking lot */
400                                                         if (pl) 
401                                                                 pl->next = pu->next;
402                                                         else
403                                                                 parkinglot = pu->next;
404                                                         pt = pu;
405                                                         pu = pu->next;
406                                                         free(pt);
407                                                         break;
408                                                 } else {
409                                                         /* XXX Maybe we could do something with packets, like dial "0" for operator or something XXX */
410                                                         ast_frfree(f);
411                                                         goto std;       /* XXX Ick: jumping into an else statement??? XXX */
412                                                 }
413                                         }
414                                 }
415                                 if (x >= AST_MAX_FDS) {
416 std:                                    for (x=0;x<AST_MAX_FDS;x++) {
417                                                 /* Keep this one for next one */
418                                                 if (pu->chan->fds[x] > -1) {
419                                                         FD_SET(pu->chan->fds[x], &nrfds);
420                                                         FD_SET(pu->chan->fds[x], &nefds);
421                                                         if (pu->chan->fds[x] > max)
422                                                                 max = pu->chan->fds[x];
423                                                 }
424                                         }
425                                         /* Keep track of our longest wait */
426                                         if ((tms < ms) || (ms < 0))
427                                                 ms = tms;
428                                         pl = pu;
429                                         pu = pu->next;
430                                 }
431                         }
432                 }
433                 ast_pthread_mutex_unlock(&parking_lock);
434                 rfds = nrfds;
435                 efds = nefds;
436                 tv.tv_sec = ms / 1000;
437                 tv.tv_usec = (ms % 1000) * 1000;
438                 /* Wait for something to happen */
439                 select(max + 1, &rfds, NULL, &efds, (ms > -1) ? &tv : NULL);
440                 pthread_testcancel();
441         }
442         return NULL;    /* Never reached */
443 }
444
445 static int park_exec(struct ast_channel *chan, void *data)
446 {
447         int res=0;
448         struct localuser *u;
449         struct ast_channel *peer=NULL;
450         struct parkeduser *pu, *pl=NULL;
451         int park;
452         int dres;
453         if (!data) {
454                 ast_log(LOG_WARNING, "Park requires an argument (extension number)\n");
455                 return -1;
456         }
457         LOCAL_USER_ADD(u);
458         park = atoi((char *)data);
459         ast_pthread_mutex_lock(&parking_lock);
460         pu = parkinglot;
461         while(pu) {
462                 if (pu->parkingnum == park) {
463                         if (pl)
464                                 pl->next = pu->next;
465                         else
466                                 parkinglot = pu->next;
467                         break;
468                 }
469                 pl = pu;
470                 pu = pu->next;
471         }
472         ast_pthread_mutex_unlock(&parking_lock);
473         if (pu) {
474                 peer = pu->chan;
475                 free(pu);
476         }
477         /* JK02: it helps to answer the channel if not already up */
478         if (chan->_state != AST_STATE_UP) {
479                 ast_answer(chan);
480         }
481
482         if (peer) {
483                 ast_moh_stop(peer);
484                 res = ast_channel_make_compatible(chan, peer);
485                 if (res < 0) {
486                         ast_log(LOG_WARNING, "Could not make channels %s and %s compatible for bridge\n", chan->name, peer->name);
487                         ast_hangup(peer);
488                         return -1;
489                 }
490                 /* This runs sorta backwards, since we give the incoming channel control, as if it
491                    were the person called. */
492                 if (option_verbose > 2) 
493                         ast_verbose(VERBOSE_PREFIX_3 "Channel %s connected to parked call %d\n", chan->name, park);
494                 res = ast_bridge_call(peer, chan, 1, 0);
495                 /* Simulate the PBX hanging up */
496                 if (res != AST_PBX_KEEPALIVE)
497                         ast_hangup(peer);
498                 return -1;
499         } else {
500                 /* XXX Play a message XXX */
501           dres = ast_streamfile(chan, "pbx-invalidpark", chan->language);
502           if (!dres)
503             dres = ast_waitstream(chan, "");
504           else {
505             ast_log(LOG_WARNING, "ast_streamfile of %s failed on %s\n", "pbx-invalidpark", chan->name);
506             dres = 0;
507           }
508                 if (option_verbose > 2) 
509                         ast_verbose(VERBOSE_PREFIX_3 "Channel %s tried to talk to non-existant parked call %d\n", chan->name, park);
510                 res = -1;
511         }
512         LOCAL_USER_REMOVE(u);
513         return res;
514 }
515
516 int load_module(void)
517 {
518         int res;
519         int x;
520         int start, end;
521         struct ast_context *con;
522         char exten[AST_MAX_EXTENSION];
523         struct ast_config *cfg;
524         struct ast_variable *var;
525         cfg = ast_load("parking.conf");
526         if (cfg) {
527                 var = ast_variable_browse(cfg, "general");
528                 while(var) {
529                         if (!strcasecmp(var->name, "parkext")) {
530                                 strncpy(parking_ext, var->value, sizeof(parking_ext) - 1);
531                         } else if (!strcasecmp(var->name, "context")) {
532                                 strncpy(parking_con, var->value, sizeof(parking_con) - 1);
533                         } else if (!strcasecmp(var->name, "parkingtime")) {
534                                 if ((sscanf(var->value, "%d", &parkingtime) != 1) || (parkingtime < 1)) {
535                                         ast_log(LOG_WARNING, "%s is not a valid parkingtime\n", var->value);
536                                         parkingtime = DEFAULT_PARK_TIME;
537                                 } else
538                                         parkingtime = parkingtime * 1000;
539                         } else if (!strcasecmp(var->name, "parkpos")) {
540                                 if (sscanf(var->value, "%i-%i", &start, &end) != 2) {
541                                         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);
542                                 } else {
543                                         parking_start = start;
544                                         parking_stop = end;
545                                 }
546                         }
547                         var = var->next;
548                 }
549                 ast_destroy(cfg);
550         }
551         con = ast_context_find(parking_con);
552         if (!con) {
553                 con = ast_context_create(parking_con, registrar);
554                 if (!con) {
555                         ast_log(LOG_ERROR, "Parking context '%s' does not exist and unable to create\n", parking_con);
556                         return -1;
557                 }
558         }
559         for(x=parking_start; x<=parking_stop;x++) {
560                 snprintf(exten, sizeof(exten), "%d", x);
561                 ast_add_extension2(con, 1, exten, 1, NULL, parkedcall, strdup(exten), free, registrar);
562         }
563         pthread_create(&parking_thread, NULL, do_parking_thread, NULL);
564         res = ast_register_application(parkedcall, park_exec, synopsis, descrip);
565         return res;
566 }
567
568 int unload_module(void)
569 {
570         STANDARD_HANGUP_LOCALUSERS;
571         return ast_unregister_application(parkedcall);
572 }
573
574 char *description(void)
575 {
576         return "Call Parking Resource";
577 }
578
579 int usecount(void)
580 {
581         /* Never allow parking to be unloaded because it will
582            unresolve needed symbols in the dialer */
583 #if 0
584         int res;
585         STANDARD_USECOUNT(res);
586         return res;
587 #else
588         return 1;
589 #endif
590 }
591
592 char *key()
593 {
594         return ASTERISK_GPL_KEY;
595 }