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