From f2784d97fde9452e2348da239dd9d3fdcd1d4df2 Mon Sep 17 00:00:00 2001 From: Mark Spencer Date: Thu, 27 Dec 2001 11:07:33 +0000 Subject: Version 0.1.10 from FTP git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@398 65c4cc65-6c06-0410-ace0-fbb531ad65f3 --- res/res_parking.c | 498 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 498 insertions(+) create mode 100755 res/res_parking.c (limited to 'res') diff --git a/res/res_parking.c b/res/res_parking.c new file mode 100755 index 000000000..034efa0f1 --- /dev/null +++ b/res/res_parking.c @@ -0,0 +1,498 @@ +/* + * Asterisk -- A telephony toolkit for Linux. + * + * Routines implementing call parking + * + * Copyright (C) 1999, Mark Spencer + * + * Mark Spencer + * + * This program is free software, distributed under the terms of + * the GNU General Public License + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +static char *parkedcall = "ParkedCall"; + +/* No more than 45 seconds parked before you do something with them */ +static int parkingtime = 45000; + +/* Context for which parking is made accessible */ +static char parking_con[AST_MAX_EXTENSION] = "parkedcalls"; + +/* Extension you type to park the call */ +static char parking_ext[AST_MAX_EXTENSION] = "700"; + +/* First available extension for parking */ +static int parking_start = 701; + +/* Last available extension for parking */ +static int parking_stop = 750; + +/* Registrar for operations */ +static char *registrar = "res_parking"; + +static char *synopsis = "Answer a parked call"; + +static char *descrip = "ParkedCall(exten):" +"Used to connect to a parked call. This Application is always\n" +"registered internally and does not need to be explicitly added\n" +"into the dialplan, although you should include the 'parkedcalls'\n" +"context.\n"; + +struct parkeduser { + struct ast_channel *chan; + struct timeval start; + int parkingnum; + /* Where to go if our parking time expires */ + char context[AST_MAX_EXTENSION]; + char exten[AST_MAX_EXTENSION]; + int priority; + struct parkeduser *next; +}; + +static struct parkeduser *parkinglot; + +static pthread_mutex_t parking_lock = PTHREAD_MUTEX_INITIALIZER; + +static pthread_t parking_thread; + +STANDARD_LOCAL_USER; + +LOCAL_USER_DECL; + +char *ast_parking_ext(void) +{ + return parking_ext; +} + +int ast_park_call(struct ast_channel *chan, struct ast_channel *peer) +{ + /* We put the user in the parking list, then wake up the parking thread to be sure it looks + after these channels too */ + struct parkeduser *pu, *cur; + int x; + pu = malloc(sizeof(struct parkeduser)); + if (pu) { + ast_pthread_mutex_lock(&parking_lock); + for (x=parking_start;x<=parking_stop;x++) { + cur = parkinglot; + while(cur) { + if (cur->parkingnum == x) + break; + cur = cur->next; + } + if (!cur) + break; + } + if (x <= parking_stop) { + pu->chan = chan; + gettimeofday(&pu->start, NULL); + pu->parkingnum = x; + /* Remember what had been dialed, so that if the parking + expires, we try to come back to the same place */ + strncpy(pu->context, chan->context, sizeof(pu->context)-1); + strncpy(pu->exten, chan->exten, sizeof(pu->exten)-1); + pu->priority = chan->priority; + pu->next = parkinglot; + parkinglot = pu; + ast_pthread_mutex_unlock(&parking_lock); + /* Wake up the (presumably select()ing) thread */ + pthread_kill(parking_thread, SIGURG); + if (option_verbose > 1) + ast_verbose(VERBOSE_PREFIX_2 "Parked %s on %d\n", pu->chan->name, pu->parkingnum); + ast_say_digits(peer, pu->parkingnum, "", peer->language); + return 0; + } else { + ast_log(LOG_WARNING, "No more parking spaces\n"); + free(pu); + ast_pthread_mutex_unlock(&parking_lock); + return -1; + } + } else { + ast_log(LOG_WARNING, "Out of memory\n"); + return -1; + } + return 0; +} + +int ast_masq_park_call(struct ast_channel *rchan, struct ast_channel *peer) +{ + struct ast_channel *chan; + struct ast_frame *f; + /* Make a new, fake channel that we'll use to masquerade in the real one */ + chan = ast_channel_alloc(); + if (chan) { + /* Let us keep track of the channel name */ + snprintf(chan->name, sizeof (chan->name), "Parked/%s",rchan->name); + /* Make formats okay */ + chan->readformat = rchan->readformat; + chan->writeformat = rchan->writeformat; + ast_channel_masquerade(chan, rchan); + /* Setup the extensions and such */ + strncpy(chan->context, rchan->context, sizeof(chan->context) - 1); + strncpy(chan->exten, rchan->exten, sizeof(chan->exten) - 1); + chan->priority = rchan->priority; + /* Make the masq execute */ + f = ast_read(chan); + if (f) + ast_frfree(f); + ast_park_call(chan, peer); + } else { + ast_log(LOG_WARNING, "Unable to create parked channel\n"); + return -1; + } + return 0; +} + +int ast_bridge_call(struct ast_channel *chan, struct ast_channel *peer, int allowredirect) +{ + /* Copy voice back and forth between the two channels. Give the peer + the ability to transfer calls with '#state != AST_STATE_UP) { + if (ast_answer(chan)) + return -1; + } + peer->appl = "Bridged Call"; + peer->data = chan->name; + for (;;) { + res = ast_channel_bridge(chan, peer, allowredirect ? AST_BRIDGE_DTMF_CHANNEL_1 : 0, &f, &who); + if (res < 0) { + ast_log(LOG_WARNING, "Bridge failed on channels %s and %s\n", chan->name, peer->name); + return -1; + } + + if (!f || ((f->frametype == AST_FRAME_CONTROL) && ((f->subclass == AST_CONTROL_HANGUP) || (f->subclass == AST_CONTROL_BUSY) || + (f->subclass == AST_CONTROL_CONGESTION)))) { + res = -1; + break; + } + if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_RINGING)) { + if (who == chan) + ast_indicate(peer, AST_CONTROL_RINGING); + else + ast_indicate(chan, AST_CONTROL_RINGING); + } + if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_OPTION)) { + aoh = f->data; + /* Forward option Requests */ + if (aoh && (aoh->flag == AST_OPTION_FLAG_REQUEST)) { + if (who == chan) + ast_channel_setoption(peer, ntohs(aoh->option), aoh->data, f->datalen - sizeof(struct ast_option_header), 0); + else + ast_channel_setoption(chan, ntohs(aoh->option), aoh->data, f->datalen - sizeof(struct ast_option_header), 0); + } + } + if ((f->frametype == AST_FRAME_DTMF) && (who == peer) && allowredirect && + (f->subclass == '#')) { + memset(newext, 0, sizeof(newext)); + ptr = newext; + /* Transfer */ + if ((res=ast_streamfile(peer, "pbx-transfer", chan->language))) + break; + if ((res=ast_waitstream(peer, AST_DIGIT_ANY)) < 0) + break; + ast_stopstream(peer); + if (res > 0) { + /* If they've typed a digit already, handle it */ + newext[0] = res; + ptr++; + len --; + } + res = 0; + while(strlen(newext) < sizeof(newext - 1)) { + res = ast_waitfordigit(peer, 3000); + if (res < 1) + break; + *(ptr++) = res; + if (!ast_canmatch_extension(peer, peer->context, newext, 1, peer->callerid) || + ast_exists_extension(peer, peer->context, newext, 1, peer->callerid)) { + res = 0; + break; + } + } + if (res) + break; + if (!strcmp(newext, ast_parking_ext())) { + if (!ast_park_call(chan, peer)) { + /* We return non-zero, but tell the PBX not to hang the channel when + the thread dies -- We have to be careful now though. We are responsible for + hanging up the channel, else it will never be hung up! */ + res=AST_PBX_KEEPALIVE; + break; + } else { + ast_log(LOG_WARNING, "Unable to park call %s\n", chan->name); + } + /* XXX Maybe we should have another message here instead of invalid extension XXX */ + } else if (ast_exists_extension(chan, peer->context, newext, 1, peer->callerid)) { + /* Set the channel's new extension, since it exists, using peer context */ + strncpy(chan->exten, newext, sizeof(chan->exten)-1); + strncpy(chan->context, peer->context, sizeof(chan->context)-1); + chan->priority = 0; + ast_frfree(f); + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "Transferring %s to '%s' (context %s) priority 1\n", chan->name, chan->exten, chan->context); + res=0; + break; + } else { + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "Unable to find extension '%s' in context %s\n", newext, peer->context); + } + res = ast_streamfile(peer, "pbx-invalid", chan->language); + if (res) + break; + res = ast_waitstream(peer, AST_DIGIT_ANY); + ast_stopstream(peer); + res = 0; + } else { + if (f && (f->frametype == AST_FRAME_DTMF)) { + if (who == peer) + ast_write(chan, f); + else + ast_write(peer, f); + } +#if 1 + ast_log(LOG_DEBUG, "Read from %s (%d,%d)\n", who->name, f->frametype, f->subclass); +#endif + } + if (f) + ast_frfree(f); + } + return res; +} + +static void *do_parking_thread(void *ignore) +{ + int ms, tms, max; + struct parkeduser *pu, *pl, *pt = NULL; + struct timeval tv; + struct ast_frame *f; + int x; + fd_set rfds, efds; + fd_set nrfds, nefds; + FD_ZERO(&rfds); + FD_ZERO(&efds); + for (;;) { + ms = -1; + max = -1; + ast_pthread_mutex_lock(&parking_lock); + pl = NULL; + pu = parkinglot; + gettimeofday(&tv, NULL); + FD_ZERO(&nrfds); + FD_ZERO(&nefds); + while(pu) { + tms = (tv.tv_sec - pu->start.tv_sec) * 1000 + (tv.tv_usec - pu->start.tv_usec) / 1000; + if (tms > parkingtime) { + /* They've been waiting too long, send them back to where they came. Theoretically they + should have their original extensions and such, but we copy to be on the safe side */ + strncpy(pu->chan->exten, pu->exten, sizeof(pu->chan->exten)-1); + strncpy(pu->chan->context, pu->context, sizeof(pu->chan->context)-1); + pu->chan->priority = pu->priority; + /* Start up the PBX, or hang them up */ + if (ast_pbx_start(pu->chan)) { + ast_log(LOG_WARNING, "Unable to restart the PBX for user on '%s', hanging them up...\n", pu->chan->name); + ast_hangup(pu->chan); + } + /* And take them out of the parking lot */ + if (pl) + pl->next = pu->next; + else + parkinglot = pu->next; + pt = pu; + pu = pu->next; + free(pt); + } else { + for (x=0;xchan->fds[x] > -1) && (FD_ISSET(pu->chan->fds[x], &rfds) || FD_ISSET(pu->chan->fds[x], &efds))) { + if (FD_ISSET(pu->chan->fds[x], &efds)) + pu->chan->exception = 1; + pu->chan->fdno = x; + /* See if they need servicing */ + f = ast_read(pu->chan); + if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP))) { + /* There's a problem, hang them up*/ + if (option_verbose > 1) + ast_verbose(VERBOSE_PREFIX_2 "%s got tired of being parked\n", pu->chan->name); + ast_hangup(pu->chan); + /* And take them out of the parking lot */ + if (pl) + pl->next = pu->next; + else + parkinglot = pu->next; + pt = pu; + pu = pu->next; + free(pt); + break; + } else { + /* XXX Maybe we could do something with packets, like dial "0" for operator or something XXX */ + ast_frfree(f); + goto std; /* XXX Ick: jumping into an else statement??? XXX */ + } + } + } + if (x >= AST_MAX_FDS) { +std: for (x=0;xchan->fds[x] > -1) { + FD_SET(pu->chan->fds[x], &nrfds); + FD_SET(pu->chan->fds[x], &nefds); + if (pu->chan->fds[x] > max) + max = pu->chan->fds[x]; + } + } + /* Keep track of our longest wait */ + if ((tms < ms) || (ms < 0)) + ms = tms; + pl = pu; + pu = pu->next; + } + } + } + ast_pthread_mutex_unlock(&parking_lock); + rfds = nrfds; + efds = nefds; + tv.tv_sec = ms / 1000; + tv.tv_usec = (ms % 1000) * 1000; + /* Wait for something to happen */ + select(max + 1, &rfds, NULL, &efds, (ms > -1) ? &tv : NULL); + pthread_testcancel(); + } + return NULL; /* Never reached */ +} + +static int park_exec(struct ast_channel *chan, void *data) +{ + int res=0; + struct localuser *u; + struct ast_channel *peer=NULL; + struct parkeduser *pu, *pl=NULL; + int park; + if (!data) { + ast_log(LOG_WARNING, "Park requires an argument (extension number)\n"); + return -1; + } + LOCAL_USER_ADD(u); + park = atoi((char *)data); + ast_pthread_mutex_lock(&parking_lock); + pu = parkinglot; + while(pu) { + if (pu->parkingnum == park) { + if (pl) + pl->next = pu->next; + else + parkinglot = pu->next; + break; + } + pu = pu->next; + } + ast_pthread_mutex_unlock(&parking_lock); + if (pu) { + peer = pu->chan; + free(pu); + } + if (peer) { + res = ast_channel_make_compatible(chan, peer); + if (res < 0) { + ast_log(LOG_WARNING, "Could not make channels %s and %s compatible for bridge\n", chan->name, peer->name); + ast_hangup(peer); + return -1; + } + /* This runs sorta backwards, since we give the incoming channel control, as if it + were the person called. */ + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "Channel %s connected to parked call %d\n", chan->name, park); + res = ast_bridge_call(peer, chan, 1); + /* Simulate the PBX hanging up */ + if (res != AST_PBX_KEEPALIVE) + ast_hangup(peer); + return -1; + } else { + /* XXX Play a message XXX */ + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "Channel %s tried to talk to non-existant parked call %d\n", chan->name, park); + res = -1; + } + LOCAL_USER_REMOVE(u); + return res; +} + +int load_module(void) +{ + int res; + int x; + struct ast_context *con; + char exten[AST_MAX_EXTENSION]; + con = ast_context_find(parking_con); + if (!con) { + con = ast_context_create(parking_con, registrar); + if (!con) { + ast_log(LOG_ERROR, "Parking context '%s' does not exist and unable to create\n", parking_con); + return -1; + } + } + for(x=parking_start; x<=parking_stop;x++) { + snprintf(exten, sizeof(exten), "%d", x); + ast_add_extension2(con, 1, exten, 1, NULL, parkedcall, strdup(exten), free, registrar); + } + pthread_create(&parking_thread, NULL, do_parking_thread, NULL); + res = ast_register_application(parkedcall, park_exec, synopsis, descrip); + return res; +} + +int unload_module(void) +{ + STANDARD_HANGUP_LOCALUSERS; + return ast_unregister_application(parkedcall); +} + +char *description(void) +{ + return "Call Parking Resource"; +} + +int usecount(void) +{ + /* Never allow parking to be unloaded because it will + unresolve needed symbols in the dialer */ +#if 0 + int res; + STANDARD_USECOUNT(res); + return res; +#else + return 1; +#endif +} + +char *key() +{ + return ASTERISK_GPL_KEY; +} -- cgit v1.2.3