Index: apps/app_queue.c =================================================================== RCS file: /usr/cvsroot/asterisk/apps/app_queue.c,v retrieving revision 1.130 diff -u -u -r1.130 app_queue.c --- apps/app_queue.c 31 Mar 2005 19:06:08 -0000 1.130 +++ apps/app_queue.c 1 Apr 2005 22:05:14 -0000 @@ -1,3319 +0,0 @@ -/* - * Asterisk -- A telephony toolkit for Linux. - * - * True call queues with optional send URL on answer - * - * Copyright (C) 1999-2005, Digium, Inc. - * - * Mark Spencer - * - * 2004-11-25: Persistent Dynamic Members added by: - * NetNation Communications (www.netnation.com) - * Kevin Lindsay - * - * Each dynamic agent in each queue is now stored in the astdb. - * When asterisk is restarted, each agent will be automatically - * readded into their recorded queues. This feature can be - * configured with the 'persistent_members=<1|0>' setting in the - * '[general]' category in queues.conf. The default is on. - * - * 2004-06-04: Priorities in queues added by inAccess Networks (work funded by Hellas On Line (HOL) www.hol.gr). - * - * These features added by David C. Troy : - * - Per-queue holdtime calculation - * - Estimated holdtime announcement - * - Position announcement - * - Abandoned/completed call counters - * - Failout timer passed as optional app parameter - * - Optional monitoring of calls, started when call is answered - * - * Patch Version 1.07 2003-12-24 01 - * - * Added servicelevel statistic by Michiel Betel - * Added Priority jumping code for adding and removing queue members by Jonathan Stanton - * - * Fixed to work with CVS as of 2004-02-25 and released as 1.07a - * by Matthew Enger - * - * 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 -#include -#include -#include -#include -#include -#include -#include - -#include "../astconf.h" - -#define QUEUE_STRATEGY_RINGALL 0 -#define QUEUE_STRATEGY_ROUNDROBIN 1 -#define QUEUE_STRATEGY_LEASTRECENT 2 -#define QUEUE_STRATEGY_FEWESTCALLS 3 -#define QUEUE_STRATEGY_RANDOM 4 -#define QUEUE_STRATEGY_RRMEMORY 5 - -static struct strategy { - int strategy; - char *name; -} strategies[] = { - { QUEUE_STRATEGY_RINGALL, "ringall" }, - { QUEUE_STRATEGY_ROUNDROBIN, "roundrobin" }, - { QUEUE_STRATEGY_LEASTRECENT, "leastrecent" }, - { QUEUE_STRATEGY_FEWESTCALLS, "fewestcalls" }, - { QUEUE_STRATEGY_RANDOM, "random" }, - { QUEUE_STRATEGY_RRMEMORY, "rrmemory" }, -}; - -#define DEFAULT_RETRY 5 -#define DEFAULT_TIMEOUT 15 -#define RECHECK 1 /* Recheck every second to see we we're at the top yet */ - -#define RES_OKAY 0 /* Action completed */ -#define RES_EXISTS (-1) /* Entry already exists */ -#define RES_OUTOFMEMORY (-2) /* Out of memory */ -#define RES_NOSUCHQUEUE (-3) /* No such queue */ - -static char *tdesc = "True Call Queueing"; - -static char *app = "Queue"; - -static char *synopsis = "Queue a call for a call queue"; - -static char *descrip = -" Queue(queuename[|options[|URL][|announceoverride][|timeout]]):\n" -"Queues an incoming call in a particular call queue as defined in queues.conf.\n" -" This application returns -1 if the originating channel hangs up, or if the\n" -"call is bridged and either of the parties in the bridge terminate the call.\n" -"Returns 0 if the queue is full, nonexistant, or has no members.\n" -"The option string may contain zero or more of the following characters:\n" -" 't' -- allow the called user transfer the calling user\n" -" 'T' -- to allow the calling user to transfer the call.\n" -" 'd' -- data-quality (modem) call (minimum delay).\n" -" 'h' -- allow callee to hang up by hitting *.\n" -" 'H' -- allow caller to hang up by hitting *.\n" -" 'n' -- no retries on the timeout; will exit this application and \n" -" go to the next step.\n" -" 'r' -- ring instead of playing MOH\n" -" In addition to transferring the call, a call may be parked and then picked\n" -"up by another user.\n" -" The optional URL will be sent to the called party if the channel supports\n" -"it.\n" -" The timeout will cause the queue to fail out after a specified number of\n" -"seconds, checked between each queues.conf 'timeout' and 'retry' cycle.\n" -" This application sets the following channel variable upon completion:\n" -" QUEUESTATUS The status of the call as a text string, one of\n" -" TIMEOUT | FULL | JOINEMPTY | LEAVEEMPTY | JOINUNAVAIL | LEAVEUNAVAIL\n"; - -static char *app_aqm = "AddQueueMember" ; -static char *app_aqm_synopsis = "Dynamically adds queue members" ; -static char *app_aqm_descrip = -" AddQueueMember(queuename[|interface[|penalty]]):\n" -"Dynamically adds interface to an existing queue.\n" -"If the interface is already in the queue and there exists an n+101 priority\n" -"then it will then jump to this priority. Otherwise it will return an error\n" -"Returns -1 if there is an error.\n" -"Example: AddQueueMember(techsupport|SIP/3000)\n" -""; - -static char *app_rqm = "RemoveQueueMember" ; -static char *app_rqm_synopsis = "Dynamically removes queue members" ; -static char *app_rqm_descrip = -" RemoveQueueMember(queuename[|interface]):\n" -"Dynamically removes interface to an existing queue\n" -"If the interface is NOT in the queue and there exists an n+101 priority\n" -"then it will then jump to this priority. Otherwise it will return an error\n" -"Returns -1 if there is an error.\n" -"Example: RemoveQueueMember(techsupport|SIP/3000)\n" -""; - -static char *app_pqm = "PauseQueueMember" ; -static char *app_pqm_synopsis = "Pauses a queue member" ; -static char *app_pqm_descrip = -" PauseQueueMember([queuename]|interface):\n" -"Pauses (blocks calls for) a queue member.\n" -"The given interface will be paused in the given queue. This prevents\n" -"any calls from being sent from the queue to the interface until it is\n" -"unpaused with UnpauseQueueMember or the manager interface. If no\n" -"queuename is given, the interface is paused in every queue it is a\n" -"member of. If the interface is not in the named queue, or if no queue\n" -"is given and the interface is not in any queue, it will jump to\n" -" priority n+101, if it exists. Returns -1 if the interface is not\n" -"found and no extension to jump to exists, 0 otherwise.\n" -"Example: PauseQueueMember(|SIP/3000)\n"; - -static char *app_upqm = "UnpauseQueueMember" ; -static char *app_upqm_synopsis = "Unpauses a queue member" ; -static char *app_upqm_descrip = -" UnpauseQueueMember([queuename]|interface):\n" -"Unpauses (resumes calls to) a queue member.\n" -"This is the counterpart to PauseQueueMember and operates exactly the\n" -"same way, except it unpauses instead of pausing the given interface.\n" -"Example: UnpauseQueueMember(|SIP/3000)\n"; - -/* Persistent Members astdb family */ -static const char *pm_family = "/Queue/PersistentMembers"; -/* The maximum lengh of each persistent member queue database entry */ -#define PM_MAX_LEN 2048 - -/* queues.conf [general] option */ -static int queue_persistent_members = 0; - -/* queues.conf per-queue weight option */ -static int use_weight = 0; - -enum queue_result { - QUEUE_UNKNOWN = 0, - QUEUE_TIMEOUT = 1, - QUEUE_JOINEMPTY = 2, - QUEUE_LEAVEEMPTY = 3, - QUEUE_JOINUNAVAIL = 4, - QUEUE_LEAVEUNAVAIL = 5, - QUEUE_FULL = 6, -}; - -const struct { - enum queue_result id; - char *text; -} queue_results[] = { - { QUEUE_UNKNOWN, "UNKNOWN" }, - { QUEUE_TIMEOUT, "TIMEOUT" }, - { QUEUE_JOINEMPTY,"JOINEMPTY" }, - { QUEUE_LEAVEEMPTY, "LEAVEEMPTY" }, - { QUEUE_JOINUNAVAIL, "JOINUNAVAIL" }, - { QUEUE_LEAVEUNAVAIL, "LEAVEUNAVAIL" }, - { QUEUE_FULL, "FULL" }, -}; - -/* We define a custom "local user" structure because we - use it not only for keeping track of what is in use but - also for keeping track of who we're dialing. */ - -struct localuser { - struct ast_channel *chan; - char interface[256]; - int stillgoing; - int metric; - int oldstatus; - time_t lastcall; - struct member *member; - struct localuser *next; -}; - -LOCAL_USER_DECL; - -struct queue_ent { - struct ast_call_queue *parent; /* What queue is our parent */ - char moh[80]; /* Name of musiconhold to be used */ - char announce[80]; /* Announcement to play for member when call is answered */ - char context[80]; /* Context when user exits queue */ - int pos; /* Where we are in the queue */ - int prio; /* Our priority */ - int last_pos_said; /* Last position we told the user */ - time_t last_pos; /* Last time we told the user their position */ - int opos; /* Where we started in the queue */ - int handled; /* Whether our call was handled */ - time_t start; /* When we started holding */ - time_t expire; /* When this entry should expire (time out of queue) */ - struct ast_channel *chan; /* Our channel */ - struct queue_ent *next; /* The next queue entry */ -}; - -struct member { - char interface[80]; /* Technology/Location */ - int penalty; /* Are we a last resort? */ - int calls; /* Number of calls serviced by this member */ - int dynamic; /* Are we dynamically added? */ - int status; /* Status of queue member */ - int paused; /* Are we paused (not accepting calls)? */ - time_t lastcall; /* When last successful call was hungup */ - struct member *next; /* Next member */ -}; - -/* values used in multi-bit flags in ast_call_queue */ -#define QUEUE_EMPTY_NORMAL 1 -#define QUEUE_EMPTY_STRICT 2 -#define ANNOUNCEHOLDTIME_ALWAYS 1 -#define ANNOUNCEHOLDTIME_ONCE 2 - -struct ast_call_queue { - ast_mutex_t lock; - char name[80]; /* Name */ - char moh[80]; /* Music On Hold class to be used */ - char announce[80]; /* Announcement to play when call is answered */ - char context[80]; /* Exit context */ - struct { - unsigned int monjoin:1; - unsigned int dead:1; - unsigned int joinempty:2; - unsigned int eventwhencalled:1; - unsigned int leavewhenempty:2; - unsigned int reportholdtime:1; - unsigned int wrapped:1; - unsigned int timeoutrestart:1; - unsigned int announceholdtime:2; - unsigned int strategy:3; - }; - int announcefrequency; /* How often to announce their position */ - int roundingseconds; /* How many seconds do we round to? */ - int holdtime; /* Current avg holdtime, based on recursive boxcar filter */ - int callscompleted; /* Number of queue calls completed */ - int callsabandoned; /* Number of queue calls abandoned */ - int servicelevel; /* seconds setting for servicelevel*/ - int callscompletedinsl; /* Number of calls answered with servicelevel*/ - char monfmt[8]; /* Format to use when recording calls */ - char sound_next[80]; /* Sound file: "Your call is now first in line" (def. queue-youarenext) */ - char sound_thereare[80]; /* Sound file: "There are currently" (def. queue-thereare) */ - char sound_calls[80]; /* Sound file: "calls waiting to speak to a representative." (def. queue-callswaiting)*/ - char sound_holdtime[80]; /* Sound file: "The current estimated total holdtime is" (def. queue-holdtime) */ - char sound_minutes[80]; /* Sound file: "minutes." (def. queue-minutes) */ - char sound_lessthan[80]; /* Sound file: "less-than" (def. queue-lessthan) */ - char sound_seconds[80]; /* Sound file: "seconds." (def. queue-seconds) */ - char sound_thanks[80]; /* Sound file: "Thank you for your patience." (def. queue-thankyou) */ - char sound_reporthold[80]; /* Sound file: "Hold time" (def. queue-reporthold) */ - - int count; /* How many entries */ - int maxlen; /* Max number of entries */ - int wrapuptime; /* Wrapup Time */ - - int retry; /* Retry calling everyone after this amount of time */ - int timeout; /* How long to wait for an answer */ - int weight; /* Respective weight */ - - /* Queue strategy things */ - int rrpos; /* Round Robin - position */ - int memberdelay; /* Seconds to delay connecting member to caller */ - - struct member *members; /* Head of the list of members */ - struct queue_ent *head; /* Head of the list of callers */ - struct ast_call_queue *next; /* Next call queue */ -}; - -static struct ast_call_queue *queues = NULL; -AST_MUTEX_DEFINE_STATIC(qlock); - -static void set_queue_result(struct ast_channel *chan, enum queue_result res) -{ - int i; - - for (i = 0; i < sizeof(queue_results) / sizeof(queue_results[0]); i++) { - if (queue_results[i].id == res) { - pbx_builtin_setvar_helper(chan, "QUEUESTATUS", queue_results[i].text); - return; - } - } -} - -static char *int2strat(int strategy) -{ - int x; - for (x=0;x"; -} - -static int strat2int(char *strategy) -{ - int x; - for (x=0;xnext; - prev->next = new; - } else { - cur = q->head; - q->head = new; - } - new->next = cur; - new->parent = q; - new->pos = ++(*pos); - new->opos = *pos; -} - -enum queue_member_status { - QUEUE_NO_MEMBERS, - QUEUE_NO_REACHABLE_MEMBERS, - QUEUE_NORMAL -}; - -static enum queue_member_status get_member_status(const struct ast_call_queue *q) -{ - struct member *member; - enum queue_member_status result = QUEUE_NO_MEMBERS; - - for (member = q->members; member; member = member->next) { - switch (member->status) { - case AST_DEVICE_INVALID: - /* nothing to do */ - break; - case AST_DEVICE_UNAVAILABLE: - result = QUEUE_NO_REACHABLE_MEMBERS; - break; - default: - return QUEUE_NORMAL; - } - } - - return result; -} - -struct statechange { - int state; - char dev[0]; -}; - -static void *changethread(void *data) -{ - struct ast_call_queue *q; - struct statechange *sc = data; - struct member *cur; - char *loc; - char *technology; - - technology = ast_strdupa(sc->dev); - loc = strchr(technology, '/'); - if (loc) { - *loc = '\0'; - loc++; - } else { - free(sc); - return NULL; - } - if (option_debug) - ast_log(LOG_DEBUG, "Device '%s/%s' changed to state '%d'\n", technology, loc, sc->state); - ast_mutex_lock(&qlock); - for (q = queues; q; q = q->next) { - ast_mutex_lock(&q->lock); - cur = q->members; - while(cur) { - if (!strcasecmp(sc->dev, cur->interface)) { - if (cur->status != sc->state) { - cur->status = sc->state; - manager_event(EVENT_FLAG_AGENT, "QueueMemberStatus", - "Queue: %s\r\n" - "Location: %s\r\n" - "Membership: %s\r\n" - "Penalty: %d\r\n" - "CallsTaken: %d\r\n" - "LastCall: %ld\r\n" - "Status: %d\r\n" - "Paused: %d\r\n", - q->name, cur->interface, cur->dynamic ? "dynamic" : "static", - cur->penalty, cur->calls, cur->lastcall, cur->status, cur->paused); - } - } - cur = cur->next; - } - ast_mutex_unlock(&q->lock); - } - ast_mutex_unlock(&qlock); - if (option_debug) - ast_log(LOG_DEBUG, "Device '%s/%s' changed to state '%d'\n", technology, loc, sc->state); - free(sc); - return NULL; -} - -static int statechange_queue(const char *dev, int state, void *ign) -{ - /* Avoid potential for deadlocks by spawning a new thread to handle - the event */ - struct statechange *sc; - pthread_t t; - pthread_attr_t attr; - - sc = malloc(sizeof(struct statechange) + strlen(dev) + 1); - if (sc) { - sc->state = state; - strcpy(sc->dev, dev); - pthread_attr_init(&attr); - pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - if (ast_pthread_create(&t, &attr, changethread, sc)) { - ast_log(LOG_WARNING, "Failed to create update thread!\n"); - free(sc); - } - } - return 0; -} - -static int join_queue(char *queuename, struct queue_ent *qe, enum queue_result *reason) -{ - struct ast_call_queue *q; - struct queue_ent *cur, *prev = NULL; - int res = -1; - int pos = 0; - int inserted = 0; - - ast_mutex_lock(&qlock); - for (q = queues; q; q = q->next) { - if (!strcasecmp(q->name, queuename)) { - enum queue_member_status stat; - /* This is our one */ - ast_mutex_lock(&q->lock); - stat = get_member_status(q); - if (!q->joinempty && (stat == QUEUE_NO_MEMBERS)) - *reason = QUEUE_JOINEMPTY; - else if ((q->joinempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS)) - *reason = QUEUE_JOINUNAVAIL; - else if (q->maxlen && (q->count >= q->maxlen)) - *reason = QUEUE_FULL; - else { - /* There's space for us, put us at the right position inside - * the queue. - * Take into account the priority of the calling user */ - inserted = 0; - prev = NULL; - cur = q->head; - while(cur) { - /* We have higher priority than the current user, enter - * before him, after all the other users with priority - * higher or equal to our priority. */ - if ((!inserted) && (qe->prio > cur->prio)) { - insert_entry(q, prev, qe, &pos); - inserted = 1; - } - cur->pos = ++pos; - prev = cur; - cur = cur->next; - } - /* No luck, join at the end of the queue */ - if (!inserted) - insert_entry(q, prev, qe, &pos); - strncpy(qe->moh, q->moh, sizeof(qe->moh) - 1); - strncpy(qe->announce, q->announce, sizeof(qe->announce) - 1); - strncpy(qe->context, q->context, sizeof(qe->context) - 1); - q->count++; - res = 0; - manager_event(EVENT_FLAG_CALL, "Join", - "Channel: %s\r\nCallerID: %s\r\nCallerIDName: %s\r\nQueue: %s\r\nPosition: %d\r\nCount: %d\r\n", - qe->chan->name, - qe->chan->cid.cid_num ? qe->chan->cid.cid_num : "unknown", - qe->chan->cid.cid_name ? qe->chan->cid.cid_name : "unknown", - q->name, qe->pos, q->count ); -#if 0 -ast_log(LOG_NOTICE, "Queue '%s' Join, Channel '%s', Position '%d'\n", q->name, qe->chan->name, qe->pos ); -#endif - } - ast_mutex_unlock(&q->lock); - break; - } - } - ast_mutex_unlock(&qlock); - return res; -} - -static void free_members(struct ast_call_queue *q, int all) -{ - /* Free non-dynamic members */ - struct member *curm, *next, *prev; - - curm = q->members; - prev = NULL; - while(curm) { - next = curm->next; - if (all || !curm->dynamic) { - if (prev) - prev->next = next; - else - q->members = next; - free(curm); - } else - prev = curm; - curm = next; - } -} - -static void destroy_queue(struct ast_call_queue *q) -{ - struct ast_call_queue *cur, *prev = NULL; - - ast_mutex_lock(&qlock); - for (cur = queues; cur; cur = cur->next) { - if (cur == q) { - if (prev) - prev->next = cur->next; - else - queues = cur->next; - } else { - prev = cur; - } - } - ast_mutex_unlock(&qlock); - free_members(q, 1); - ast_mutex_destroy(&q->lock); - free(q); -} - -static int play_file(struct ast_channel *chan, char *filename) -{ - int res; - - ast_stopstream(chan); - res = ast_streamfile(chan, filename, chan->language); - - if (!res) - res = ast_waitstream(chan, ""); - else - res = 0; - - if (res) { - ast_log(LOG_WARNING, "ast_streamfile failed on %s \n", chan->name); - res = 0; - } - ast_stopstream(chan); - - return res; -} - -static int say_position(struct queue_ent *qe) -{ - int res = 0, avgholdmins, avgholdsecs; - time_t now; - - /* Check to see if this is ludicrous -- if we just announced position, don't do it again*/ - time(&now); - if ( (now - qe->last_pos) < 15 ) - return -1; - - /* If either our position has changed, or we are over the freq timer, say position */ - if ( (qe->last_pos_said == qe->pos) && ((now - qe->last_pos) < qe->parent->announcefrequency) ) - return -1; - - ast_moh_stop(qe->chan); - /* Say we're next, if we are */ - if (qe->pos == 1) { - res += play_file(qe->chan, qe->parent->sound_next); - goto posout; - } else { - res += play_file(qe->chan, qe->parent->sound_thereare); - res += ast_say_number(qe->chan, qe->pos, AST_DIGIT_ANY, qe->chan->language, (char *) NULL); /* Needs gender */ - res += play_file(qe->chan, qe->parent->sound_calls); - } - /* Round hold time to nearest minute */ - avgholdmins = abs(( (qe->parent->holdtime + 30) - (now - qe->start) ) / 60); - - /* If they have specified a rounding then round the seconds as well */ - if(qe->parent->roundingseconds) { - avgholdsecs = (abs(( (qe->parent->holdtime + 30) - (now - qe->start) )) - 60 * avgholdmins) / qe->parent->roundingseconds; - avgholdsecs*= qe->parent->roundingseconds; - } else { - avgholdsecs=0; - } - - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Hold time for %s is %d minutes %d seconds\n", qe->parent->name, avgholdmins, avgholdsecs); - - /* If the hold time is >1 min, if it's enabled, and if it's not - supposed to be only once and we have already said it, say it */ - if ((avgholdmins+avgholdsecs) > 0 && (qe->parent->announceholdtime) && - (!(qe->parent->announceholdtime == ANNOUNCEHOLDTIME_ONCE) && qe->last_pos)) { - res += play_file(qe->chan, qe->parent->sound_holdtime); - if(avgholdmins>0) { - if (avgholdmins < 2) { - res += play_file(qe->chan, qe->parent->sound_lessthan); - res += ast_say_number(qe->chan, 2, AST_DIGIT_ANY, qe->chan->language, (char *)NULL); - } else - res += ast_say_number(qe->chan, avgholdmins, AST_DIGIT_ANY, qe->chan->language, (char*) NULL); - res += play_file(qe->chan, qe->parent->sound_minutes); - } - if(avgholdsecs>0) { - res += ast_say_number(qe->chan, avgholdsecs, AST_DIGIT_ANY, qe->chan->language, (char*) NULL); - res += play_file(qe->chan, qe->parent->sound_seconds); - } - - } - - posout: - /* Set our last_pos indicators */ - qe->last_pos = now; - qe->last_pos_said = qe->pos; - - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Told %s in %s their queue position (which was %d)\n", qe->chan->name, qe->parent->name, qe->pos); - res += play_file(qe->chan, qe->parent->sound_thanks); - ast_moh_start(qe->chan, qe->moh); - - return (res>0); -} - -static void record_abandoned(struct queue_ent *qe) -{ - ast_mutex_lock(&qe->parent->lock); - qe->parent->callsabandoned++; - ast_mutex_unlock(&qe->parent->lock); -} - -static void recalc_holdtime(struct queue_ent *qe) -{ - int oldvalue, newvalue; - - /* Calculate holdtime using a recursive boxcar filter */ - /* Thanks to SRT for this contribution */ - /* 2^2 (4) is the filter coefficient; a higher exponent would give old entries more weight */ - - newvalue = time(NULL) - qe->start; - - ast_mutex_lock(&qe->parent->lock); - if (newvalue <= qe->parent->servicelevel) - qe->parent->callscompletedinsl++; - oldvalue = qe->parent->holdtime; - qe->parent->holdtime = (((oldvalue << 2) - oldvalue) + newvalue) >> 2; - ast_mutex_unlock(&qe->parent->lock); -} - - -static void leave_queue(struct queue_ent *qe) -{ - struct ast_call_queue *q; - struct queue_ent *cur, *prev = NULL; - int pos = 0; - - q = qe->parent; - if (!q) - return; - ast_mutex_lock(&q->lock); - - prev = NULL; - cur = q->head; - while(cur) { - if (cur == qe) { - q->count--; - - /* Take us out of the queue */ - manager_event(EVENT_FLAG_CALL, "Leave", - "Channel: %s\r\nQueue: %s\r\nCount: %d\r\n", - qe->chan->name, q->name, q->count); -#if 0 -ast_log(LOG_NOTICE, "Queue '%s' Leave, Channel '%s'\n", q->name, qe->chan->name ); -#endif - /* Take us out of the queue */ - if (prev) - prev->next = cur->next; - else - q->head = cur->next; - } else { - /* Renumber the people after us in the queue based on a new count */ - cur->pos = ++pos; - prev = cur; - } - cur = cur->next; - } - ast_mutex_unlock(&q->lock); - if (q->dead && !q->count) { - /* It's dead and nobody is in it, so kill it */ - destroy_queue(q); - } -} - -/* Hang up a list of outgoing calls */ -static void hangupcalls(struct localuser *outgoing, struct ast_channel *exception) -{ - struct localuser *oo; - - while(outgoing) { - /* Hangup any existing lines we have open */ - if (outgoing->chan && (outgoing->chan != exception)) - ast_hangup(outgoing->chan); - oo = outgoing; - outgoing=outgoing->next; - free(oo); - } -} - -static int update_status(struct ast_call_queue *q, struct member *member, int status) -{ - struct member *cur; - - /* Since a reload could have taken place, we have to traverse the list to - be sure it's still valid */ - ast_mutex_lock(&q->lock); - cur = q->members; - while(cur) { - if (member == cur) { - cur->status = status; - manager_event(EVENT_FLAG_AGENT, "QueueMemberStatus", - "Queue: %s\r\n" - "Location: %s\r\n" - "Membership: %s\r\n" - "Penalty: %d\r\n" - "CallsTaken: %d\r\n" - "LastCall: %ld\r\n" - "Status: %d\r\n" - "Paused: %d\r\n", - q->name, cur->interface, cur->dynamic ? "dynamic" : "static", - cur->penalty, cur->calls, cur->lastcall, cur->status, cur->paused); - break; - } - cur = cur->next; - } - q->callscompleted++; - ast_mutex_unlock(&q->lock); - return 0; -} - -static int update_dial_status(struct ast_call_queue *q, struct member *member, int status) -{ - if (status == AST_CAUSE_BUSY) - status = AST_DEVICE_BUSY; - else if (status == AST_CAUSE_UNREGISTERED) - status = AST_DEVICE_UNAVAILABLE; - else if (status == AST_CAUSE_NOSUCHDRIVER) - status = AST_DEVICE_INVALID; - else - status = AST_DEVICE_UNKNOWN; - return update_status(q, member, status); -} - -/* traverse all defined queues which have calls waiting and contain this member - return 0 if no other queue has precedence (higher weight) or 1 if found */ -static int compare_weight(struct ast_call_queue *rq, struct member *member) -{ - struct ast_call_queue *q; - struct member *mem; - int found = 0; - - /* &qlock and &rq->lock already set by try_calling() - * to solve deadlock */ - for (q = queues; q; q = q->next) { - if (q == rq) /* don't check myself, could deadlock */ - continue; - ast_mutex_lock(&q->lock); - if (q->count && q->members) { - for (mem = q->members; mem; mem = mem->next) { - if (mem == member) { - ast_log(LOG_DEBUG, "Found matching member %s in queue '%s'\n", mem->interface, q->name); - if (q->weight > rq->weight) { - ast_log(LOG_DEBUG, "Queue '%s' (weight %d, calls %d) is preferred over '%s' (weight %d, calls %d)\n", q->name, q->weight, q->count, rq->name, rq->weight, rq->count); - found = 1; - break; - } - } - } - } - ast_mutex_unlock(&q->lock); - if (found) - break; - } - ast_mutex_unlock(&qlock); - return found; -} - -static int ring_entry(struct queue_ent *qe, struct localuser *tmp, int *busies) -{ - int res; - int status; - char tech[256]; - char *location; - - if (qe->parent->wrapuptime && (time(NULL) - tmp->lastcall < qe->parent->wrapuptime)) { - if (option_debug) - ast_log(LOG_DEBUG, "Wrapuptime not yet expired for %s\n", tmp->interface); - if (qe->chan->cdr) - ast_cdr_busy(qe->chan->cdr); - tmp->stillgoing = 0; - (*busies)++; - return 0; - } - - if (tmp->member->paused) { - if (option_debug) - ast_log(LOG_DEBUG, "%s paused, can't receive call\n", tmp->interface); - if (qe->chan->cdr) - ast_cdr_busy(qe->chan->cdr); - tmp->stillgoing = 0; - return 0; - } - if (use_weight && compare_weight(qe->parent,tmp->member)) { - ast_log(LOG_DEBUG, "Priority queue delaying call to %s:%s\n", qe->parent->name, tmp->interface); - if (qe->chan->cdr) - ast_cdr_busy(qe->chan->cdr); - tmp->stillgoing = 0; - (*busies)++; - return 0; - } - - strncpy(tech, tmp->interface, sizeof(tech) - 1); - if ((location = strchr(tech, '/'))) - *location++ = '\0'; - else - location = ""; - - /* Request the peer */ - tmp->chan = ast_request(tech, qe->chan->nativeformats, location, &status); - if (!tmp->chan) { /* If we can't, just go on to the next call */ -#if 0 - ast_log(LOG_NOTICE, "Unable to create channel of type '%s' for Queue\n", cur->tech); -#endif - if (qe->chan->cdr) - ast_cdr_busy(qe->chan->cdr); - tmp->stillgoing = 0; - update_dial_status(qe->parent, tmp->member, status); - (*busies)++; - return 0; - } else if (status != tmp->oldstatus) - update_dial_status(qe->parent, tmp->member, status); - - tmp->chan->appl = "AppQueue"; - tmp->chan->data = "(Outgoing Line)"; - tmp->chan->whentohangup = 0; - if (tmp->chan->cid.cid_num) - free(tmp->chan->cid.cid_num); - tmp->chan->cid.cid_num = NULL; - if (tmp->chan->cid.cid_name) - free(tmp->chan->cid.cid_name); - tmp->chan->cid.cid_name = NULL; - if (tmp->chan->cid.cid_ani) - free(tmp->chan->cid.cid_ani); - tmp->chan->cid.cid_ani = NULL; - if (qe->chan->cid.cid_num) - tmp->chan->cid.cid_num = strdup(qe->chan->cid.cid_num); - if (qe->chan->cid.cid_name) - tmp->chan->cid.cid_name = strdup(qe->chan->cid.cid_name); - if (qe->chan->cid.cid_ani) - tmp->chan->cid.cid_ani = strdup(qe->chan->cid.cid_ani); - - /* Inherit specially named variables from parent channel */ - ast_channel_inherit_variables(qe->chan, tmp->chan); - - /* Presense of ADSI CPE on outgoing channel follows ours */ - tmp->chan->adsicpe = qe->chan->adsicpe; - - /* Place the call, but don't wait on the answer */ - res = ast_call(tmp->chan, location, 0); - if (res) { - /* Again, keep going even if there's an error */ - if (option_debug) - ast_log(LOG_DEBUG, "ast call on peer returned %d\n", res); - else if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Couldn't call %s\n", tmp->interface); - ast_hangup(tmp->chan); - tmp->chan = NULL; - tmp->stillgoing = 0; - (*busies)++; - return 0; - } else { - if (qe->parent->eventwhencalled) { - manager_event(EVENT_FLAG_AGENT, "AgentCalled", - "AgentCalled: %s\r\n" - "ChannelCalling: %s\r\n" - "CallerID: %s\r\n" - "CallerIDName: %s\r\n" - "Context: %s\r\n" - "Extension: %s\r\n" - "Priority: %d\r\n", - tmp->interface, qe->chan->name, - tmp->chan->cid.cid_num ? tmp->chan->cid.cid_num : "unknown", - tmp->chan->cid.cid_name ? tmp->chan->cid.cid_name : "unknown", - qe->chan->context, qe->chan->exten, qe->chan->priority); - } - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Called %s\n", tmp->interface); - } - return 1; -} - -static int ring_one(struct queue_ent *qe, struct localuser *outgoing, int *busies) -{ - struct localuser *cur; - struct localuser *best; - int bestmetric=0; - - do { - best = NULL; - cur = outgoing; - while(cur) { - if (cur->stillgoing && /* Not already done */ - !cur->chan && /* Isn't already going */ - (!best || (cur->metric < bestmetric))) { /* We haven't found one yet, or it's better */ - bestmetric = cur->metric; - best = cur; - } - cur = cur->next; - } - if (best) { - if (!qe->parent->strategy) { - /* Ring everyone who shares this best metric (for ringall) */ - cur = outgoing; - while(cur) { - if (cur->stillgoing && !cur->chan && (cur->metric <= bestmetric)) { - if (option_debug) - ast_log(LOG_DEBUG, "(Parallel) Trying '%s' with metric %d\n", cur->interface, cur->metric); - ring_entry(qe, cur, busies); - } - cur = cur->next; - } - } else { - /* Ring just the best channel */ - if (option_debug) - ast_log(LOG_DEBUG, "Trying '%s' with metric %d\n", best->interface, best->metric); - ring_entry(qe, best, busies); - } - } - } while (best && !best->chan); - if (!best) { - if (option_debug) - ast_log(LOG_DEBUG, "Nobody left to try ringing in queue\n"); - return 0; - } - return 1; -} - -static int store_next(struct queue_ent *qe, struct localuser *outgoing) -{ - struct localuser *cur; - struct localuser *best; - int bestmetric=0; - - best = NULL; - cur = outgoing; - while(cur) { - if (cur->stillgoing && /* Not already done */ - !cur->chan && /* Isn't already going */ - (!best || (cur->metric < bestmetric))) { /* We haven't found one yet, or it's better */ - bestmetric = cur->metric; - best = cur; - } - cur = cur->next; - } - if (best) { - /* Ring just the best channel */ - if (option_debug) - ast_log(LOG_DEBUG, "Next is '%s' with metric %d\n", best->interface, best->metric); - qe->parent->rrpos = best->metric % 1000; - } else { - /* Just increment rrpos */ - if (qe->parent->wrapped) { - /* No more channels, start over */ - qe->parent->rrpos = 0; - } else { - /* Prioritize next entry */ - qe->parent->rrpos++; - } - } - qe->parent->wrapped = 0; - return 0; -} - -static int valid_exit(struct queue_ent *qe, char digit) -{ - char tmp[2]; - - if (ast_strlen_zero(qe->context)) - return 0; - tmp[0] = digit; - tmp[1] = '\0'; - if (ast_exists_extension(qe->chan, qe->context, tmp, 1, qe->chan->cid.cid_num)) { - strncpy(qe->chan->context, qe->context, sizeof(qe->chan->context) - 1); - strncpy(qe->chan->exten, tmp, sizeof(qe->chan->exten) - 1); - qe->chan->priority = 0; - return 1; - } - return 0; -} - -#define AST_MAX_WATCHERS 256 - -#define BUILD_WATCHERS do { \ - o = outgoing; \ - found = -1; \ - pos = 1; \ - numlines = 0; \ - watchers[0] = in; \ - while(o) { \ - /* Keep track of important channels */ \ - if (o->stillgoing) { \ - stillgoing = 1; \ - if (o->chan) { \ - watchers[pos++] = o->chan; \ - found = 1; \ - } \ - } \ - o = o->next; \ - numlines++; \ - } \ - } while(0) - -static struct localuser *wait_for_answer(struct queue_ent *qe, struct localuser *outgoing, int *to, char *digit, int prebusies, int caller_disconnect) -{ - char *queue = qe->parent->name; - struct localuser *o; - int found; - int numlines; - int status; - int sentringing = 0; - int numbusies = prebusies; - int numnochan = 0; - int stillgoing = 0; - int orig = *to; - struct ast_frame *f; - struct localuser *peer = NULL; - struct ast_channel *watchers[AST_MAX_WATCHERS]; - int pos; - struct ast_channel *winner; - struct ast_channel *in = qe->chan; - - while(*to && !peer) { - BUILD_WATCHERS; - if ((found < 0) && stillgoing && !qe->parent->strategy) { - /* On "ringall" strategy we only move to the next penalty level - when *all* ringing phones are done in the current penalty level */ - ring_one(qe, outgoing, &numbusies); - BUILD_WATCHERS; - } - if (found < 0) { - if (numlines == (numbusies + numnochan)) { - ast_log(LOG_DEBUG, "Everyone is busy at this time\n"); - } else { - ast_log(LOG_NOTICE, "No one is answering queue '%s' (%d/%d/%d)\n", queue, numlines, numbusies, numnochan); - } - *to = 0; - return NULL; - } - winner = ast_waitfor_n(watchers, pos, to); - o = outgoing; - while(o) { - if (o->stillgoing && (o->chan) && (o->chan->_state == AST_STATE_UP)) { - if (!peer) { - if (option_verbose > 2) - ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name); - peer = o; - } - } else if (o->chan && (o->chan == winner)) { - if (!ast_strlen_zero(o->chan->call_forward)) { - char tmpchan[256]=""; - char *stuff; - char *tech; - strncpy(tmpchan, o->chan->call_forward, sizeof(tmpchan) - 1); - if ((stuff = strchr(tmpchan, '/'))) { - *stuff = '\0'; - stuff++; - tech = tmpchan; - } else { - snprintf(tmpchan, sizeof(tmpchan), "%s@%s", o->chan->call_forward, o->chan->context); - stuff = tmpchan; - tech = "Local"; - } - /* Before processing channel, go ahead and check for forwarding */ - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Now forwarding %s to '%s/%s' (thanks to %s)\n", in->name, tech, stuff, o->chan->name); - /* Setup parameters */ - o->chan = ast_request(tech, in->nativeformats, stuff, &status); - if (status != o->oldstatus) - update_dial_status(qe->parent, o->member, status); - if (!o->chan) { - ast_log(LOG_NOTICE, "Unable to create local channel for call forward to '%s/%s'\n", tech, stuff); - o->stillgoing = 0; - numnochan++; - } else { - if (o->chan->cid.cid_num) - free(o->chan->cid.cid_num); - o->chan->cid.cid_num = NULL; - if (o->chan->cid.cid_name) - free(o->chan->cid.cid_name); - o->chan->cid.cid_name = NULL; - - if (in->cid.cid_num) { - o->chan->cid.cid_num = strdup(in->cid.cid_num); - if (!o->chan->cid.cid_num) - ast_log(LOG_WARNING, "Out of memory\n"); - } - if (in->cid.cid_name) { - o->chan->cid.cid_name = strdup(in->cid.cid_name); - if (!o->chan->cid.cid_name) - ast_log(LOG_WARNING, "Out of memory\n"); - } - strncpy(o->chan->accountcode, in->accountcode, sizeof(o->chan->accountcode) - 1); - o->chan->cdrflags = in->cdrflags; - - if (in->cid.cid_ani) { - if (o->chan->cid.cid_ani) - free(o->chan->cid.cid_ani); - o->chan->cid.cid_ani = malloc(strlen(in->cid.cid_ani) + 1); - if (o->chan->cid.cid_ani) - strncpy(o->chan->cid.cid_ani, in->cid.cid_ani, strlen(in->cid.cid_ani) + 1); - else - ast_log(LOG_WARNING, "Out of memory\n"); - } - if (o->chan->cid.cid_rdnis) - free(o->chan->cid.cid_rdnis); - if (!ast_strlen_zero(in->macroexten)) - o->chan->cid.cid_rdnis = strdup(in->macroexten); - else - o->chan->cid.cid_rdnis = strdup(in->exten); - if (ast_call(o->chan, tmpchan, 0)) { - ast_log(LOG_NOTICE, "Failed to dial on local channel for call forward to '%s'\n", tmpchan); - o->stillgoing = 0; - ast_hangup(o->chan); - o->chan = NULL; - numnochan++; - } - } - /* Hangup the original channel now, in case we needed it */ - ast_hangup(winner); - continue; - } - f = ast_read(winner); - if (f) { - if (f->frametype == AST_FRAME_CONTROL) { - switch(f->subclass) { - case AST_CONTROL_ANSWER: - /* This is our guy if someone answered. */ - if (!peer) { - if (option_verbose > 2) - ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name); - peer = o; - } - break; - case AST_CONTROL_BUSY: - if (option_verbose > 2) - ast_verbose( VERBOSE_PREFIX_3 "%s is busy\n", o->chan->name); - o->stillgoing = 0; - if (in->cdr) - ast_cdr_busy(in->cdr); - ast_hangup(o->chan); - o->chan = NULL; - if (qe->parent->strategy) { - if (qe->parent->timeoutrestart) - *to = orig; - ring_one(qe, outgoing, &numbusies); - } - numbusies++; - break; - case AST_CONTROL_CONGESTION: - if (option_verbose > 2) - ast_verbose( VERBOSE_PREFIX_3 "%s is circuit-busy\n", o->chan->name); - o->stillgoing = 0; - if (in->cdr) - ast_cdr_busy(in->cdr); - ast_hangup(o->chan); - o->chan = NULL; - if (qe->parent->strategy) { - if (qe->parent->timeoutrestart) - *to = orig; - ring_one(qe, outgoing, &numbusies); - } - numbusies++; - break; - case AST_CONTROL_RINGING: - if (option_verbose > 2) - ast_verbose( VERBOSE_PREFIX_3 "%s is ringing\n", o->chan->name); - if (!sentringing) { -#if 0 - ast_indicate(in, AST_CONTROL_RINGING); -#endif - sentringing++; - } - break; - case AST_CONTROL_OFFHOOK: - /* Ignore going off hook */ - break; - default: - ast_log(LOG_DEBUG, "Dunno what to do with control type %d\n", f->subclass); - } - } - ast_frfree(f); - } else { - o->stillgoing = 0; - ast_hangup(o->chan); - o->chan = NULL; - if (qe->parent->strategy) { - if (qe->parent->timeoutrestart) - *to = orig; - ring_one(qe, outgoing, &numbusies); - } - } - } - o = o->next; - } - if (winner == in) { - f = ast_read(in); -#if 0 - if (f && (f->frametype != AST_FRAME_VOICE)) - printf("Frame type: %d, %d\n", f->frametype, f->subclass); - else if (!f || (f->frametype != AST_FRAME_VOICE)) - printf("Hangup received on %s\n", in->name); -#endif - if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP))) { - /* Got hung up */ - *to=-1; - return NULL; - } - if (f && (f->frametype == AST_FRAME_DTMF) && caller_disconnect && (f->subclass == '*')) { - if (option_verbose > 3) - ast_verbose(VERBOSE_PREFIX_3 "User hit %c to disconnect call.\n", f->subclass); - *to=0; - return NULL; - } - if (f && (f->frametype == AST_FRAME_DTMF) && (f->subclass != '*') && valid_exit(qe, f->subclass)) { - if (option_verbose > 3) - ast_verbose(VERBOSE_PREFIX_3 "User pressed digit: %c", f->subclass); - *to=0; - *digit=f->subclass; - return NULL; - } - } - if (!*to && (option_verbose > 2)) - ast_verbose( VERBOSE_PREFIX_3 "Nobody picked up in %d ms\n", orig); - } - - return peer; - -} - -static int is_our_turn(struct queue_ent *qe) -{ - struct queue_ent *ch; - int res; - - /* Atomically read the parent head -- does not need a lock */ - ch = qe->parent->head; - /* If we are now at the top of the head, break out */ - if (ch == qe) { - if (option_debug) - ast_log(LOG_DEBUG, "It's our turn (%s).\n", qe->chan->name); - res = 1; - } else { - if (option_debug) - ast_log(LOG_DEBUG, "It's not our turn (%s).\n", qe->chan->name); - res = 0; - } - return res; -} - -static int wait_our_turn(struct queue_ent *qe, int ringing, enum queue_result *reason) -{ - int res = 0; - - /* This is the holding pen for callers 2 through maxlen */ - for (;;) { - enum queue_member_status stat; - - if (is_our_turn(qe)) - break; - - /* If we have timed out, break out */ - if (qe->expire && (time(NULL) > qe->expire)) { - *reason = QUEUE_TIMEOUT; - break; - } - - stat = get_member_status(qe->parent); - - /* leave the queue if no agents, if enabled */ - if (qe->parent->leavewhenempty && (stat == QUEUE_NO_MEMBERS)) { - *reason = QUEUE_LEAVEEMPTY; - leave_queue(qe); - break; - } - - /* leave the queue if no reachable agents, if enabled */ - if ((qe->parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS)) { - *reason = QUEUE_LEAVEUNAVAIL; - leave_queue(qe); - break; - } - - /* Make a position announcement, if enabled */ - if (qe->parent->announcefrequency && !ringing) - say_position(qe); - - /* Wait a second before checking again */ - res = ast_waitfordigit(qe->chan, RECHECK * 1000); - if (res) - break; - } - return res; -} - -static int update_queue(struct ast_call_queue *q, struct member *member) -{ - struct member *cur; - - /* Since a reload could have taken place, we have to traverse the list to - be sure it's still valid */ - ast_mutex_lock(&q->lock); - cur = q->members; - while(cur) { - if (member == cur) { - time(&cur->lastcall); - cur->calls++; - break; - } - cur = cur->next; - } - q->callscompleted++; - ast_mutex_unlock(&q->lock); - return 0; -} - -static int calc_metric(struct ast_call_queue *q, struct member *mem, int pos, struct queue_ent *qe, struct localuser *tmp) -{ - switch (q->strategy) { - case QUEUE_STRATEGY_RINGALL: - /* Everyone equal, except for penalty */ - tmp->metric = mem->penalty * 1000000; - break; - case QUEUE_STRATEGY_ROUNDROBIN: - if (!pos) { - if (!q->wrapped) { - /* No more channels, start over */ - q->rrpos = 0; - } else { - /* Prioritize next entry */ - q->rrpos++; - } - q->wrapped = 0; - } - /* Fall through */ - case QUEUE_STRATEGY_RRMEMORY: - if (pos < q->rrpos) { - tmp->metric = 1000 + pos; - } else { - if (pos > q->rrpos) - /* Indicate there is another priority */ - q->wrapped = 1; - tmp->metric = pos; - } - tmp->metric += mem->penalty * 1000000; - break; - case QUEUE_STRATEGY_RANDOM: - tmp->metric = rand() % 1000; - tmp->metric += mem->penalty * 1000000; - break; - case QUEUE_STRATEGY_FEWESTCALLS: - tmp->metric = mem->calls; - tmp->metric += mem->penalty * 1000000; - break; - case QUEUE_STRATEGY_LEASTRECENT: - if (!mem->lastcall) - tmp->metric = 0; - else - tmp->metric = 1000000 - (time(NULL) - mem->lastcall); - tmp->metric += mem->penalty * 1000000; - break; - default: - ast_log(LOG_WARNING, "Can't calculate metric for unknown strategy %d\n", q->strategy); - break; - } - return 0; -} - -static int try_calling(struct queue_ent *qe, const char *options, char *announceoverride, const char *url, int *go_on) -{ - struct member *cur; - struct localuser *outgoing=NULL, *tmp = NULL; - int to; - char restofit[AST_MAX_EXTENSION]; - char oldexten[AST_MAX_EXTENSION]=""; - char oldcontext[AST_MAX_EXTENSION]=""; - char queuename[256]=""; - char *newnum; - char *monitorfilename; - struct ast_channel *peer; - struct ast_channel *which; - struct localuser *lpeer; - struct member *member; - int res = 0, bridge = 0; - int numbusies = 0; - int x=0; - char *announce = NULL; - char digit = 0; - time_t callstart; - time_t now; - struct ast_bridge_config bridge_config; - char nondataquality = 1; - - memset(&bridge_config, 0, sizeof(bridge_config)); - - for (; options && *options; options++) - switch (*options) { - case 't': - ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_REDIRECT); - break; - case 'T': - ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_REDIRECT); - break; - case 'd': - nondataquality = 0; - break; - case 'h': - ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_DISCONNECT); - break; - case 'H': - ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT); - break; - case 'n': - if ((now - qe->start >= qe->parent->timeout)) - *go_on = 1; - break; - } - - /* Hold the lock while we setup the outgoing calls */ - if (use_weight) - ast_mutex_lock(&qlock); - ast_mutex_lock(&qe->parent->lock); - if (option_debug) - ast_log(LOG_DEBUG, "%s is trying to call a queue member.\n", - qe->chan->name); - strncpy(queuename, qe->parent->name, sizeof(queuename) - 1); - time(&now); - cur = qe->parent->members; - if (!ast_strlen_zero(qe->announce)) - announce = qe->announce; - if (announceoverride && !ast_strlen_zero(announceoverride)) - announce = announceoverride; - - while(cur) { - tmp = malloc(sizeof(*tmp)); - if (!tmp) { - ast_mutex_unlock(&qe->parent->lock); - if (use_weight) - ast_mutex_unlock(&qlock); - ast_log(LOG_WARNING, "Out of memory\n"); - goto out; - } - memset(tmp, 0, sizeof(*tmp)); - tmp->stillgoing = -1; - if (option_debug) { - if (url) - ast_log(LOG_DEBUG, "Queue with URL=%s_\n", url); - else - ast_log(LOG_DEBUG, "Simple queue (no URL)\n"); - } - - tmp->member = cur; /* Never directly dereference! Could change on reload */ - tmp->oldstatus = cur->status; - tmp->lastcall = cur->lastcall; - strncpy(tmp->interface, cur->interface, sizeof(tmp->interface)-1); - /* If we're dialing by extension, look at the extension to know what to dial */ - if ((newnum = strstr(tmp->interface, "/BYEXTENSION"))) { - newnum++; - strncpy(restofit, newnum + strlen("BYEXTENSION"), sizeof(restofit) - 1); - snprintf(newnum, sizeof(tmp->interface) - (newnum - tmp->interface), "%s%s", qe->chan->exten, restofit); - if (option_debug) - ast_log(LOG_DEBUG, "Dialing by extension %s\n", tmp->interface); - } - /* Special case: If we ring everyone, go ahead and ring them, otherwise - just calculate their metric for the appropriate strategy */ - calc_metric(qe->parent, cur, x++, qe, tmp); - /* Put them in the list of outgoing thingies... We're ready now. - XXX If we're forcibly removed, these outgoing calls won't get - hung up XXX */ - tmp->next = outgoing; - outgoing = tmp; - /* If this line is up, don't try anybody else */ - if (outgoing->chan && (outgoing->chan->_state == AST_STATE_UP)) - break; - - cur = cur->next; - } - if (qe->parent->timeout) - to = qe->parent->timeout * 1000; - else - to = -1; - ring_one(qe, outgoing, &numbusies); - ast_mutex_unlock(&qe->parent->lock); - if (use_weight) - ast_mutex_unlock(&qlock); - lpeer = wait_for_answer(qe, outgoing, &to, &digit, numbusies, ast_test_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT)); - ast_mutex_lock(&qe->parent->lock); - if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY) { - store_next(qe, outgoing); - } - ast_mutex_unlock(&qe->parent->lock); - if (lpeer) - peer = lpeer->chan; - else - peer = NULL; - if (!peer) { - if (to) { - /* Musta gotten hung up */ - record_abandoned(qe); - res = -1; - } else { - if (digit && valid_exit(qe, digit)) - res=digit; - else - /* Nobody answered, next please? */ - res=0; - } - if (option_debug) - ast_log(LOG_DEBUG, "%s: Nobody answered.\n", qe->chan->name); - goto out; - } - if (peer) { - /* Ah ha! Someone answered within the desired timeframe. Of course after this - we will always return with -1 so that it is hung up properly after the - conversation. */ - qe->handled++; - if (!strcmp(qe->chan->type,"Zap")) - ast_channel_setoption(qe->chan, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0); - if (!strcmp(peer->type,"Zap")) - ast_channel_setoption(peer, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0); - /* Update parameters for the queue */ - recalc_holdtime(qe); - member = lpeer->member; - hangupcalls(outgoing, peer); - outgoing = NULL; - if (announce || qe->parent->reportholdtime || qe->parent->memberdelay) { - int res2; - res2 = ast_autoservice_start(qe->chan); - if (!res2) { - if (qe->parent->memberdelay) { - ast_log(LOG_NOTICE, "Delaying member connect for %d seconds\n", qe->parent->memberdelay); - res2 |= ast_safe_sleep(peer, qe->parent->memberdelay * 1000); - } - if (!res2 && announce) { - if (play_file(peer, announce)) - ast_log(LOG_WARNING, "Announcement file '%s' is unavailable, continuing anyway...\n", announce); - } - if (!res2 && qe->parent->reportholdtime) { - if (!play_file(peer, qe->parent->sound_reporthold)) { - int holdtime; - time_t now; - - time(&now); - holdtime = abs((now - qe->start) / 60); - if (holdtime < 2) { - play_file(peer, qe->parent->sound_lessthan); - ast_say_number(peer, 2, AST_DIGIT_ANY, peer->language, NULL); - } else - ast_say_number(peer, holdtime, AST_DIGIT_ANY, peer->language, NULL); - play_file(peer, qe->parent->sound_minutes); - } - } - } - res2 |= ast_autoservice_stop(qe->chan); - if (peer->_softhangup) { - /* Agent must have hung up */ - ast_log(LOG_WARNING, "Agent on %s hungup on the customer. They're going to be pissed.\n", peer->name); - ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "AGENTDUMP", "%s", ""); - if (qe->parent->eventwhencalled) { - manager_event(EVENT_FLAG_AGENT, "AgentDump", - "Queue: %s\r\n" - "Uniqueid: %s\r\n" - "Channel: %s\r\n" - "Member: %s\r\n", - queuename, qe->chan->uniqueid, peer->name, member->interface); - } - ast_hangup(peer); - goto out; - } else if (res2) { - /* Caller must have hung up just before being connected*/ - ast_log(LOG_NOTICE, "Caller was about to talk to agent on %s but the caller hungup.\n", peer->name); - ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "ABANDON", "%d|%d|%ld", qe->pos, qe->opos, (long)time(NULL) - qe->start); - record_abandoned(qe); - ast_hangup(peer); - return -1; - } - } - /* Stop music on hold */ - ast_moh_stop(qe->chan); - /* If appropriate, log that we have a destination channel */ - if (qe->chan->cdr) - ast_cdr_setdestchan(qe->chan->cdr, peer->name); - /* Make sure channels are compatible */ - res = ast_channel_make_compatible(qe->chan, peer); - if (res < 0) { - ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "SYSCOMPAT", "%s", ""); - ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", qe->chan->name, peer->name); - ast_hangup(peer); - return -1; - } - /* Begin Monitoring */ - if (qe->parent->monfmt && *qe->parent->monfmt) { - monitorfilename = pbx_builtin_getvar_helper(qe->chan, "MONITOR_FILENAME"); - if (pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC") || pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC_ARGS")) - which = qe->chan; - else - which = peer; - if (monitorfilename) - ast_monitor_start(which, qe->parent->monfmt, monitorfilename, 1 ); - else - ast_monitor_start(which, qe->parent->monfmt, qe->chan->cdr->uniqueid, 1 ); - if (qe->parent->monjoin) - ast_monitor_setjoinfiles(which, 1); - } - /* Drop out of the queue at this point, to prepare for next caller */ - leave_queue(qe); - if (url && !ast_strlen_zero(url) && ast_channel_supports_html(peer)) { - if (option_debug) - ast_log(LOG_DEBUG, "app_queue: sendurl=%s.\n", url); - ast_channel_sendurl(peer, url); - } - ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "CONNECT", "%ld", (long)time(NULL) - qe->start); - if (qe->parent->eventwhencalled) - manager_event(EVENT_FLAG_AGENT, "AgentConnect", - "Queue: %s\r\n" - "Uniqueid: %s\r\n" - "Channel: %s\r\n" - "Member: %s\r\n" - "Holdtime: %ld\r\n", - queuename, qe->chan->uniqueid, peer->name, member->interface, - (long)time(NULL) - qe->start); - strncpy(oldcontext, qe->chan->context, sizeof(oldcontext) - 1); - strncpy(oldexten, qe->chan->exten, sizeof(oldexten) - 1); - time(&callstart); - - bridge = ast_bridge_call(qe->chan,peer, &bridge_config); - - if (strcasecmp(oldcontext, qe->chan->context) || strcasecmp(oldexten, qe->chan->exten)) { - ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "TRANSFER", "%s|%s", qe->chan->exten, qe->chan->context); - } else if (qe->chan->_softhangup) { - ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "COMPLETECALLER", "%ld|%ld", - (long)(callstart - qe->start), (long)(time(NULL) - callstart)); - if (qe->parent->eventwhencalled) - manager_event(EVENT_FLAG_AGENT, "AgentComplete", - "Queue: %s\r\n" - "Uniqueid: %s\r\n" - "Channel: %s\r\n" - "Member: %s\r\n" - "HoldTime: %ld\r\n" - "TalkTime: %ld\r\n" - "Reason: caller\r\n", - queuename, qe->chan->uniqueid, peer->name, member->interface, - (long)(callstart - qe->start), (long)(time(NULL) - callstart)); - } else { - ast_queue_log(queuename, qe->chan->uniqueid, peer->name, "COMPLETEAGENT", "%ld|%ld", (long)(callstart - qe->start), (long)(time(NULL) - callstart)); - if (qe->parent->eventwhencalled) - manager_event(EVENT_FLAG_AGENT, "AgentComplete", - "Queue: %s\r\n" - "Uniqueid: %s\r\n" - "Channel: %s\r\n" - "HoldTime: %ld\r\n" - "TalkTime: %ld\r\n" - "Reason: agent\r\n", - queuename, qe->chan->uniqueid, peer->name, (long)(callstart - qe->start), - (long)(time(NULL) - callstart)); - } - - if(bridge != AST_PBX_NO_HANGUP_PEER) - ast_hangup(peer); - update_queue(qe->parent, member); - if (bridge == 0) - res = 1; /* JDG: bridge successfull, leave app_queue */ - else - res = bridge; /* bridge error, stay in the queue */ - } -out: - hangupcalls(outgoing, NULL); - return res; -} - -static int wait_a_bit(struct queue_ent *qe) -{ - /* Don't need to hold the lock while we setup the outgoing calls */ - int retrywait = qe->parent->retry * 1000; - - return ast_waitfordigit(qe->chan, retrywait); -} - -static struct member * interface_exists(struct ast_call_queue *q, char *interface) -{ - struct member *mem; - - if (q) - for (mem = q->members; mem; mem = mem->next) - if (!strcmp(interface, mem->interface)) - return mem; - - return NULL; -} - - -static struct member *create_queue_node(char *interface, int penalty, int paused) -{ - struct member *cur; - - /* Add a new member */ - - cur = malloc(sizeof(struct member)); - - if (cur) { - memset(cur, 0, sizeof(struct member)); - cur->penalty = penalty; - cur->paused = paused; - strncpy(cur->interface, interface, sizeof(cur->interface) - 1); - if (!strchr(cur->interface, '/')) - ast_log(LOG_WARNING, "No location at interface '%s'\n", interface); - cur->status = ast_device_state(interface); - } - - return cur; -} - -/* Dump all members in a specific queue to the databse - * - * / = ;;[|...] - * - */ -static void dump_queue_members(struct ast_call_queue *pm_queue) -{ - struct member *cur_member; - char value[PM_MAX_LEN]; - int value_len = 0; - int res; - - memset(value, 0, sizeof(value)); - - if (!pm_queue) - return; - - for (cur_member = pm_queue->members; cur_member; cur_member = cur_member->next) { - if (!cur_member->dynamic) - continue; - - res = snprintf(value + value_len, sizeof(value) - value_len, "%s;%d;%d%s", - cur_member->interface, cur_member->penalty, cur_member->paused, - cur_member->next ? "|" : ""); - if (res != strlen(value + value_len)) { - ast_log(LOG_WARNING, "Could not create persistent member string, out of space\n"); - break; - } - value_len += res; - } - - if (value_len && !cur_member) { - if (ast_db_put(pm_family, pm_queue->name, value)) - ast_log(LOG_WARNING, "failed to create persistent dynamic entry!\n"); - } else - /* Delete the entry if the queue is empty or there is an error */ - ast_db_del(pm_family, pm_queue->name); -} - -static int remove_from_queue(char *queuename, char *interface) -{ - struct ast_call_queue *q; - struct member *last_member, *look; - int res = RES_NOSUCHQUEUE; - - ast_mutex_lock(&qlock); - for (q = queues ; q ; q = q->next) { - ast_mutex_lock(&q->lock); - if (!strcmp(q->name, queuename)) { - if ((last_member = interface_exists(q, interface))) { - if ((look = q->members) == last_member) { - q->members = last_member->next; - } else { - while (look != NULL) { - if (look->next == last_member) { - look->next = last_member->next; - break; - } else { - look = look->next; - } - } - } - manager_event(EVENT_FLAG_AGENT, "QueueMemberRemoved", - "Queue: %s\r\n" - "Location: %s\r\n", - q->name, last_member->interface); - free(last_member); - - if (queue_persistent_members) - dump_queue_members(q); - - res = RES_OKAY; - } else { - res = RES_EXISTS; - } - ast_mutex_unlock(&q->lock); - break; - } - ast_mutex_unlock(&q->lock); - } - ast_mutex_unlock(&qlock); - return res; -} - -static int add_to_queue(char *queuename, char *interface, int penalty, int paused, int dump) -{ - struct ast_call_queue *q; - struct member *new_member; - int res = RES_NOSUCHQUEUE; - - ast_mutex_lock(&qlock); - for (q = queues ; q ; q = q->next) { - ast_mutex_lock(&q->lock); - if (!strcmp(q->name, queuename)) { - if (interface_exists(q, interface) == NULL) { - new_member = create_queue_node(interface, penalty, paused); - - if (new_member != NULL) { - new_member->dynamic = 1; - new_member->next = q->members; - q->members = new_member; - manager_event(EVENT_FLAG_AGENT, "QueueMemberAdded", - "Queue: %s\r\n" - "Location: %s\r\n" - "Membership: %s\r\n" - "Penalty: %d\r\n" - "CallsTaken: %d\r\n" - "LastCall: %ld\r\n" - "Status: %d\r\n" - "Paused: %d\r\n", - q->name, new_member->interface, new_member->dynamic ? "dynamic" : "static", - new_member->penalty, new_member->calls, new_member->lastcall, new_member->status, new_member->paused); - - if (dump) - dump_queue_members(q); - - res = RES_OKAY; - } else { - res = RES_OUTOFMEMORY; - } - } else { - res = RES_EXISTS; - } - ast_mutex_unlock(&q->lock); - break; - } - ast_mutex_unlock(&q->lock); - } - ast_mutex_unlock(&qlock); - return res; -} - -static int set_member_paused(char *queuename, char *interface, int paused) -{ - int found = 0; - struct ast_call_queue *q; - struct member *mem; - - /* Special event for when all queues are paused - individual events still generated */ - - if (ast_strlen_zero(queuename)) - ast_queue_log("NONE", "NONE", interface, (paused ? "PAUSEALL" : "UNPAUSEALL"), "%s", ""); - - ast_mutex_lock(&qlock); - for (q = queues ; q ; q = q->next) { - ast_mutex_lock(&q->lock); - if (ast_strlen_zero(queuename) || !strcmp(q->name, queuename)) { - if ((mem = interface_exists(q, interface))) { - found++; - if (mem->paused == paused) - ast_log(LOG_DEBUG, "%spausing already-%spaused queue member %s:%s\n", (paused ? "" : "un"), (paused ? "" : "un"), q->name, interface); - mem->paused = paused; - - if (queue_persistent_members) - dump_queue_members(q); - - ast_queue_log(q->name, "NONE", interface, (paused ? "PAUSE" : "UNPAUSE"), "%s", ""); - - manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused", - "Queue: %s\r\n" - "Location: %s\r\n" - "Paused: %d\r\n", - q->name, mem->interface, paused); - } - } - ast_mutex_unlock(&q->lock); - } - ast_mutex_unlock(&qlock); - - if (found) - return RESULT_SUCCESS; - else - return RESULT_FAILURE; -} - -/* Reload dynamic queue members persisted into the astdb */ -static void reload_queue_members(void) -{ - char *cur_ptr; - char *queue_name; - char *member; - char *interface; - char *penalty_tok; - int penalty = 0; - char *paused_tok; - int paused = 0; - struct ast_db_entry *db_tree; - struct ast_db_entry *entry; - struct ast_call_queue *cur_queue; - char queue_data[PM_MAX_LEN]; - - ast_mutex_lock(&qlock); - - /* Each key in 'pm_family' is the name of a queue */ - db_tree = ast_db_gettree(pm_family, NULL); - for (entry = db_tree; entry; entry = entry->next) { - - queue_name = entry->key + strlen(pm_family) + 2; - - cur_queue = queues; - while (cur_queue) { - ast_mutex_lock(&cur_queue->lock); - if (!strcmp(queue_name, cur_queue->name)) - break; - ast_mutex_unlock(&cur_queue->lock); - cur_queue = cur_queue->next; - } - - if (!cur_queue) { - /* If the queue no longer exists, remove it from the - * database */ - ast_db_del(pm_family, queue_name); - continue; - } else - ast_mutex_unlock(&cur_queue->lock); - - if (ast_db_get(pm_family, queue_name, queue_data, PM_MAX_LEN)) - continue; - - cur_ptr = queue_data; - while ((member = strsep(&cur_ptr, "|"))) { - if (ast_strlen_zero(member)) - continue; - - interface = strsep(&member, ";"); - penalty_tok = strsep(&member, ";"); - paused_tok = strsep(&member, ";"); - - if (!penalty_tok) { - ast_log(LOG_WARNING, "Error parsing persisent member string for '%s' (penalty)\n", queue_name); - break; - } - penalty = strtol(penalty_tok, NULL, 10); - if (errno == ERANGE) { - ast_log(LOG_WARNING, "Error converting penalty: %s: Out of range.\n", penalty_tok); - break; - } - - if (!paused_tok) { - ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (paused)\n", queue_name); - break; - } - paused = strtol(paused_tok, NULL, 10); - if ((errno == ERANGE) || paused < 0 || paused > 1) { - ast_log(LOG_WARNING, "Error converting paused: %s: Expected 0 or 1.\n", paused_tok); - break; - } - - if (option_debug) - ast_log(LOG_DEBUG, "Reload Members: Queue: %s Member: %s Penalty: %d Paused: %d\n", queue_name, interface, penalty, paused); - - if (add_to_queue(queue_name, interface, penalty, paused, 0) == RES_OUTOFMEMORY) { - ast_log(LOG_ERROR, "Out of Memory when reloading persistent queue member\n"); - break; - } - } - } - - ast_mutex_unlock(&qlock); - if (db_tree) { - ast_log(LOG_NOTICE, "Queue members sucessfully reloaded from database.\n"); - ast_db_freetree(db_tree); - } -} - -static int pqm_exec(struct ast_channel *chan, void *data) -{ - struct localuser *u; - char *queuename, *interface; - - if (!data) { - ast_log(LOG_WARNING, "PauseQueueMember requires an argument ([queuename]|interface])\n"); - return -1; - } - - queuename = ast_strdupa((char *)data); - if (!queuename) { - ast_log(LOG_ERROR, "Out of memory\n"); - return -1; - } - - interface = strchr(queuename, '|'); - if (!interface) { - ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename]|interface])\n"); - return -1; - } - - LOCAL_USER_ADD(u); - - *interface = '\0'; - interface++; - - if (set_member_paused(queuename, interface, 1)) { - ast_log(LOG_WARNING, "Attempt to pause interface %s, not found\n", interface); - if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 101, chan->cid.cid_num)) { - chan->priority += 100; - LOCAL_USER_REMOVE(u); - return 0; - } - return -1; - } - - LOCAL_USER_REMOVE(u); - - return 0; -} - -static int upqm_exec(struct ast_channel *chan, void *data) -{ - struct localuser *u; - char *queuename, *interface; - - if (!data) { - ast_log(LOG_WARNING, "UnpauseQueueMember requires an argument ([queuename]|interface])\n"); - return -1; - } - - queuename = ast_strdupa((char *)data); - if (!queuename) { - ast_log(LOG_ERROR, "Out of memory\n"); - return -1; - } - - interface = strchr(queuename, '|'); - if (!interface) { - ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename]|interface])\n"); - return -1; - } - - LOCAL_USER_ADD(u); - - *interface = '\0'; - interface++; - - if (set_member_paused(queuename, interface, 0)) { - ast_log(LOG_WARNING, "Attempt to unpause interface %s, not found\n", interface); - if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 101, chan->cid.cid_num)) { - chan->priority += 100; - LOCAL_USER_REMOVE(u); - return 0; - } - return -1; - } - - LOCAL_USER_REMOVE(u); - - return 0; -} - -static int rqm_exec(struct ast_channel *chan, void *data) -{ - int res=-1; - struct localuser *u; - char *info, *queuename; - char tmpchan[256]=""; - char *interface = NULL; - - if (!data) { - ast_log(LOG_WARNING, "RemoveQueueMember requires an argument (queuename[|interface])\n"); - return -1; - } - - info = ast_strdupa((char *)data); - if (!info) { - ast_log(LOG_ERROR, "Out of memory\n"); - return -1; - } - - LOCAL_USER_ADD(u); - - queuename = info; - if (queuename) { - interface = strchr(queuename, '|'); - if (interface) { - *interface = '\0'; - interface++; - } - else { - strncpy(tmpchan, chan->name, sizeof(tmpchan) - 1); - interface = strrchr(tmpchan, '-'); - if (interface) - *interface = '\0'; - interface = tmpchan; - } - } - - switch (remove_from_queue(queuename, interface)) { - case RES_OKAY: - ast_log(LOG_NOTICE, "Removed interface '%s' from queue '%s'\n", interface, queuename); - res = 0; - break; - case RES_EXISTS: - ast_log(LOG_WARNING, "Unable to remove interface '%s' from queue '%s': Not there\n", interface, queuename); - if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 101, chan->cid.cid_num)) { - chan->priority += 100; - } - res = 0; - break; - case RES_NOSUCHQUEUE: - ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': No such queue\n", queuename); - res = 0; - break; - case RES_OUTOFMEMORY: - ast_log(LOG_ERROR, "Out of memory\n"); - break; - } - - LOCAL_USER_REMOVE(u); - return res; -} - -static int aqm_exec(struct ast_channel *chan, void *data) -{ - int res=-1; - struct localuser *u; - char *queuename; - char *info; - char tmpchan[512]=""; - char *interface=NULL; - char *penaltys=NULL; - int penalty = 0; - - if (!data) { - ast_log(LOG_WARNING, "AddQueueMember requires an argument (queuename[|[interface][|penalty]])\n"); - return -1; - } - - info = ast_strdupa((char *)data); - if (!info) { - ast_log(LOG_ERROR, "Out of memory\n"); - return -1; - } - LOCAL_USER_ADD(u); - - queuename = info; - if (queuename) { - interface = strchr(queuename, '|'); - if (interface) { - *interface = '\0'; - interface++; - } - if (interface) { - penaltys = strchr(interface, '|'); - if (penaltys) { - *penaltys = '\0'; - penaltys++; - } - } - if (!interface || ast_strlen_zero(interface)) { - strncpy(tmpchan, chan->name, sizeof(tmpchan) - 1); - interface = strrchr(tmpchan, '-'); - if (interface) - *interface = '\0'; - interface = tmpchan; - } - if (penaltys && !ast_strlen_zero(penaltys)) { - if ((sscanf(penaltys, "%d", &penalty) != 1) || penalty < 0) { - ast_log(LOG_WARNING, "Penalty '%s' is invalid, must be an integer >= 0\n", penaltys); - penalty = 0; - } - } - } - - switch (add_to_queue(queuename, interface, penalty, 0, queue_persistent_members)) { - case RES_OKAY: - ast_log(LOG_NOTICE, "Added interface '%s' to queue '%s'\n", interface, queuename); - res = 0; - break; - case RES_EXISTS: - ast_log(LOG_WARNING, "Unable to add interface '%s' to queue '%s': Already there\n", interface, queuename); - if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 101, chan->cid.cid_num)) { - chan->priority += 100; - } - res = 0; - break; - case RES_NOSUCHQUEUE: - ast_log(LOG_WARNING, "Unable to add interface to queue '%s': No such queue\n", queuename); - res = 0; - break; - case RES_OUTOFMEMORY: - ast_log(LOG_ERROR, "Out of memory adding member %s to queue %s\n", interface, queuename); - break; - } - - LOCAL_USER_REMOVE(u); - return res; -} - -static int queue_exec(struct ast_channel *chan, void *data) -{ - int res=-1; - int ringing=0; - struct localuser *u; - char *queuename; - char info[512]; - char *info_ptr = info; - char *options = NULL; - char *url = NULL; - char *announceoverride = NULL; - char *user_priority; - int prio; - char *queuetimeoutstr = NULL; - enum queue_result reason = QUEUE_UNKNOWN; - - /* whether to exit Queue application after the timeout hits */ - int go_on = 0; - - /* Our queue entry */ - struct queue_ent qe; - - if (!data || ast_strlen_zero(data)) { - ast_log(LOG_WARNING, "Queue requires an argument: queuename[|options[|URL][|announceoverride][|timeout]]\n"); - return -1; - } - - LOCAL_USER_ADD(u); - - /* Setup our queue entry */ - memset(&qe, 0, sizeof(qe)); - qe.start = time(NULL); - - /* Parse our arguments XXX Check for failure XXX */ - strncpy(info, (char *) data, sizeof(info) - 1); - queuename = strsep(&info_ptr, "|"); - options = strsep(&info_ptr, "|"); - url = strsep(&info_ptr, "|"); - announceoverride = strsep(&info_ptr, "|"); - queuetimeoutstr = info_ptr; - - /* set the expire time based on the supplied timeout; */ - if (queuetimeoutstr) - qe.expire = qe.start + atoi(queuetimeoutstr); - else - qe.expire = 0; - - /* Get the priority from the variable ${QUEUE_PRIO} */ - user_priority = pbx_builtin_getvar_helper(chan, "QUEUE_PRIO"); - if (user_priority) { - if (sscanf(user_priority, "%d", &prio) == 1) { - if (option_debug) - ast_log(LOG_DEBUG, "%s: Got priority %d from ${QUEUE_PRIO}.\n", - chan->name, prio); - } else { - ast_log(LOG_WARNING, "${QUEUE_PRIO}: Invalid value (%s), channel %s.\n", - user_priority, chan->name); - prio = 0; - } - } else { - if (option_debug > 2) - ast_log(LOG_DEBUG, "NO QUEUE_PRIO variable found. Using default.\n"); - prio = 0; - } - - if (options && (strchr(options, 'r'))) - ringing = 1; - - if (option_debug) - ast_log(LOG_DEBUG, "queue: %s, options: %s, url: %s, announce: %s, expires: %ld, priority: %d\n", - queuename, options, url, announceoverride, (long)qe.expire, (int)prio); - - qe.chan = chan; - qe.prio = (int)prio; - qe.last_pos_said = 0; - qe.last_pos = 0; - if (!join_queue(queuename, &qe, &reason)) { - ast_queue_log(queuename, chan->uniqueid, "NONE", "ENTERQUEUE", "%s|%s", url ? url : "", - chan->cid.cid_num ? chan->cid.cid_num : ""); -check_turns: - if (ringing) { - ast_indicate(chan, AST_CONTROL_RINGING); - } else { - ast_moh_start(chan, qe.moh); - } - for (;;) { - /* This is the wait loop for callers 2 through maxlen */ - - res = wait_our_turn(&qe, ringing, &reason); - /* If they hungup, return immediately */ - if (res < 0) { - /* Record this abandoned call */ - record_abandoned(&qe); - ast_queue_log(queuename, chan->uniqueid, "NONE", "ABANDON", "%d|%d|%ld", qe.pos, qe.opos, (long)time(NULL) - qe.start); - if (option_verbose > 2) { - ast_verbose(VERBOSE_PREFIX_3 "User disconnected from queue %s while waiting their turn\n", queuename); - res = -1; - } - break; - } - if (!res) - break; - if (valid_exit(&qe, res)) { - ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHKEY", "%c|%d", res, qe.pos); - break; - } - } - if (!res) { - int makeannouncement = 0; - for (;;) { - /* This is the wait loop for the head caller*/ - /* To exit, they may get their call answered; */ - /* they may dial a digit from the queue context; */ - /* or, they may timeout. */ - - enum queue_member_status stat; - - /* Leave if we have exceeded our queuetimeout */ - if (qe.expire && (time(NULL) > qe.expire)) { - reason = QUEUE_TIMEOUT; - res = 0; - break; - } - - if (makeannouncement) { - /* Make a position announcement, if enabled */ - if (qe.parent->announcefrequency && !ringing) - say_position(&qe); - } - makeannouncement = 1; - - /* Try calling all queue members for 'timeout' seconds */ - res = try_calling(&qe, options, announceoverride, url, &go_on); - if (res) { - if (res < 0) { - if (!qe.handled) - ast_queue_log(queuename, chan->uniqueid, "NONE", "ABANDON", "%d|%d|%ld", qe.pos, qe.opos, (long)time(NULL) - qe.start); - } else if (res > 0) - ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHKEY", "%c|%d", res, qe.pos); - break; - } - - stat = get_member_status(qe.parent); - - /* leave the queue if no agents, if enabled */ - if (qe.parent->leavewhenempty && (stat == QUEUE_NO_MEMBERS)) { - reason = QUEUE_LEAVEEMPTY; - res = 0; - break; - } - - /* leave the queue if no reachable agents, if enabled */ - if ((qe.parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS)) { - reason = QUEUE_LEAVEUNAVAIL; - res = 0; - break; - } - - /* Leave if we have exceeded our queuetimeout */ - if (qe.expire && (time(NULL) > qe.expire)) { - reason = QUEUE_TIMEOUT; - res = 0; - break; - } - - /* OK, we didn't get anybody; wait for 'retry' seconds; may get a digit to exit with */ - res = wait_a_bit(&qe); - if (res < 0) { - ast_queue_log(queuename, chan->uniqueid, "NONE", "ABANDON", "%d|%d|%ld", qe.pos, qe.opos, (long)time(NULL) - qe.start); - if (option_verbose > 2) { - ast_verbose(VERBOSE_PREFIX_3 "User disconnected from queue %s when they almost made it\n", queuename); - res = -1; - } - break; - } - if (res && valid_exit(&qe, res)) { - ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHKEY", "%c|%d", res, qe.pos); - break; - } - /* exit after 'timeout' cycle if 'n' option enabled */ - if (go_on) { - if (option_verbose > 2) { - ast_verbose(VERBOSE_PREFIX_3 "Exiting on time-out cycle\n"); - res = -1; - } - ast_queue_log(queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos); - reason = QUEUE_TIMEOUT; - res = 0; - break; - } - /* Since this is a priority queue and - * it is not sure that we are still at the head - * of the queue, go and check for our turn again. - */ - if (!is_our_turn(&qe)) { - if (option_debug) - ast_log(LOG_DEBUG, "Darn priorities, going back in queue (%s)!\n", - qe.chan->name); - goto check_turns; - } - } - } - /* Don't allow return code > 0 */ - if (res >= 0 && res != AST_PBX_KEEPALIVE) { - res = 0; - if (ringing) { - ast_indicate(chan, -1); - } else { - ast_moh_stop(chan); - } - ast_stopstream(chan); - } - leave_queue(&qe); - if (reason != QUEUE_UNKNOWN) - set_queue_result(chan, reason); - } else { - ast_log(LOG_WARNING, "Unable to join queue '%s'\n", queuename); - set_queue_result(chan, reason); - res = 0; - } - LOCAL_USER_REMOVE(u); - return res; -} - -static void reload_queues(void) -{ - struct ast_call_queue *q, *ql, *qn; - struct ast_config *cfg; - char *cat, *tmp; - struct ast_variable *var; - struct member *prev, *cur; - int new; - char *general_val = NULL; - - cfg = ast_config_load("queues.conf"); - if (!cfg) { - ast_log(LOG_NOTICE, "No call queueing config file (queues.conf), so no call queues\n"); - return; - } - ast_mutex_lock(&qlock); - use_weight=0; - /* Mark all queues as dead for the moment */ - q = queues; - while(q) { - q->dead = 1; - q = q->next; - } - /* Chug through config file */ - cat = ast_category_browse(cfg, NULL); - while(cat) { - if (strcasecmp(cat, "general")) { /* Define queue */ - /* Look for an existing one */ - q = queues; - while(q) { - if (!strcmp(q->name, cat)) - break; - q = q->next; - } - if (!q) { - /* Make one then */ - q = malloc(sizeof(struct ast_call_queue)); - if (q) { - /* Initialize it */ - memset(q, 0, sizeof(struct ast_call_queue)); - ast_mutex_init(&q->lock); - strncpy(q->name, cat, sizeof(q->name) - 1); - new = 1; - } else new = 0; - } else - new = 0; - if (q) { - if (!new) - ast_mutex_lock(&q->lock); - /* Re-initialize the queue */ - q->dead = 0; - q->retry = DEFAULT_RETRY; - q->timeout = -1; - q->maxlen = 0; - q->announcefrequency = 0; - q->announceholdtime = 0; - q->roundingseconds = 0; /* Default - don't announce seconds */ - q->holdtime = 0; - q->callscompleted = 0; - q->callsabandoned = 0; - q->callscompletedinsl = 0; - q->servicelevel = 0; - q->wrapuptime = 0; - free_members(q, 0); - q->moh[0] = '\0'; - q->announce[0] = '\0'; - q->context[0] = '\0'; - q->monfmt[0] = '\0'; - strncpy(q->sound_next, "queue-youarenext", sizeof(q->sound_next) - 1); - strncpy(q->sound_thereare, "queue-thereare", sizeof(q->sound_thereare) - 1); - strncpy(q->sound_calls, "queue-callswaiting", sizeof(q->sound_calls) - 1); - strncpy(q->sound_holdtime, "queue-holdtime", sizeof(q->sound_holdtime) - 1); - strncpy(q->sound_minutes, "queue-minutes", sizeof(q->sound_minutes) - 1); - strncpy(q->sound_seconds, "queue-seconds", sizeof(q->sound_seconds) - 1); - strncpy(q->sound_thanks, "queue-thankyou", sizeof(q->sound_thanks) - 1); - strncpy(q->sound_lessthan, "queue-less-than", sizeof(q->sound_lessthan) - 1); - strncpy(q->sound_reporthold, "queue-reporthold", sizeof(q->sound_reporthold) - 1); - prev = q->members; - if (prev) { - /* find the end of any dynamic members */ - while(prev->next) - prev = prev->next; - } - var = ast_variable_browse(cfg, cat); - while(var) { - if (!strcasecmp(var->name, "member")) { - /* Add a new member */ - cur = malloc(sizeof(struct member)); - if (cur) { - memset(cur, 0, sizeof(struct member)); - strncpy(cur->interface, var->value, sizeof(cur->interface) - 1); - if ((tmp = strchr(cur->interface, ','))) { - *tmp = '\0'; - tmp++; - cur->penalty = atoi(tmp); - if (cur->penalty < 0) - cur->penalty = 0; - } - if (!strchr(cur->interface, '/')) - ast_log(LOG_WARNING, "No location at line %d of queue.conf\n", var->lineno); - if (prev) - prev->next = cur; - else - q->members = cur; - prev = cur; - } - } else if (!strcasecmp(var->name, "music") || !strcasecmp(var->name, "musiconhold")) { - strncpy(q->moh, var->value, sizeof(q->moh) - 1); - } else if (!strcasecmp(var->name, "announce")) { - strncpy(q->announce, var->value, sizeof(q->announce) - 1); - } else if (!strcasecmp(var->name, "context")) { - strncpy(q->context, var->value, sizeof(q->context) - 1); - } else if (!strcasecmp(var->name, "timeout")) { - q->timeout = atoi(var->value); - } else if (!strcasecmp(var->name, "monitor-join")) { - q->monjoin = ast_true(var->value); - } else if (!strcasecmp(var->name, "monitor-format")) { - strncpy(q->monfmt, var->value, sizeof(q->monfmt) - 1); - } else if (!strcasecmp(var->name, "queue-youarenext")) { - strncpy(q->sound_next, var->value, sizeof(q->sound_next) - 1); - } else if (!strcasecmp(var->name, "queue-thereare")) { - strncpy(q->sound_thereare, var->value, sizeof(q->sound_thereare) - 1); - } else if (!strcasecmp(var->name, "queue-callswaiting")) { - strncpy(q->sound_calls, var->value, sizeof(q->sound_calls) - 1); - } else if (!strcasecmp(var->name, "queue-holdtime")) { - strncpy(q->sound_holdtime, var->value, sizeof(q->sound_holdtime) - 1); - } else if (!strcasecmp(var->name, "queue-minutes")) { - strncpy(q->sound_minutes, var->value, sizeof(q->sound_minutes) - 1); - } else if (!strcasecmp(var->name, "queue-seconds")) { - strncpy(q->sound_seconds, var->value, sizeof(q->sound_seconds) - 1); - } else if (!strcasecmp(var->name, "queue-lessthan")) { - strncpy(q->sound_lessthan, var->value, sizeof(q->sound_lessthan) - 1); - } else if (!strcasecmp(var->name, "queue-thankyou")) { - strncpy(q->sound_thanks, var->value, sizeof(q->sound_thanks) - 1); - } else if (!strcasecmp(var->name, "queue-reporthold")) { - strncpy(q->sound_reporthold, var->value, sizeof(q->sound_reporthold) - 1); - } else if (!strcasecmp(var->name, "announce-frequency")) { - q->announcefrequency = atoi(var->value); - } else if (!strcasecmp(var->name, "announce-round-seconds")) { - q->roundingseconds = atoi(var->value); - if(q->roundingseconds>60 || q->roundingseconds<0) { - ast_log(LOG_WARNING, "'%s' isn't a valid value for queue-rounding-seconds using 0 instead at line %d of queue.conf\n", var->value, var->lineno); - q->roundingseconds=0; - } - } else if (!strcasecmp(var->name, "announce-holdtime")) { - if (!strcasecmp(var->value, "once")) - q->announceholdtime = ANNOUNCEHOLDTIME_ONCE; - else if (ast_true(var->value)) - q->announceholdtime = ANNOUNCEHOLDTIME_ALWAYS; - else - q->announceholdtime = 0; - } else if (!strcasecmp(var->name, "retry")) { - q->retry = atoi(var->value); - } else if (!strcasecmp(var->name, "wrapuptime")) { - q->wrapuptime = atoi(var->value); - } else if (!strcasecmp(var->name, "maxlen")) { - q->maxlen = atoi(var->value); - } else if (!strcasecmp(var->name, "servicelevel")) { - q->servicelevel= atoi(var->value); - } else if (!strcasecmp(var->name, "strategy")) { - q->strategy = strat2int(var->value); - if (q->strategy < 0) { - ast_log(LOG_WARNING, "'%s' isn't a valid strategy, using ringall instead\n", var->value); - q->strategy = 0; - } - } else if (!strcasecmp(var->name, "joinempty")) { - if (!strcasecmp(var->value, "strict")) - q->joinempty = QUEUE_EMPTY_STRICT; - else if (ast_true(var->value)) - q->joinempty = QUEUE_EMPTY_NORMAL; - else - q->joinempty = 0; - } else if (!strcasecmp(var->name, "leavewhenempty")) { - if (!strcasecmp(var->value, "strict")) - q->leavewhenempty = QUEUE_EMPTY_STRICT; - else if (ast_true(var->value)) - q->leavewhenempty = QUEUE_EMPTY_NORMAL; - else - q->leavewhenempty = 0; - } else if (!strcasecmp(var->name, "eventwhencalled")) { - q->eventwhencalled = ast_true(var->value); - } else if (!strcasecmp(var->name, "reportholdtime")) { - q->reportholdtime = ast_true(var->value); - } else if (!strcasecmp(var->name, "memberdelay")) { - q->memberdelay = atoi(var->value); - } else if (!strcasecmp(var->name, "weight")) { - q->weight = atoi(var->value); - if (q->weight) - use_weight++; - } else if (!strcasecmp(var->name, "timeoutrestart")) { - q->timeoutrestart = ast_true(var->value); - } else { - ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s at line %d of queue.conf\n", cat, var->name, var->lineno); - } - var = var->next; - } - if (q->retry < 0) - q->retry = DEFAULT_RETRY; - if (q->timeout < 0) - q->timeout = DEFAULT_TIMEOUT; - if (q->maxlen < 0) - q->maxlen = 0; - if (!new) - ast_mutex_unlock(&q->lock); - if (new) { - q->next = queues; - queues = q; - } - } - } else { - /* Initialize global settings */ - queue_persistent_members = 0; - if ((general_val = ast_variable_retrieve(cfg, "general", "persistentmembers"))) - queue_persistent_members = ast_true(general_val); - } - cat = ast_category_browse(cfg, cat); - } - ast_config_destroy(cfg); - q = queues; - ql = NULL; - while(q) { - qn = q->next; - if (q->dead) { - if (ql) - ql->next = q->next; - else - queues = q->next; - if (!q->count) { - free(q); - } else - ast_log(LOG_WARNING, "XXX Leaking a little memory :( XXX\n"); - } else { - for (cur = q->members; cur; cur = cur->next) - cur->status = ast_device_state(cur->interface); - ql = q; - } - q = qn; - } - ast_mutex_unlock(&qlock); -} - -static char *status2str(int status, char *buf, int buflen) -{ - switch(status) { - case AST_DEVICE_UNKNOWN: - strncpy(buf, "unknown", buflen - 1); - break; - case AST_DEVICE_NOT_INUSE: - strncpy(buf, "notinuse", buflen - 1); - break; - case AST_DEVICE_INUSE: - strncpy(buf, "inuse", buflen - 1); - break; - case AST_DEVICE_BUSY: - strncpy(buf, "busy", buflen - 1); - break; - case AST_DEVICE_INVALID: - strncpy(buf, "invalid", buflen - 1); - break; - case AST_DEVICE_UNAVAILABLE: - strncpy(buf, "unavailable", buflen - 1); - break; - default: - snprintf(buf, buflen, "unknown status %d", status); - } - return buf; -} - -static int __queues_show(int fd, int argc, char **argv, int queue_show) -{ - struct ast_call_queue *q; - struct queue_ent *qe; - struct member *mem; - int pos; - time_t now; - char max[80] = ""; - char calls[80] = ""; - char tmpbuf[80] = ""; - float sl = 0; - - time(&now); - if ((!queue_show && argc != 2) || (queue_show && argc != 3)) - return RESULT_SHOWUSAGE; - ast_mutex_lock(&qlock); - q = queues; - if (!q) { - ast_mutex_unlock(&qlock); - if (queue_show) - ast_cli(fd, "No such queue: %s.\n",argv[2]); - else - ast_cli(fd, "No queues.\n"); - return RESULT_SUCCESS; - } - while(q) { - ast_mutex_lock(&q->lock); - if (queue_show) { - if (strcasecmp(q->name, argv[2]) != 0) { - ast_mutex_unlock(&q->lock); - q = q->next; - if (!q) { - ast_cli(fd, "No such queue: %s.\n",argv[2]); - break; - } - continue; - } - } - if (q->maxlen) - snprintf(max, sizeof(max), "%d", q->maxlen); - else - strncpy(max, "unlimited", sizeof(max) - 1); - sl = 0; - if(q->callscompleted > 0) - sl = 100*((float)q->callscompletedinsl/(float)q->callscompleted); - ast_cli(fd, "%-12.12s has %d calls (max %s) in '%s' strategy (%ds holdtime), W:%d, C:%d, A:%d, SL:%2.1f%% within %ds\n", - q->name, q->count, max, int2strat(q->strategy), q->holdtime, q->weight, q->callscompleted, q->callsabandoned,sl,q->servicelevel); - if (q->members) { - ast_cli(fd, " Members: \n"); - for (mem = q->members; mem; mem = mem->next) { - if (mem->penalty) - snprintf(max, sizeof(max) - 20, " with penalty %d", mem->penalty); - else - max[0] = '\0'; - if (mem->dynamic) - strncat(max, " (dynamic)", sizeof(max) - strlen(max) - 1); - if (mem->paused) - strncat(max, " (paused)", sizeof(max) - strlen(max) - 1); - if (mem->status) - snprintf(max + strlen(max), sizeof(max) - strlen(max), " (%s)", status2str(mem->status, tmpbuf, sizeof(tmpbuf))); - if (mem->calls) { - snprintf(calls, sizeof(calls), " has taken %d calls (last was %ld secs ago)", - mem->calls, (long)(time(NULL) - mem->lastcall)); - } else - strncpy(calls, " has taken no calls yet", sizeof(calls) - 1); - ast_cli(fd, " %s%s%s\n", mem->interface, max, calls); - } - } else - ast_cli(fd, " No Members\n"); - if (q->head) { - pos = 1; - ast_cli(fd, " Callers: \n"); - for (qe = q->head; qe; qe = qe->next) - ast_cli(fd, " %d. %s (wait: %ld:%2.2ld, prio: %d)\n", pos++, qe->chan->name, - (long)(now - qe->start) / 60, (long)(now - qe->start) % 60, qe->prio); - } else - ast_cli(fd, " No Callers\n"); - ast_cli(fd, "\n"); - ast_mutex_unlock(&q->lock); - q = q->next; - if (queue_show) - break; - } - ast_mutex_unlock(&qlock); - return RESULT_SUCCESS; -} - -static int queues_show(int fd, int argc, char **argv) -{ - return __queues_show(fd, argc, argv, 0); -} - -static int queue_show(int fd, int argc, char **argv) -{ - return __queues_show(fd, argc, argv, 1); -} - -static char *complete_queue(char *line, char *word, int pos, int state) -{ - struct ast_call_queue *q; - int which=0; - - ast_mutex_lock(&qlock); - for (q = queues; q; q = q->next) { - if (!strncasecmp(word, q->name, strlen(word))) { - if (++which > state) - break; - } - } - ast_mutex_unlock(&qlock); - return q ? strdup(q->name) : NULL; -} - -/* JDG: callback to display queues status in manager */ -static int manager_queues_show( struct mansession *s, struct message *m ) -{ - char *a[] = { "show", "queues" }; - return queues_show(s->fd, 2, a); -} - - -/* Dump queue status */ -static int manager_queues_status( struct mansession *s, struct message *m ) -{ - time_t now; - int pos; - char *id = astman_get_header(m,"ActionID"); - char idText[256] = ""; - struct ast_call_queue *q; - struct queue_ent *qe; - float sl = 0; - struct member *mem; - - astman_send_ack(s, m, "Queue status will follow"); - time(&now); - ast_mutex_lock(&qlock); - if (!ast_strlen_zero(id)) { - snprintf(idText,256,"ActionID: %s\r\n",id); - } - for (q = queues; q; q = q->next) { - ast_mutex_lock(&q->lock); - - /* List queue properties */ - if(q->callscompleted > 0) - sl = 100*((float)q->callscompletedinsl/(float)q->callscompleted); - ast_mutex_lock(&s->lock); - ast_cli(s->fd, "Event: QueueParams\r\n" - "Queue: %s\r\n" - "Max: %d\r\n" - "Calls: %d\r\n" - "Holdtime: %d\r\n" - "Completed: %d\r\n" - "Abandoned: %d\r\n" - "ServiceLevel: %d\r\n" - "ServicelevelPerf: %2.1f\r\n" - "Weight: %d\r\n" - "%s" - "\r\n", - q->name, q->maxlen, q->count, q->holdtime, q->callscompleted, - q->callsabandoned, q->servicelevel, sl, q->weight, idText); - - /* List Queue Members */ - for (mem = q->members; mem; mem = mem->next) - ast_cli(s->fd, "Event: QueueMember\r\n" - "Queue: %s\r\n" - "Location: %s\r\n" - "Membership: %s\r\n" - "Penalty: %d\r\n" - "CallsTaken: %d\r\n" - "LastCall: %ld\r\n" - "Status: %d\r\n" - "Paused: %d\r\n" - "%s" - "\r\n", - q->name, mem->interface, mem->dynamic ? "dynamic" : "static", - mem->penalty, mem->calls, mem->lastcall, mem->status, mem->paused, idText); - - /* List Queue Entries */ - - pos = 1; - for (qe = q->head; qe; qe = qe->next) - ast_cli(s->fd, "Event: QueueEntry\r\n" - "Queue: %s\r\n" - "Position: %d\r\n" - "Channel: %s\r\n" - "CallerID: %s\r\n" - "CallerIDName: %s\r\n" - "Wait: %ld\r\n" - "%s" - "\r\n", - q->name, pos++, qe->chan->name, - qe->chan->cid.cid_num ? qe->chan->cid.cid_num : "unknown", - qe->chan->cid.cid_name ? qe->chan->cid.cid_name : "unknown", - (long)(now - qe->start), idText); - ast_mutex_unlock(&s->lock); - ast_mutex_unlock(&q->lock); - } - ast_mutex_unlock(&qlock); - return RESULT_SUCCESS; -} - -static int manager_add_queue_member(struct mansession *s, struct message *m) -{ - char *queuename, *interface, *penalty_s, *paused_s; - int paused, penalty = 0; - - queuename = astman_get_header(m, "Queue"); - interface = astman_get_header(m, "Interface"); - penalty_s = astman_get_header(m, "Penalty"); - paused_s = astman_get_header(m, "Paused"); - - if (ast_strlen_zero(queuename)) { - astman_send_error(s, m, "'Queue' not specified."); - return 0; - } - - if (ast_strlen_zero(interface)) { - astman_send_error(s, m, "'Interface' not specified."); - return 0; - } - - if (ast_strlen_zero(penalty_s)) - penalty = 0; - else if (sscanf(penalty_s, "%d", &penalty) != 1) { - penalty = 0; - } - - if (ast_strlen_zero(paused_s)) - paused = 0; - else - paused = abs(ast_true(paused_s)); - - switch (add_to_queue(queuename, interface, penalty, paused, queue_persistent_members)) { - case RES_OKAY: - astman_send_ack(s, m, "Added interface to queue"); - break; - case RES_EXISTS: - astman_send_error(s, m, "Unable to add interface: Already there"); - break; - case RES_NOSUCHQUEUE: - astman_send_error(s, m, "Unable to add interface to queue: No such queue"); - break; - case RES_OUTOFMEMORY: - astman_send_error(s, m, "Out of memory"); - break; - } - return 0; -} - -static int manager_remove_queue_member(struct mansession *s, struct message *m) -{ - char *queuename, *interface; - - queuename = astman_get_header(m, "Queue"); - interface = astman_get_header(m, "Interface"); - - if (ast_strlen_zero(queuename) || ast_strlen_zero(interface)) { - astman_send_error(s, m, "Need 'Queue' and 'Interface' parameters."); - return 0; - } - - switch (remove_from_queue(queuename, interface)) { - case RES_OKAY: - astman_send_ack(s, m, "Removed interface from queue"); - break; - case RES_EXISTS: - astman_send_error(s, m, "Unable to remove interface: Not there"); - break; - case RES_NOSUCHQUEUE: - astman_send_error(s, m, "Unable to remove interface from queue: No such queue"); - break; - case RES_OUTOFMEMORY: - astman_send_error(s, m, "Out of memory"); - break; - } - return 0; -} - -static int manager_pause_queue_member(struct mansession *s, struct message *m) -{ - char *queuename, *interface, *paused_s; - int paused; - - interface = astman_get_header(m, "Interface"); - paused_s = astman_get_header(m, "Paused"); - queuename = astman_get_header(m, "Queue"); /* Optional - if not supplied, pause the given Interface in all queues */ - - if (ast_strlen_zero(interface) || ast_strlen_zero(paused_s)) { - astman_send_error(s, m, "Need 'Interface' and 'Paused' parameters."); - return 0; - } - - paused = abs(ast_true(paused_s)); - - if (set_member_paused(queuename, interface, paused)) - astman_send_error(s, m, "Interface not found"); - else - if (paused) - astman_send_ack(s, m, "Interface paused successfully"); - else - astman_send_ack(s, m, "Interface unpaused successfully"); - - return 0; -} - -static int handle_add_queue_member(int fd, int argc, char *argv[]) -{ - char *queuename, *interface; - int penalty; - - if ((argc != 6) && (argc != 8)) { - return RESULT_SHOWUSAGE; - } else if (strcmp(argv[4], "to")) { - return RESULT_SHOWUSAGE; - } else if ((argc == 8) && strcmp(argv[6], "penalty")) { - return RESULT_SHOWUSAGE; - } - - queuename = argv[5]; - interface = argv[3]; - if (argc == 8) { - if (sscanf(argv[7], "%d", &penalty) == 1) { - if (penalty < 0) { - ast_cli(fd, "Penalty must be >= 0\n"); - penalty = 0; - } - } else { - ast_cli(fd, "Penalty must be an integer >= 0\n"); - penalty = 0; - } - } else { - penalty = 0; - } - - switch (add_to_queue(queuename, interface, penalty, 0, queue_persistent_members)) { - case RES_OKAY: - ast_cli(fd, "Added interface '%s' to queue '%s'\n", interface, queuename); - return RESULT_SUCCESS; - case RES_EXISTS: - ast_cli(fd, "Unable to add interface '%s' to queue '%s': Already there\n", interface, queuename); - return RESULT_FAILURE; - case RES_NOSUCHQUEUE: - ast_cli(fd, "Unable to add interface to queue '%s': No such queue\n", queuename); - return RESULT_FAILURE; - case RES_OUTOFMEMORY: - ast_cli(fd, "Out of memory\n"); - return RESULT_FAILURE; - default: - return RESULT_FAILURE; - } -} - -static char *complete_add_queue_member(char *line, char *word, int pos, int state) -{ - /* 0 - add; 1 - queue; 2 - member; 3 - ; 4 - to; 5 - ; 6 - penalty; 7 - */ - switch (pos) { - case 3: - /* Don't attempt to complete name of member (infinite possibilities) */ - return NULL; - case 4: - if (state == 0) { - return strdup("to"); - } else { - return NULL; - } - case 5: - /* No need to duplicate code */ - return complete_queue(line, word, pos, state); - case 6: - if (state == 0) { - return strdup("penalty"); - } else { - return NULL; - } - case 7: - if (state < 100) { /* 0-99 */ - char *num = malloc(3); - if (num) { - sprintf(num, "%d", state); - } - return num; - } else { - return NULL; - } - default: - return NULL; - } -} - -static int handle_remove_queue_member(int fd, int argc, char *argv[]) -{ - char *queuename, *interface; - - if (argc != 6) { - return RESULT_SHOWUSAGE; - } else if (strcmp(argv[4], "from")) { - return RESULT_SHOWUSAGE; - } - - queuename = argv[5]; - interface = argv[3]; - - switch (remove_from_queue(queuename, interface)) { - case RES_OKAY: - ast_cli(fd, "Removed interface '%s' from queue '%s'\n", interface, queuename); - return RESULT_SUCCESS; - case RES_EXISTS: - ast_cli(fd, "Unable to remove interface '%s' from queue '%s': Not there\n", interface, queuename); - return RESULT_FAILURE; - case RES_NOSUCHQUEUE: - ast_cli(fd, "Unable to remove interface from queue '%s': No such queue\n", queuename); - return RESULT_FAILURE; - case RES_OUTOFMEMORY: - ast_cli(fd, "Out of memory\n"); - return RESULT_FAILURE; - default: - return RESULT_FAILURE; - } -} - -static char *complete_remove_queue_member(char *line, char *word, int pos, int state) -{ - int which = 0; - struct ast_call_queue *q; - struct member *m; - - /* 0 - add; 1 - queue; 2 - member; 3 - ; 4 - to; 5 - */ - if ((pos > 5) || (pos < 3)) { - return NULL; - } - if (pos == 4) { - if (state == 0) { - return strdup("from"); - } else { - return NULL; - } - } - - if (pos == 5) { - /* No need to duplicate code */ - return complete_queue(line, word, pos, state); - } - - if (queues != NULL) { - for (q = queues ; q ; q = q->next) { - ast_mutex_lock(&q->lock); - for (m = q->members ; m ; m = m->next) { - if (++which > state) { - ast_mutex_unlock(&q->lock); - return strdup(m->interface); - } - } - ast_mutex_unlock(&q->lock); - } - } - return NULL; -} - -static char show_queues_usage[] = -"Usage: show queues\n" -" Provides summary information on call queues.\n"; - -static struct ast_cli_entry cli_show_queues = { - { "show", "queues", NULL }, queues_show, - "Show status of queues", show_queues_usage, NULL }; - -static char show_queue_usage[] = -"Usage: show queue\n" -" Provides summary information on a specified queue.\n"; - -static struct ast_cli_entry cli_show_queue = { - { "show", "queue", NULL }, queue_show, - "Show status of a specified queue", show_queue_usage, complete_queue }; - -static char aqm_cmd_usage[] = -"Usage: add queue member to [penalty ]\n"; - -static struct ast_cli_entry cli_add_queue_member = { - { "add", "queue", "member", NULL }, handle_add_queue_member, - "Add a channel to a specified queue", aqm_cmd_usage, complete_add_queue_member }; - -static char rqm_cmd_usage[] = -"Usage: remove queue member from \n"; - -static struct ast_cli_entry cli_remove_queue_member = { - { "remove", "queue", "member", NULL }, handle_remove_queue_member, - "Removes a channel from a specified queue", rqm_cmd_usage, complete_remove_queue_member }; - -int unload_module(void) -{ - STANDARD_HANGUP_LOCALUSERS; - ast_cli_unregister(&cli_show_queue); - ast_cli_unregister(&cli_show_queues); - ast_cli_unregister(&cli_add_queue_member); - ast_cli_unregister(&cli_remove_queue_member); - ast_manager_unregister("Queues"); - ast_manager_unregister("QueueStatus"); - ast_manager_unregister("QueueAdd"); - ast_manager_unregister("QueueRemove"); - ast_manager_unregister("QueuePause"); - ast_devstate_del(statechange_queue, NULL); - ast_unregister_application(app_aqm); - ast_unregister_application(app_rqm); - ast_unregister_application(app_pqm); - ast_unregister_application(app_upqm); - return ast_unregister_application(app); -} - -int load_module(void) -{ - int res; - res = ast_register_application(app, queue_exec, synopsis, descrip); - if (!res) { - ast_cli_register(&cli_show_queue); - ast_cli_register(&cli_show_queues); - ast_cli_register(&cli_add_queue_member); - ast_cli_register(&cli_remove_queue_member); - ast_devstate_add(statechange_queue, NULL); - ast_manager_register( "Queues", 0, manager_queues_show, "Queues" ); - ast_manager_register( "QueueStatus", 0, manager_queues_status, "Queue Status" ); - ast_manager_register( "QueueAdd", EVENT_FLAG_AGENT, manager_add_queue_member, "Add interface to queue." ); - ast_manager_register( "QueueRemove", EVENT_FLAG_AGENT, manager_remove_queue_member, "Remove interface from queue." ); - ast_manager_register( "QueuePause", EVENT_FLAG_AGENT, manager_pause_queue_member, "Makes a queue member temporarily unavailable" ); - ast_register_application(app_aqm, aqm_exec, app_aqm_synopsis, app_aqm_descrip) ; - ast_register_application(app_rqm, rqm_exec, app_rqm_synopsis, app_rqm_descrip) ; - ast_register_application(app_pqm, pqm_exec, app_pqm_synopsis, app_pqm_descrip) ; - ast_register_application(app_upqm, upqm_exec, app_upqm_synopsis, app_upqm_descrip) ; - } - reload_queues(); - - if (queue_persistent_members) - reload_queue_members(); - - return res; -} - - -int reload(void) -{ - reload_queues(); - return 0; -} - -char *description(void) -{ - return tdesc; -} - -int usecount(void) -{ - int res; - STANDARD_USECOUNT(res); - return res; -} - -char *key() -{ - return ASTERISK_GPL_KEY; -} Index: channels/chan_agent.c =================================================================== RCS file: /usr/cvsroot/asterisk/channels/chan_agent.c,v retrieving revision 1.128 diff -u -u -r1.128 chan_agent.c --- channels/chan_agent.c 28 Mar 2005 20:48:24 -0000 1.128 +++ channels/chan_agent.c 1 Apr 2005 22:06:17 -0000 @@ -1,2193 +0,0 @@ -/* - * Asterisk -- A telephony toolkit for Linux. - * - * Implementation of Agents - * - * Copyright (C) 1999 - 2005, Digium Inc. - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static const char desc[] = "Agent Proxy Channel"; -static const char channeltype[] = "Agent"; -static const char tdesc[] = "Call Agent Proxy Channel"; -static const char config[] = "agents.conf"; - -static const char app[] = "AgentLogin"; -static const char app2[] = "AgentCallbackLogin"; -static const char app3[] = "AgentMonitorOutgoing"; - -static const char synopsis[] = "Call agent login"; -static const char synopsis2[] = "Call agent callback login"; -static const char synopsis3[] = "Record agent's outgoing call"; - -static const char descrip[] = -" AgentLogin([AgentNo][|options]):\n" -"Asks the agent to login to the system. Always returns -1. While\n" -"logged in, the agent can receive calls and will hear a 'beep'\n" -"when a new call comes in. The agent can dump the call by pressing\n" -"the star key.\n" -"The option string may contain zero or more of the following characters:\n" -" 's' -- silent login - do not announce the login ok segment after agent logged in/off\n"; - -static const char descrip2[] = -" AgentCallbackLogin([AgentNo][|[options][exten]@context]):\n" -"Asks the agent to login to the system with callback.\n" -"The agent's callback extension is called (optionally with the specified\n" -"context).\n" -"The option string may contain zero or more of the following characters:\n" -" 's' -- silent login - do not announce the login ok segment agent logged in/off\n"; - -static const char descrip3[] = -" AgentMonitorOutgoing([options]):\n" -"Tries to figure out the id of the agent who is placing outgoing call based on\n" -"comparision of the callerid of the current interface and the global variable \n" -"placed by the AgentCallbackLogin application. That's why it should be used only\n" -"with the AgentCallbackLogin app. Uses the monitoring functions in chan_agent \n" -"instead of Monitor application. That have to be configured in the agents.conf file.\n" -"\nReturn value:\n" -"Normally the app returns 0 unless the options are passed. Also if the callerid or\n" -"the agentid are not specified it'll look for n+101 priority.\n" -"\nOptions:\n" -" 'd' - make the app return -1 if there is an error condition and there is\n" -" no extension n+101\n" -" 'c' - change the CDR so that the source of the call is 'Agent/agent_id'\n" -" 'n' - don't generate the warnings when there is no callerid or the\n" -" agentid is not known.\n" -" It's handy if you want to have one context for agent and non-agent calls.\n"; - -static const char mandescr_agents[] = -"Description: Will list info about all possible agents.\n" -"Variables: NONE\n"; - -static char moh[80] = "default"; - -#define AST_MAX_AGENT 80 /* Agent ID or Password max length */ -#define AST_MAX_BUF 256 -#define AST_MAX_FILENAME_LEN 256 - -/* Persistent Agents astdb family */ -static const char pa_family[] = "/Agents"; -/* The maximum lengh of each persistent member agent database entry */ -#define PA_MAX_LEN 2048 -/* queues.conf [general] option */ -static int persistent_agents = 0; -static void dump_agents(void); - -static ast_group_t group; -static int autologoff; -static int wrapuptime; -static int ackcall; - -static int maxlogintries = 3; -static char agentgoodbye[AST_MAX_FILENAME_LEN] = "vm-goodbye"; - -static int usecnt =0; -AST_MUTEX_DEFINE_STATIC(usecnt_lock); - -/* Protect the interface list (of pvt's) */ -AST_MUTEX_DEFINE_STATIC(agentlock); - -static int recordagentcalls = 0; -static char recordformat[AST_MAX_BUF] = ""; -static char recordformatext[AST_MAX_BUF] = ""; -static int createlink = 0; -static char urlprefix[AST_MAX_BUF] = ""; -static char savecallsin[AST_MAX_BUF] = ""; -static int updatecdr = 0; -static char beep[AST_MAX_BUF] = "beep"; - -#define GETAGENTBYCALLERID "AGENTBYCALLERID" - -static struct agent_pvt { - ast_mutex_t lock; /* Channel private lock */ - int dead; /* Poised for destruction? */ - int pending; /* Not a real agent -- just pending a match */ - int abouttograb; /* About to grab */ - int autologoff; /* Auto timeout time */ - int ackcall; /* ackcall */ - time_t loginstart; /* When agent first logged in (0 when logged off) */ - time_t start; /* When call started */ - struct timeval lastdisc; /* When last disconnected */ - int wrapuptime; /* Wrapup time in ms */ - ast_group_t group; /* Group memberships */ - int acknowledged; /* Acknowledged */ - char moh[80]; /* Which music on hold */ - char agent[AST_MAX_AGENT]; /* Agent ID */ - char password[AST_MAX_AGENT]; /* Password for Agent login */ - char name[AST_MAX_AGENT]; - ast_mutex_t app_lock; /* Synchronization between owning applications */ - volatile pthread_t owning_app; /* Owning application thread id */ - volatile int app_sleep_cond; /* Sleep condition for the login app */ - struct ast_channel *owner; /* Agent */ - char loginchan[80]; - struct ast_channel *chan; /* Channel we use */ - struct agent_pvt *next; /* Agent */ -} *agents = NULL; - -#define CHECK_FORMATS(ast, p) do { \ - if (p->chan) {\ - if (ast->nativeformats != p->chan->nativeformats) { \ - ast_log(LOG_DEBUG, "Native formats changing from %d to %d\n", ast->nativeformats, p->chan->nativeformats); \ - /* Native formats changed, reset things */ \ - ast->nativeformats = p->chan->nativeformats; \ - ast_log(LOG_DEBUG, "Resetting read to %d and write to %d\n", ast->readformat, ast->writeformat);\ - ast_set_read_format(ast, ast->readformat); \ - ast_set_write_format(ast, ast->writeformat); \ - } \ - if (p->chan->readformat != ast->rawreadformat) \ - ast_set_read_format(p->chan, ast->rawreadformat); \ - if (p->chan->writeformat != ast->rawwriteformat) \ - ast_set_write_format(p->chan, ast->rawwriteformat); \ - } \ -} while(0) - -/* Cleanup moves all the relevant FD's from the 2nd to the first, but retains things - properly for a timingfd XXX This might need more work if agents were logged in as agents or other - totally impractical combinations XXX */ - -#define CLEANUP(ast, p) do { \ - int x; \ - if (p->chan) { \ - for (x=0;xfds[x] = p->chan->fds[x]; \ - } \ - ast->fds[AST_MAX_FDS - 3] = p->chan->fds[AST_MAX_FDS - 2]; \ - } \ -} while(0) - -static struct ast_channel *agent_request(const char *type, int format, void *data, int *cause); -static int agent_devicestate(void *data); -static int agent_digit(struct ast_channel *ast, char digit); -static int agent_call(struct ast_channel *ast, char *dest, int timeout); -static int agent_hangup(struct ast_channel *ast); -static int agent_answer(struct ast_channel *ast); -static struct ast_frame *agent_read(struct ast_channel *ast); -static int agent_write(struct ast_channel *ast, struct ast_frame *f); -static int agent_sendhtml(struct ast_channel *ast, int subclass, const char *data, int datalen); -static int agent_indicate(struct ast_channel *ast, int condition); -static int agent_fixup(struct ast_channel *oldchan, struct ast_channel *newchan); -static struct ast_channel *agent_bridgedchannel(struct ast_channel *chan, struct ast_channel *bridge); - -static const struct ast_channel_tech agent_tech = { - .type = channeltype, - .description = tdesc, - .capabilities = -1, - .requester = agent_request, - .devicestate = agent_devicestate, - .send_digit = agent_digit, - .call = agent_call, - .hangup = agent_hangup, - .answer = agent_answer, - .read = agent_read, - .write = agent_write, - .send_html = agent_sendhtml, - .exception = agent_read, - .indicate = agent_indicate, - .fixup = agent_fixup, - .bridged_channel = agent_bridgedchannel, -}; - -static void agent_unlink(struct agent_pvt *agent) -{ - struct agent_pvt *p, *prev; - prev = NULL; - p = agents; - while(p) { - if (p == agent) { - if (prev) - prev->next = agent->next; - else - agents = agent->next; - break; - } - prev = p; - p = p->next; - } -} - -static struct agent_pvt *add_agent(char *agent, int pending) -{ - char tmp[AST_MAX_BUF] = ""; - char *password=NULL, *name=NULL; - struct agent_pvt *p, *prev; - - strncpy(tmp, agent, sizeof(tmp) - 1); - if ((password = strchr(tmp, ','))) { - *password = '\0'; - password++; - while (*password < 33) password++; - } - if (password && (name = strchr(password, ','))) { - *name = '\0'; - name++; - while (*name < 33) name++; - } - prev=NULL; - p = agents; - while(p) { - if (!pending && !strcmp(p->agent, tmp)) - break; - prev = p; - p = p->next; - } - if (!p) { - p = malloc(sizeof(struct agent_pvt)); - if (p) { - memset(p, 0, sizeof(struct agent_pvt)); - strncpy(p->agent, tmp, sizeof(p->agent) -1); - ast_mutex_init(&p->lock); - ast_mutex_init(&p->app_lock); - p->owning_app = (pthread_t) -1; - p->app_sleep_cond = 1; - p->group = group; - p->pending = pending; - p->next = NULL; - if (prev) - prev->next = p; - else - agents = p; - - } - } - if (!p) - return NULL; - strncpy(p->password, password ? password : "", sizeof(p->password) - 1); - strncpy(p->name, name ? name : "", sizeof(p->name) - 1); - strncpy(p->moh, moh, sizeof(p->moh) - 1); - p->ackcall = ackcall; - p->autologoff = autologoff; - p->wrapuptime = wrapuptime; - if (pending) - p->dead = 1; - else - p->dead = 0; - return p; -} - -static int agent_cleanup(struct agent_pvt *p) -{ - struct ast_channel *chan = p->owner; - p->owner = NULL; - chan->tech_pvt = NULL; - p->app_sleep_cond = 1; - /* Release ownership of the agent to other threads (presumably running the login app). */ - ast_mutex_unlock(&p->app_lock); - if (chan) - ast_channel_free(chan); - if (p->dead) { - ast_mutex_destroy(&p->lock); - ast_mutex_destroy(&p->app_lock); - free(p); - } - return 0; -} - -static int check_availability(struct agent_pvt *newlyavailable, int needlock); - -static int agent_answer(struct ast_channel *ast) -{ - ast_log(LOG_WARNING, "Huh? Agent is being asked to answer?\n"); - return -1; -} - -static int __agent_start_monitoring(struct ast_channel *ast, struct agent_pvt *p, int needlock) -{ - char tmp[AST_MAX_BUF],tmp2[AST_MAX_BUF], *pointer; - char filename[AST_MAX_BUF]; - int res = -1; - if (!p) - return -1; - if (!ast->monitor) { - snprintf(filename, sizeof(filename), "agent-%s-%s",p->agent, ast->uniqueid); - /* substitute . for - */ - if ((pointer = strchr(filename, '.'))) - *pointer = '-'; - snprintf(tmp, sizeof(tmp), "%s%s",savecallsin ? savecallsin : "", filename); - ast_monitor_start(ast, recordformat, tmp, needlock); - ast_monitor_setjoinfiles(ast, 1); - snprintf(tmp2, sizeof(tmp2), "%s%s.%s", urlprefix ? urlprefix : "", filename, recordformatext); -#if 0 - ast_verbose("name is %s, link is %s\n",tmp, tmp2); -#endif - if (!ast->cdr) - ast->cdr = ast_cdr_alloc(); - ast_cdr_setuserfield(ast, tmp2); - res = 0; - } else - ast_log(LOG_ERROR, "Recording already started on that call.\n"); - return res; -} - -static int agent_start_monitoring(struct ast_channel *ast, int needlock) -{ - return __agent_start_monitoring(ast, ast->tech_pvt, needlock); -} - -static struct ast_frame *agent_read(struct ast_channel *ast) -{ - struct agent_pvt *p = ast->tech_pvt; - struct ast_frame *f = NULL; - static struct ast_frame null_frame = { AST_FRAME_NULL, }; - static struct ast_frame answer_frame = { AST_FRAME_CONTROL, AST_CONTROL_ANSWER }; - ast_mutex_lock(&p->lock); - CHECK_FORMATS(ast, p); - if (p->chan) { - ast_copy_flags(p->chan, ast, AST_FLAG_EXCEPTION); - if (ast->fdno == AST_MAX_FDS - 3) - p->chan->fdno = AST_MAX_FDS - 2; - else - p->chan->fdno = ast->fdno; - f = ast_read(p->chan); - } else - f = &null_frame; - if (!f) { - /* If there's a channel, hang it up (if it's on a callback) make it NULL */ - if (p->chan) { - p->chan->_bridge = NULL; - /* Note that we don't hangup if it's not a callback because Asterisk will do it - for us when the PBX instance that called login finishes */ - if (!ast_strlen_zero(p->loginchan)) { - if (p->chan) - ast_log(LOG_DEBUG, "Bridge on '%s' being cleared (2)\n", p->chan->name); - ast_hangup(p->chan); - if (p->wrapuptime && p->acknowledged) { - gettimeofday(&p->lastdisc, NULL); - p->lastdisc.tv_usec += (p->wrapuptime % 1000) * 1000; - if (p->lastdisc.tv_usec > 1000000) { - p->lastdisc.tv_usec -= 1000000; - p->lastdisc.tv_sec++; - } - p->lastdisc.tv_sec += (p->wrapuptime / 1000); - } - } - p->chan = NULL; - p->acknowledged = 0; - } - } - if (p->chan && f && (f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_ANSWER)) { -/* TC */ - ast_log(LOG_DEBUG, "Got answer on %s\n", p->chan->name); - if (p->ackcall) { - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "%s answered, waiting for '#' to acknowledge\n", p->chan->name); - /* Don't pass answer along */ - ast_frfree(f); - f = &null_frame; - } else { - p->acknowledged = 1; - ast_frfree(f); - f = &answer_frame; - } - } - if (f && (f->frametype == AST_FRAME_DTMF) && (f->subclass == '#')) { - if (!p->acknowledged) { - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "%s acknowledged\n", p->chan->name); - p->acknowledged = 1; - ast_frfree(f); - f = &answer_frame; - } - } - if (f && (f->frametype == AST_FRAME_DTMF) && (f->subclass == '*')) { - /* * terminates call */ - ast_frfree(f); - f = NULL; - } - if (f && (f->frametype == AST_FRAME_VOICE) && !p->acknowledged) { - /* Don't pass along agent audio until call is acknowledged */ - ast_frfree(f); - f = &null_frame; - } - CLEANUP(ast,p); - if (p->chan && !p->chan->_bridge) { - if (strcasecmp(p->chan->type, "Local")) { - p->chan->_bridge = ast; - if (p->chan) - ast_log(LOG_DEBUG, "Bridge on '%s' being set to '%s' (3)\n", p->chan->name, p->chan->_bridge->name); - } - } - ast_mutex_unlock(&p->lock); - if (recordagentcalls && f == &answer_frame) - agent_start_monitoring(ast,0); - return f; -} - -static int agent_sendhtml(struct ast_channel *ast, int subclass, const char *data, int datalen) -{ - struct agent_pvt *p = ast->tech_pvt; - int res = -1; - ast_mutex_lock(&p->lock); - if (p->chan) - res = ast_channel_sendhtml(p->chan, subclass, data, datalen); - ast_mutex_unlock(&p->lock); - return res; -} - -static int agent_write(struct ast_channel *ast, struct ast_frame *f) -{ - struct agent_pvt *p = ast->tech_pvt; - int res = -1; - CHECK_FORMATS(ast, p); - ast_mutex_lock(&p->lock); - if (p->chan) { - if ((f->frametype != AST_FRAME_VOICE) || - (f->subclass == p->chan->writeformat)) { - res = ast_write(p->chan, f); - } else { - ast_log(LOG_DEBUG, "Dropping one incompatible voice frame on '%s' to '%s'\n", ast->name, p->chan->name); - res = 0; - } - } else - res = 0; - CLEANUP(ast, p); - ast_mutex_unlock(&p->lock); - return res; -} - -static int agent_fixup(struct ast_channel *oldchan, struct ast_channel *newchan) -{ - struct agent_pvt *p = newchan->tech_pvt; - ast_mutex_lock(&p->lock); - if (p->owner != oldchan) { - ast_log(LOG_WARNING, "old channel wasn't %p but was %p\n", oldchan, p->owner); - ast_mutex_unlock(&p->lock); - return -1; - } - p->owner = newchan; - ast_mutex_unlock(&p->lock); - return 0; -} - -static int agent_indicate(struct ast_channel *ast, int condition) -{ - struct agent_pvt *p = ast->tech_pvt; - int res = -1; - ast_mutex_lock(&p->lock); - if (p->chan) - res = ast_indicate(p->chan, condition); - else - res = 0; - ast_mutex_unlock(&p->lock); - return res; -} - -static int agent_digit(struct ast_channel *ast, char digit) -{ - struct agent_pvt *p = ast->tech_pvt; - int res = -1; - ast_mutex_lock(&p->lock); - if (p->chan) - res = p->chan->tech->send_digit(p->chan, digit); - else - res = 0; - ast_mutex_unlock(&p->lock); - return res; -} - -static int agent_call(struct ast_channel *ast, char *dest, int timeout) -{ - struct agent_pvt *p = ast->tech_pvt; - int res = -1; - int newstate=0; - ast_mutex_lock(&p->lock); - p->acknowledged = 0; - if (!p->chan) { - if (p->pending) { - ast_log(LOG_DEBUG, "Pretending to dial on pending agent\n"); - newstate = AST_STATE_DIALING; - res = 0; - } else { - ast_log(LOG_NOTICE, "Whoa, they hung up between alloc and call... what are the odds of that?\n"); - res = -1; - } - ast_mutex_unlock(&p->lock); - if (newstate) - ast_setstate(ast, newstate); - return res; - } else if (!ast_strlen_zero(p->loginchan)) { - time(&p->start); - /* Call on this agent */ - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "outgoing agentcall, to agent '%s', on '%s'\n", p->agent, p->chan->name); - if (p->chan->cid.cid_num) - free(p->chan->cid.cid_num); - if (ast->cid.cid_num) - p->chan->cid.cid_num = strdup(ast->cid.cid_num); - else - p->chan->cid.cid_num = NULL; - if (p->chan->cid.cid_name) - free(p->chan->cid.cid_name); - if (ast->cid.cid_name) - p->chan->cid.cid_name = strdup(ast->cid.cid_name); - else - p->chan->cid.cid_name = NULL; - ast_channel_inherit_variables(ast, p->chan); - res = ast_call(p->chan, p->loginchan, 0); - CLEANUP(ast,p); - ast_mutex_unlock(&p->lock); - return res; - } - ast_verbose( VERBOSE_PREFIX_3 "agent_call, call to agent '%s' call on '%s'\n", p->agent, p->chan->name); - ast_log( LOG_DEBUG, "Playing beep, lang '%s'\n", p->chan->language); - res = ast_streamfile(p->chan, beep, p->chan->language); - ast_log( LOG_DEBUG, "Played beep, result '%d'\n", res); - if (!res) { - res = ast_waitstream(p->chan, ""); - ast_log( LOG_DEBUG, "Waited for stream, result '%d'\n", res); - } - if (!res) { - res = ast_set_read_format(p->chan, ast_best_codec(p->chan->nativeformats)); - ast_log( LOG_DEBUG, "Set read format, result '%d'\n", res); - if (res) - ast_log(LOG_WARNING, "Unable to set read format to %s\n", ast_getformatname(ast_best_codec(p->chan->nativeformats))); - } else { - /* Agent hung-up */ - p->chan = NULL; - } - - if (!res) { - ast_set_write_format(p->chan, ast_best_codec(p->chan->nativeformats)); - ast_log( LOG_DEBUG, "Set write format, result '%d'\n", res); - if (res) - ast_log(LOG_WARNING, "Unable to set write format to %s\n", ast_getformatname(ast_best_codec(p->chan->nativeformats))); - } - if( !res ) - { - /* Call is immediately up, or might need ack */ - if (p->ackcall > 1) - newstate = AST_STATE_RINGING; - else { - newstate = AST_STATE_UP; - if (recordagentcalls) - agent_start_monitoring(ast,0); - p->acknowledged = 1; - } - res = 0; - } - CLEANUP(ast,p); - ast_mutex_unlock(&p->lock); - if (newstate) - ast_setstate(ast, newstate); - return res; -} - -static int agent_hangup(struct ast_channel *ast) -{ - struct agent_pvt *p = ast->tech_pvt; - int howlong = 0; - ast_mutex_lock(&p->lock); - p->owner = NULL; - ast->tech_pvt = NULL; - p->app_sleep_cond = 1; - p->acknowledged = 0; - - /* if they really are hung up then set start to 0 so the test - * later if we're called on an already downed channel - * doesn't cause an agent to be logged out like when - * agent_request() is followed immediately by agent_hangup() - * as in apps/app_chanisavail.c:chanavail_exec() - */ - - ast_mutex_lock(&usecnt_lock); - usecnt--; - ast_mutex_unlock(&usecnt_lock); - - ast_log(LOG_DEBUG, "Hangup called for state %s\n", ast_state2str(ast->_state)); - if (p->start && (ast->_state != AST_STATE_UP)) { - howlong = time(NULL) - p->start; - p->start = 0; - } else if (ast->_state == AST_STATE_RESERVED) { - howlong = 0; - } else - p->start = 0; - if (p->chan) { - p->chan->_bridge = NULL; - /* If they're dead, go ahead and hang up on the agent now */ - if (!ast_strlen_zero(p->loginchan)) { - /* Store last disconnect time */ - if (p->wrapuptime && p->acknowledged) { - gettimeofday(&p->lastdisc, NULL); - p->lastdisc.tv_usec += (p->wrapuptime % 1000) * 1000; - if (p->lastdisc.tv_usec >= 1000000) { - p->lastdisc.tv_usec -= 1000000; - p->lastdisc.tv_sec++; - } - p->lastdisc.tv_sec += (p->wrapuptime / 1000); - } else - memset(&p->lastdisc, 0, sizeof(p->lastdisc)); - if (p->chan) { - /* Recognize the hangup and pass it along immediately */ - ast_hangup(p->chan); - p->chan = NULL; - } - ast_log(LOG_DEBUG, "Hungup, howlong is %d, autologoff is %d\n", howlong, p->autologoff); - if (howlong && p->autologoff && (howlong > p->autologoff)) { - char agent[AST_MAX_AGENT] = ""; - long logintime = time(NULL) - p->loginstart; - p->loginstart = 0; - ast_log(LOG_NOTICE, "Agent '%s' didn't answer/confirm within %d seconds (waited %d)\n", p->name, p->autologoff, howlong); - manager_event(EVENT_FLAG_AGENT, "Agentcallbacklogoff", - "Agent: %s\r\n" - "Loginchan: %s\r\n" - "Logintime: %ld\r\n" - "Reason: Autologoff\r\n" - "Uniqueid: %s\r\n", - p->agent, p->loginchan, logintime, ast->uniqueid); - snprintf(agent, sizeof(agent), "Agent/%s", p->agent); - ast_queue_log("NONE", ast->uniqueid, agent, "AGENTCALLBACKLOGOFF", "%s|%ld|%s", p->loginchan, logintime, "Autologoff"); - p->loginchan[0] = '\0'; - ast_device_state_changed("Agent/%s", p->agent); - } - } else if (p->dead) { - ast_mutex_lock(&p->chan->lock); - ast_softhangup(p->chan, AST_SOFTHANGUP_EXPLICIT); - ast_mutex_unlock(&p->chan->lock); - } else { - ast_device_state_changed("Agent/%s", p->agent); - ast_mutex_lock(&p->chan->lock); - ast_moh_start(p->chan, p->moh); - ast_mutex_unlock(&p->chan->lock); - } - } -#if 0 - ast_mutex_unlock(&p->lock); - /* Release ownership of the agent to other threads (presumably running the login app). */ - ast_mutex_unlock(&p->app_lock); - } else if (p->dead) { - /* Go ahead and lose it */ - ast_mutex_unlock(&p->lock); - /* Release ownership of the agent to other threads (presumably running the login app). */ - ast_mutex_unlock(&p->app_lock); - } else { - ast_mutex_unlock(&p->lock); - /* Release ownership of the agent to other threads (presumably running the login app). */ - ast_mutex_unlock(&p->app_lock); - } -#endif - ast_mutex_unlock(&p->lock); - - if (p->pending) { - ast_mutex_lock(&agentlock); - agent_unlink(p); - ast_mutex_unlock(&agentlock); - } - if (p->abouttograb) { - /* Let the "about to grab" thread know this isn't valid anymore, and let it - kill it later */ - p->abouttograb = 0; - } else if (p->dead) { - ast_mutex_destroy(&p->lock); - ast_mutex_destroy(&p->app_lock); - free(p); - } else { - if (p->chan) { - /* Not dead -- check availability now */ - ast_mutex_lock(&p->lock); - /* Store last disconnect time */ - gettimeofday(&p->lastdisc, NULL); - ast_mutex_unlock(&p->lock); - } - /* Release ownership of the agent to other threads (presumably running the login app). */ - ast_mutex_unlock(&p->app_lock); - } - return 0; -} - -static int agent_cont_sleep( void *data ) -{ - struct agent_pvt *p; - struct timeval tv; - int res; - - p = (struct agent_pvt *)data; - - ast_mutex_lock(&p->lock); - res = p->app_sleep_cond; - if (p->lastdisc.tv_sec) { - gettimeofday(&tv, NULL); - if ((tv.tv_sec - p->lastdisc.tv_sec) * 1000 + - (tv.tv_usec - p->lastdisc.tv_usec) / 1000 > p->wrapuptime) - res = 1; - } - ast_mutex_unlock(&p->lock); -#if 0 - if( !res ) - ast_log( LOG_DEBUG, "agent_cont_sleep() returning %d\n", res ); -#endif - return res; -} - -static int agent_ack_sleep( void *data ) -{ - struct agent_pvt *p; - int res=0; - int to = 1000; - struct ast_frame *f; - - /* Wait a second and look for something */ - - p = (struct agent_pvt *)data; - if (p->chan) { - for(;;) { - to = ast_waitfor(p->chan, to); - if (to < 0) { - res = -1; - break; - } - if (!to) { - res = 0; - break; - } - f = ast_read(p->chan); - if (!f) { - res = -1; - break; - } - if (f->frametype == AST_FRAME_DTMF) - res = f->subclass; - else - res = 0; - ast_frfree(f); - ast_mutex_lock(&p->lock); - if (!p->app_sleep_cond) { - ast_mutex_unlock(&p->lock); - res = 0; - break; - } else if (res == '#') { - ast_mutex_unlock(&p->lock); - res = 1; - break; - } - ast_mutex_unlock(&p->lock); - res = 0; - } - } else - res = -1; - return res; -} - -static struct ast_channel *agent_bridgedchannel(struct ast_channel *chan, struct ast_channel *bridge) -{ - struct agent_pvt *p; - struct ast_channel *ret=NULL; - - - p = bridge->tech_pvt; - if (chan == p->chan) - ret = bridge->_bridge; - else if (chan == bridge->_bridge) - ret = p->chan; - if (option_debug) - ast_log(LOG_DEBUG, "Asked for bridged channel on '%s'/'%s', returning '%s'\n", chan->name, bridge->name, ret ? ret->name : ""); - return ret; -} - -/*--- agent_new: Create new agent channel ---*/ -static struct ast_channel *agent_new(struct agent_pvt *p, int state) -{ - struct ast_channel *tmp; - struct ast_frame null_frame = { AST_FRAME_NULL }; -#if 0 - if (!p->chan) { - ast_log(LOG_WARNING, "No channel? :(\n"); - return NULL; - } -#endif - tmp = ast_channel_alloc(0); - if (tmp) { - tmp->tech = &agent_tech; - if (p->chan) { - tmp->nativeformats = p->chan->nativeformats; - tmp->writeformat = p->chan->writeformat; - tmp->rawwriteformat = p->chan->writeformat; - tmp->readformat = p->chan->readformat; - tmp->rawreadformat = p->chan->readformat; - strncpy(tmp->language, p->chan->language, sizeof(tmp->language)-1); - strncpy(tmp->context, p->chan->context, sizeof(tmp->context)-1); - strncpy(tmp->exten, p->chan->exten, sizeof(tmp->exten)-1); - } else { - tmp->nativeformats = AST_FORMAT_SLINEAR; - tmp->writeformat = AST_FORMAT_SLINEAR; - tmp->rawwriteformat = AST_FORMAT_SLINEAR; - tmp->readformat = AST_FORMAT_SLINEAR; - tmp->rawreadformat = AST_FORMAT_SLINEAR; - } - if (p->pending) - snprintf(tmp->name, sizeof(tmp->name), "Agent/P%s-%d", p->agent, rand() & 0xffff); - else - snprintf(tmp->name, sizeof(tmp->name), "Agent/%s", p->agent); - tmp->type = channeltype; - /* Safe, agentlock already held */ - ast_setstate(tmp, state); - tmp->tech_pvt = p; - p->owner = tmp; - ast_mutex_lock(&usecnt_lock); - usecnt++; - ast_mutex_unlock(&usecnt_lock); - ast_update_use_count(); - tmp->priority = 1; - /* Wake up and wait for other applications (by definition the login app) - * to release this channel). Takes ownership of the agent channel - * to this thread only. - * For signalling the other thread, ast_queue_frame is used until we - * can safely use signals for this purpose. The pselect() needs to be - * implemented in the kernel for this. - */ - p->app_sleep_cond = 0; - if( ast_mutex_trylock(&p->app_lock) ) - { - if (p->chan) { - ast_queue_frame(p->chan, &null_frame); - ast_mutex_unlock(&p->lock); /* For other thread to read the condition. */ - ast_mutex_lock(&p->app_lock); - ast_mutex_lock(&p->lock); - } - if( !p->chan ) - { - ast_log(LOG_WARNING, "Agent disconnected while we were connecting the call\n"); - p->owner = NULL; - tmp->tech_pvt = NULL; - p->app_sleep_cond = 1; - ast_channel_free( tmp ); - ast_mutex_unlock(&p->lock); /* For other thread to read the condition. */ - ast_mutex_unlock(&p->app_lock); - return NULL; - } - } - p->owning_app = pthread_self(); - /* After the above step, there should not be any blockers. */ - if (p->chan) { - if (ast_test_flag(p->chan, AST_FLAG_BLOCKING)) { - ast_log( LOG_ERROR, "A blocker exists after agent channel ownership acquired\n" ); - CRASH; - } - ast_moh_stop(p->chan); - } - } else - ast_log(LOG_WARNING, "Unable to allocate agent channel structure\n"); - return tmp; -} - - -/*--- read_agent_config: Read configuration data (agents.conf) ---*/ -static int read_agent_config(void) -{ - struct ast_config *cfg; - struct ast_variable *v; - struct agent_pvt *p, *pl, *pn; - char *general_val; - - group = 0; - autologoff = 0; - wrapuptime = 0; - ackcall = 0; - cfg = ast_config_load(config); - if (!cfg) { - ast_log(LOG_NOTICE, "No agent configuration found -- agent support disabled\n"); - return 0; - } - ast_mutex_lock(&agentlock); - p = agents; - while(p) { - p->dead = 1; - p = p->next; - } - strncpy(moh, "default", sizeof(moh) - 1); - /* set the default recording values */ - recordagentcalls = 0; - createlink = 0; - strncpy(recordformat, "wav", sizeof(recordformat) - 1); - strncpy(recordformatext, "wav", sizeof(recordformatext) - 1); - urlprefix[0] = '\0'; - savecallsin[0] = '\0'; - - /* Read in [general] section for persistance */ - if ((general_val = ast_variable_retrieve(cfg, "general", "persistentagents"))) - persistent_agents = ast_true(general_val); - - /* Read in the [agents] section */ - v = ast_variable_browse(cfg, "agents"); - while(v) { - /* Create the interface list */ - if (!strcasecmp(v->name, "agent")) { - add_agent(v->value, 0); - } else if (!strcasecmp(v->name, "group")) { - group = ast_get_group(v->value); - } else if (!strcasecmp(v->name, "autologoff")) { - autologoff = atoi(v->value); - if (autologoff < 0) - autologoff = 0; - } else if (!strcasecmp(v->name, "ackcall")) { - if (!strcasecmp(v->value, "always")) - ackcall = 2; - else if (ast_true(v->value)) - ackcall = 1; - else - ackcall = 0; - } else if (!strcasecmp(v->name, "wrapuptime")) { - wrapuptime = atoi(v->value); - if (wrapuptime < 0) - wrapuptime = 0; - } else if (!strcasecmp(v->name, "maxlogintries") && !ast_strlen_zero(v->value)) { - maxlogintries = atoi(v->value); - if (maxlogintries < 0) - maxlogintries = 0; - } else if (!strcasecmp(v->name, "goodbye") && !ast_strlen_zero(v->value)) { - strcpy(agentgoodbye,v->value); - } else if (!strcasecmp(v->name, "musiconhold")) { - strncpy(moh, v->value, sizeof(moh) - 1); - } else if (!strcasecmp(v->name, "updatecdr")) { - if (ast_true(v->value)) - updatecdr = 1; - else - updatecdr = 0; - } else if (!strcasecmp(v->name, "recordagentcalls")) { - recordagentcalls = ast_true(v->value); - } else if (!strcasecmp(v->name, "createlink")) { - createlink = ast_true(v->value); - } else if (!strcasecmp(v->name, "recordformat")) { - strncpy(recordformat, v->value, sizeof(recordformat) - 1); - if (!strcasecmp(v->value, "wav49")) - strncpy(recordformatext, "WAV", sizeof(recordformatext) - 1); - else - strncpy(recordformatext, v->value, sizeof(recordformatext) - 1); - } else if (!strcasecmp(v->name, "urlprefix")) { - strncpy(urlprefix, v->value, sizeof(urlprefix) - 2); - if (urlprefix[strlen(urlprefix) - 1] != '/') - strncat(urlprefix, "/", sizeof(urlprefix) - strlen(urlprefix) - 1); - } else if (!strcasecmp(v->name, "savecallsin")) { - if (v->value[0] == '/') - strncpy(savecallsin, v->value, sizeof(savecallsin) - 2); - else - snprintf(savecallsin, sizeof(savecallsin) - 2, "/%s", v->value); - if (savecallsin[strlen(savecallsin) - 1] != '/') - strncat(savecallsin, "/", sizeof(savecallsin) - strlen(savecallsin) - 1); - } else if (!strcasecmp(v->name, "custom_beep")) { - strncpy(beep, v->value, sizeof(beep) - 1); - } - v = v->next; - } - p = agents; - pl = NULL; - while(p) { - pn = p->next; - if (p->dead) { - /* Unlink */ - if (pl) - pl->next = p->next; - else - agents = p->next; - /* Destroy if appropriate */ - if (!p->owner) { - if (!p->chan) { - ast_mutex_destroy(&p->lock); - ast_mutex_destroy(&p->app_lock); - free(p); - } else { - /* Cause them to hang up */ - ast_softhangup(p->chan, AST_SOFTHANGUP_EXPLICIT); - } - } - } else - pl = p; - p = pn; - } - ast_mutex_unlock(&agentlock); - ast_config_destroy(cfg); - return 0; -} - -static int check_availability(struct agent_pvt *newlyavailable, int needlock) -{ - struct ast_channel *chan=NULL, *parent=NULL; - struct agent_pvt *p; - int res; - - if (option_debug) - ast_log(LOG_DEBUG, "Checking availability of '%s'\n", newlyavailable->agent); - if (needlock) - ast_mutex_lock(&agentlock); - p = agents; - while(p) { - if (p == newlyavailable) { - p = p->next; - continue; - } - ast_mutex_lock(&p->lock); - if (!p->abouttograb && p->pending && ((p->group && (newlyavailable->group & p->group)) || !strcmp(p->agent, newlyavailable->agent))) { - if (option_debug) - ast_log(LOG_DEBUG, "Call '%s' looks like a winner for agent '%s'\n", p->owner->name, newlyavailable->agent); - /* We found a pending call, time to merge */ - chan = agent_new(newlyavailable, AST_STATE_DOWN); - parent = p->owner; - p->abouttograb = 1; - ast_mutex_unlock(&p->lock); - break; - } - ast_mutex_unlock(&p->lock); - p = p->next; - } - if (needlock) - ast_mutex_unlock(&agentlock); - if (parent && chan) { - if (newlyavailable->ackcall > 1) { - /* Don't do beep here */ - res = 0; - } else { - if (option_debug > 2) - ast_log( LOG_DEBUG, "Playing beep, lang '%s'\n", newlyavailable->chan->language); - res = ast_streamfile(newlyavailable->chan, beep, newlyavailable->chan->language); - if (option_debug > 2) - ast_log( LOG_DEBUG, "Played beep, result '%d'\n", res); - if (!res) { - res = ast_waitstream(newlyavailable->chan, ""); - ast_log( LOG_DEBUG, "Waited for stream, result '%d'\n", res); - } - } - if (!res) { - /* Note -- parent may have disappeared */ - if (p->abouttograb) { - newlyavailable->acknowledged = 1; - /* Safe -- agent lock already held */ - ast_setstate(parent, AST_STATE_UP); - ast_setstate(chan, AST_STATE_UP); - strncpy(parent->context, chan->context, sizeof(parent->context) - 1); - /* Go ahead and mark the channel as a zombie so that masquerade will - destroy it for us, and we need not call ast_hangup */ - ast_mutex_lock(&parent->lock); - ast_set_flag(chan, AST_FLAG_ZOMBIE); - ast_channel_masquerade(parent, chan); - ast_mutex_unlock(&parent->lock); - p->abouttograb = 0; - } else { - if (option_debug) - ast_log(LOG_DEBUG, "Sneaky, parent disappeared in the mean time...\n"); - agent_cleanup(newlyavailable); - } - } else { - if (option_debug) - ast_log(LOG_DEBUG, "Ugh... Agent hung up at exactly the wrong time\n"); - agent_cleanup(newlyavailable); - } - } - return 0; -} - -static int check_beep(struct agent_pvt *newlyavailable, int needlock) -{ - struct agent_pvt *p; - int res=0; - - ast_log(LOG_DEBUG, "Checking beep availability of '%s'\n", newlyavailable->agent); - if (needlock) - ast_mutex_lock(&agentlock); - p = agents; - while(p) { - if (p == newlyavailable) { - p = p->next; - continue; - } - ast_mutex_lock(&p->lock); - if (!p->abouttograb && p->pending && ((p->group && (newlyavailable->group & p->group)) || !strcmp(p->agent, newlyavailable->agent))) { - if (option_debug) - ast_log(LOG_DEBUG, "Call '%s' looks like a would-be winner for agent '%s'\n", p->owner->name, newlyavailable->agent); - ast_mutex_unlock(&p->lock); - break; - } - ast_mutex_unlock(&p->lock); - p = p->next; - } - if (needlock) - ast_mutex_unlock(&agentlock); - if (p) { - ast_mutex_unlock(&newlyavailable->lock); - if (option_debug > 2) - ast_log( LOG_DEBUG, "Playing beep, lang '%s'\n", newlyavailable->chan->language); - res = ast_streamfile(newlyavailable->chan, beep, newlyavailable->chan->language); - if (option_debug > 2) - ast_log( LOG_DEBUG, "Played beep, result '%d'\n", res); - if (!res) { - res = ast_waitstream(newlyavailable->chan, ""); - if (option_debug) - ast_log( LOG_DEBUG, "Waited for stream, result '%d'\n", res); - } - ast_mutex_lock(&newlyavailable->lock); - } - return res; -} - -/*--- agent_request: Part of the Asterisk PBX interface ---*/ -static struct ast_channel *agent_request(const char *type, int format, void *data, int *cause) -{ - struct agent_pvt *p; - struct ast_channel *chan = NULL; - char *s; - ast_group_t groupmatch; - int groupoff; - int waitforagent=0; - int hasagent = 0; - struct timeval tv; - - s = data; - if ((s[0] == '@') && (sscanf(s + 1, "%d", &groupoff) == 1)) { - groupmatch = (1 << groupoff); - } else if ((s[0] == ':') && (sscanf(s + 1, "%d", &groupoff) == 1)) { - groupmatch = (1 << groupoff); - waitforagent = 1; - } else { - groupmatch = 0; - } - - /* Check actual logged in agents first */ - ast_mutex_lock(&agentlock); - p = agents; - while(p) { - ast_mutex_lock(&p->lock); - if (!p->pending && ((groupmatch && (p->group & groupmatch)) || !strcmp(data, p->agent)) && - ast_strlen_zero(p->loginchan)) { - if (p->chan) - hasagent++; - if (!p->lastdisc.tv_sec) { - /* Agent must be registered, but not have any active call, and not be in a waiting state */ - if (!p->owner && p->chan) { - /* Fixed agent */ - chan = agent_new(p, AST_STATE_DOWN); - } - if (chan) { - ast_mutex_unlock(&p->lock); - break; - } - } - } - ast_mutex_unlock(&p->lock); - p = p->next; - } - if (!p) { - p = agents; - while(p) { - ast_mutex_lock(&p->lock); - if (!p->pending && ((groupmatch && (p->group & groupmatch)) || !strcmp(data, p->agent))) { - if (p->chan || !ast_strlen_zero(p->loginchan)) - hasagent++; - gettimeofday(&tv, NULL); -#if 0 - ast_log(LOG_NOTICE, "Time now: %ld, Time of lastdisc: %ld\n", tv.tv_sec, p->lastdisc.tv_sec); -#endif - if (!p->lastdisc.tv_sec || (tv.tv_sec > p->lastdisc.tv_sec)) { - memset(&p->lastdisc, 0, sizeof(p->lastdisc)); - /* Agent must be registered, but not have any active call, and not be in a waiting state */ - if (!p->owner && p->chan) { - /* Could still get a fixed agent */ - chan = agent_new(p, AST_STATE_DOWN); - } else if (!p->owner && !ast_strlen_zero(p->loginchan)) { - /* Adjustable agent */ - p->chan = ast_request("Local", format, p->loginchan, cause); - if (p->chan) - chan = agent_new(p, AST_STATE_DOWN); - } - if (chan) { - ast_mutex_unlock(&p->lock); - break; - } - } - } - ast_mutex_unlock(&p->lock); - p = p->next; - } - } - - if (!chan && waitforagent) { - /* No agent available -- but we're requesting to wait for one. - Allocate a place holder */ - if (hasagent) { - if (option_debug) - ast_log(LOG_DEBUG, "Creating place holder for '%s'\n", s); - p = add_agent(data, 1); - p->group = groupmatch; - chan = agent_new(p, AST_STATE_DOWN); - if (!chan) { - ast_log(LOG_WARNING, "Weird... Fix this to drop the unused pending agent\n"); - } - } else - ast_log(LOG_DEBUG, "Not creating place holder for '%s' since nobody logged in\n", s); - } - if (hasagent) - *cause = AST_CAUSE_BUSY; - else - *cause = AST_CAUSE_UNREGISTERED; - ast_mutex_unlock(&agentlock); - return chan; -} - -static int powerof(unsigned int v) -{ - int x; - for (x=0;x<32;x++) { - if (v & (1 << x)) return x; - } - return 0; -} - -/*--- action_agents: Manager routine for listing channels */ -static int action_agents(struct mansession *s, struct message *m) -{ - struct agent_pvt *p; - char *username = NULL; - char *loginChan = NULL; - char *talkingtoChan = NULL; - char *status = NULL; - - ast_mutex_lock(&agentlock); - p = agents; - while(p) { - ast_mutex_lock(&p->lock); - - /* Status Values: - AGENT_LOGGEDOFF - Agent isn't logged in - AGENT_IDLE - Agent is logged in, and waiting for call - AGENT_ONCALL - Agent is logged in, and on a call - AGENT_UNKNOWN - Don't know anything about agent. Shouldn't ever get this. */ - - if(!ast_strlen_zero(p->name)) { - username = p->name; - } else { - username = "None"; - } - - /* Set a default status. It 'should' get changed. */ - status = "AGENT_UNKNOWN"; - - if(p->chan) { - loginChan = p->loginchan; - if(p->owner && p->owner->_bridge) { - talkingtoChan = p->chan->cid.cid_num; - status = "AGENT_ONCALL"; - } else { - talkingtoChan = "n/a"; - status = "AGENT_IDLE"; - } - } else if(!ast_strlen_zero(p->loginchan)) { - loginChan = p->loginchan; - talkingtoChan = "n/a"; - status = "AGENT_IDLE"; - if(p->acknowledged) { - sprintf(loginChan, " %s (Confirmed)", loginChan); - } - } else { - loginChan = "n/a"; - talkingtoChan = "n/a"; - status = "AGENT_LOGGEDOFF"; - } - - ast_cli(s->fd, "Event: Agents\r\n" - "Agent: %s\r\n" - "Name: %s\r\n" - "Status: %s\r\n" - "LoggedInChan: %s\r\n" - "LoggedInTime: %ld\r\n" - "TalkingTo: %s\r\n" - "\r\n", - p->agent,p->name,status,loginChan,p->loginstart,talkingtoChan); - ast_mutex_unlock(&p->lock); - p = p->next; - } - ast_mutex_unlock(&agentlock); - return 0; -} - -static int agent_logoff_cmd(int fd, int argc, char **argv) -{ - struct agent_pvt *p; - char *agent = argv[2] + 6; - long logintime; - - if (argc < 3 || argc > 4) - return RESULT_SHOWUSAGE; - if (argc == 4 && strcasecmp(argv[3], "soft")) - return RESULT_SHOWUSAGE; - - for (p=agents; p; p=p->next) { - if (!strcasecmp(p->agent, agent)) { - if (argc == 3) { - if (p->owner) { - ast_softhangup(p->owner, AST_SOFTHANGUP_EXPLICIT); - } - if (p->chan) { - ast_softhangup(p->chan, AST_SOFTHANGUP_EXPLICIT); - } - } - logintime = time(NULL) - p->loginstart; - p->loginstart = 0; - - manager_event(EVENT_FLAG_AGENT, "Agentcallbacklogoff", - "Agent: %s\r\n" - "Loginchan: %s\r\n" - "Logintime: %ld\r\n", - p->agent, p->loginchan, logintime); - ast_queue_log("NONE", "NONE", agent, "AGENTCALLBACKLOGOFF", "%s|%ld|%s", p->loginchan, logintime, "CommandLogoff"); - p->loginchan[0] = '\0'; - ast_cli(fd, "Logging out %s\n", agent); - ast_device_state_changed("Agent/%s", p->agent); - if (persistent_agents) - dump_agents(); - break; - } - } - return RESULT_SUCCESS; -} - -static char *complete_agent_logoff_cmd(char *line, char *word, int pos, int state) -{ - struct agent_pvt *p; - char name[AST_MAX_AGENT]; - int which = 0; - - if (pos == 2) { - for (p=agents; p; p=p->next) { - snprintf(name, sizeof(name), "Agent/%s", p->agent); - if (!strncasecmp(word, name, strlen(word))) { - if (++which > state) { - return strdup(name); - } - } - } - } else if (pos == 3 && state == 0) { - return strdup("soft"); - } - return NULL; -} - -/*--- agents_show: Show agents in cli ---*/ -static int agents_show(int fd, int argc, char **argv) -{ - struct agent_pvt *p; - char username[AST_MAX_BUF]; - char location[AST_MAX_BUF] = ""; - char talkingto[AST_MAX_BUF] = ""; - char moh[AST_MAX_BUF]; - - if (argc != 2) - return RESULT_SHOWUSAGE; - ast_mutex_lock(&agentlock); - p = agents; - while(p) { - ast_mutex_lock(&p->lock); - if (p->pending) { - if (p->group) - ast_cli(fd, "-- Pending call to group %d\n", powerof(p->group)); - else - ast_cli(fd, "-- Pending call to agent %s\n", p->agent); - } else { - if (!ast_strlen_zero(p->name)) - snprintf(username, sizeof(username), "(%s) ", p->name); - else - username[0] = '\0'; - if (p->chan) { - snprintf(location, sizeof(location), "logged in on %s", p->chan->name); - if (p->owner && ast_bridged_channel(p->owner)) { - snprintf(talkingto, sizeof(talkingto), " talking to %s", ast_bridged_channel(p->owner)->name); - } else { - strncpy(talkingto, " is idle", sizeof(talkingto) - 1); - } - } else if (!ast_strlen_zero(p->loginchan)) { - snprintf(location, sizeof(location) - 20, "available at '%s'", p->loginchan); - talkingto[0] = '\0'; - if (p->acknowledged) - strncat(location, " (Confirmed)", sizeof(location) - strlen(location) - 1); - } else { - strncpy(location, "not logged in", sizeof(location) - 1); - talkingto[0] = '\0'; - } - if (!ast_strlen_zero(p->moh)) - snprintf(moh, sizeof(moh), " (musiconhold is '%s')", p->moh); - ast_cli(fd, "%-12.12s %s%s%s%s\n", p->agent, - username, location, talkingto, moh); - } - ast_mutex_unlock(&p->lock); - p = p->next; - } - ast_mutex_unlock(&agentlock); - return RESULT_SUCCESS; -} - -static char show_agents_usage[] = -"Usage: show agents\n" -" Provides summary information on agents.\n"; - -static char agent_logoff_usage[] = -"Usage: agent logoff [soft]\n" -" Sets an agent as no longer logged in.\n" -" If 'soft' is specified, do not hangup existing calls.\n"; - -static struct ast_cli_entry cli_show_agents = { - { "show", "agents", NULL }, agents_show, - "Show status of agents", show_agents_usage, NULL }; - -static struct ast_cli_entry cli_agent_logoff = { - { "agent", "logoff", NULL }, agent_logoff_cmd, - "Sets an agent offline", agent_logoff_usage, complete_agent_logoff_cmd }; - -STANDARD_LOCAL_USER; -LOCAL_USER_DECL; - -/*--- __login_exec: Log in agent application ---*/ -static int __login_exec(struct ast_channel *chan, void *data, int callbackmode) -{ - int res=0; - int tries = 0; - int max_login_tries = maxlogintries; - struct agent_pvt *p; - struct localuser *u; - struct timeval tv; - int login_state = 0; - char user[AST_MAX_AGENT] = ""; - char pass[AST_MAX_AGENT]; - char agent[AST_MAX_AGENT] = ""; - char xpass[AST_MAX_AGENT] = ""; - char *errmsg; - char info[512]; - char *opt_user = NULL; - char *options = NULL; - char option; - char badoption[2]; - char *tmpoptions = NULL; - char *context = NULL; - char *exten = NULL; - int play_announcement = 1; - char agent_goodbye[AST_MAX_FILENAME_LEN]; - int update_cdr = updatecdr; - char *filename = "agent-loginok"; - - strcpy(agent_goodbye, agentgoodbye); - LOCAL_USER_ADD(u); - - /* Parse the arguments XXX Check for failure XXX */ - strncpy(info, (char *)data, strlen((char *)data) + AST_MAX_EXTENSION-1); - opt_user = info; - /* Set Channel Specific Login Overrides */ - if (pbx_builtin_getvar_helper(chan, "AGENTLMAXLOGINTRIES") && strlen(pbx_builtin_getvar_helper(chan, "AGENTLMAXLOGINTRIES"))) { - max_login_tries = atoi(pbx_builtin_getvar_helper(chan, "AGENTMAXLOGINTRIES")); - if (max_login_tries < 0) - max_login_tries = 0; - tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTMAXLOGINTRIES"); - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Saw variable AGENTMAXLOGINTRIES=%s, setting max_login_tries to: %d on Channel '%s'.\n",tmpoptions,max_login_tries,chan->name); - } - if (pbx_builtin_getvar_helper(chan, "AGENTUPDATECDR") && !ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENTUPDATECDR"))) { - if (ast_true(pbx_builtin_getvar_helper(chan, "AGENTUPDATECDR"))) - update_cdr = 1; - else - update_cdr = 0; - tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTUPDATECDR"); - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Saw variable AGENTUPDATECDR=%s, setting update_cdr to: %d on Channel '%s'.\n",tmpoptions,update_cdr,chan->name); - } - if (pbx_builtin_getvar_helper(chan, "AGENTGOODBYE") && !ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENTGOODBYE"))) { - strcpy(agent_goodbye, pbx_builtin_getvar_helper(chan, "AGENTGOODBYE")); - tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTGOODBYE"); - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Saw variable AGENTGOODBYE=%s, setting agent_goodbye to: %s on Channel '%s'.\n",tmpoptions,agent_goodbye,chan->name); - } - /* End Channel Specific Login Overrides */ - /* Read command line options */ - if( opt_user ) { - options = strchr(opt_user, '|'); - if (options) { - *options = '\0'; - options++; - if (callbackmode) { - context = strchr(options, '@'); - if (context) { - *context = '\0'; - context++; - } - exten = options; - while(*exten && ((*exten < '0') || (*exten > '9'))) exten++; - if (!*exten) - exten = NULL; - } - } - if (options) { - while (*options) { - option = (char)options[0]; - if ((option >= 0) && (option <= '9')) - { - options++; - continue; - } - if (option=='s') - play_announcement = 0; - else { - badoption[0] = option; - badoption[1] = '\0'; - tmpoptions=badoption; - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Warning: option %s is unknown.\n",tmpoptions); - } - options++; - } - } - } - /* End command line options */ - - if (chan->_state != AST_STATE_UP) - res = ast_answer(chan); - if (!res) { - if( opt_user && !ast_strlen_zero(opt_user)) - strncpy( user, opt_user, AST_MAX_AGENT - 1); - else - res = ast_app_getdata(chan, "agent-user", user, sizeof(user) - 1, 0); - } - while (!res && (max_login_tries==0 || tries < max_login_tries)) { - tries++; - /* Check for password */ - ast_mutex_lock(&agentlock); - p = agents; - while(p) { - if (!strcmp(p->agent, user) && !p->pending) - strncpy(xpass, p->password, sizeof(xpass) - 1); - p = p->next; - } - ast_mutex_unlock(&agentlock); - if (!res) { - if (!ast_strlen_zero(xpass)) - res = ast_app_getdata(chan, "agent-pass", pass, sizeof(pass) - 1, 0); - else - pass[0] = '\0'; - } - errmsg = "agent-incorrect"; - -#if 0 - ast_log(LOG_NOTICE, "user: %s, pass: %s\n", user, pass); -#endif - - /* Check again for accuracy */ - ast_mutex_lock(&agentlock); - p = agents; - while(p) { - ast_mutex_lock(&p->lock); - if (!strcmp(p->agent, user) && - !strcmp(p->password, pass) && !p->pending) { - login_state = 1; /* Successful Login */ - /* Set Channel Specific Agent Overides */ - if (pbx_builtin_getvar_helper(chan, "AGENTACKCALL") && strlen(pbx_builtin_getvar_helper(chan, "AGENTACKCALL"))) { - if (!strcasecmp(pbx_builtin_getvar_helper(chan, "AGENTACKCALL"), "always")) - p->ackcall = 2; - else if (ast_true(pbx_builtin_getvar_helper(chan, "AGENTACKCALL"))) - p->ackcall = 1; - else - p->ackcall = 0; - tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTACKCALL"); - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Saw variable AGENTACKCALL=%s, setting ackcall to: %d for Agent '%s'.\n",tmpoptions,p->ackcall,p->agent); - } - if (pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF") && strlen(pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF"))) { - p->autologoff = atoi(pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF")); - if (p->autologoff < 0) - p->autologoff = 0; - tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF"); - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Saw variable AGENTAUTOLOGOFF=%s, setting autologff to: %d for Agent '%s'.\n",tmpoptions,p->autologoff,p->agent); - } - if (pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME") && strlen(pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME"))) { - p->wrapuptime = atoi(pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME")); - if (p->wrapuptime < 0) - p->wrapuptime = 0; - tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME"); - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Saw variable AGENTWRAPUPTIME=%s, setting wrapuptime to: %d for Agent '%s'.\n",tmpoptions,p->wrapuptime,p->agent); - } - /* End Channel Specific Agent Overides */ - if (!p->chan) { - char last_loginchan[80] = ""; - long logintime; - snprintf(agent, sizeof(agent), "Agent/%s", p->agent); - - if (callbackmode) { - char tmpchan[AST_MAX_BUF] = ""; - int pos = 0; - /* Retrieve login chan */ - for (;;) { - if (exten) { - strncpy(tmpchan, exten, sizeof(tmpchan) - 1); - res = 0; - } else - res = ast_app_getdata(chan, "agent-newlocation", tmpchan+pos, sizeof(tmpchan) - 2, 0); - if (ast_strlen_zero(tmpchan) || ast_exists_extension(chan, context && !ast_strlen_zero(context) ? context : "default", tmpchan, - 1, NULL)) - break; - if (exten) { - ast_log(LOG_WARNING, "Extension '%s' is not valid for automatic login of agent '%s'\n", exten, p->agent); - exten = NULL; - pos = 0; - } else { - ast_log(LOG_WARNING, "Extension '%s@%s' is not valid for automatic login of agent '%s'\n", tmpchan, context && !ast_strlen_zero(context) ? context : "default", p->agent); - res = ast_streamfile(chan, "invalid", chan->language); - if (!res) - res = ast_waitstream(chan, AST_DIGIT_ANY); - if (res > 0) { - tmpchan[0] = res; - tmpchan[1] = '\0'; - pos = 1; - } else { - tmpchan[0] = '\0'; - pos = 0; - } - } - } - exten = tmpchan; - if (!res) { - if (context && !ast_strlen_zero(context) && !ast_strlen_zero(tmpchan)) - snprintf(p->loginchan, sizeof(p->loginchan), "%s@%s", tmpchan, context); - else { - strncpy(last_loginchan, p->loginchan, sizeof(last_loginchan) - 1); - strncpy(p->loginchan, tmpchan, sizeof(p->loginchan) - 1); - } - if (ast_strlen_zero(p->loginchan)) { - login_state = 2; - filename = "agent-loggedoff"; - } - p->acknowledged = 0; - /* store/clear the global variable that stores agentid based on the callerid */ - if (chan->cid.cid_num) { - char agentvar[AST_MAX_BUF]; - snprintf(agentvar, sizeof(agentvar), "%s_%s",GETAGENTBYCALLERID, chan->cid.cid_num); - if (ast_strlen_zero(p->loginchan)) - pbx_builtin_setvar_helper(NULL, agentvar, NULL); - else - pbx_builtin_setvar_helper(NULL, agentvar, p->agent); - } - if(update_cdr && chan->cdr) - snprintf(chan->cdr->channel, sizeof(chan->cdr->channel), "Agent/%s", p->agent); - - } - } else { - p->loginchan[0] = '\0'; - p->acknowledged = 0; - } - ast_mutex_unlock(&p->lock); - ast_mutex_unlock(&agentlock); - if( !res && play_announcement==1 ) - res = ast_streamfile(chan, filename, chan->language); - if (!res) - ast_waitstream(chan, ""); - ast_mutex_lock(&agentlock); - ast_mutex_lock(&p->lock); - if (!res) { - res = ast_set_read_format(chan, ast_best_codec(chan->nativeformats)); - if (res) - ast_log(LOG_WARNING, "Unable to set read format to %d\n", ast_best_codec(chan->nativeformats)); - } - if (!res) { - ast_set_write_format(chan, ast_best_codec(chan->nativeformats)); - if (res) - ast_log(LOG_WARNING, "Unable to set write format to %d\n", ast_best_codec(chan->nativeformats)); - } - /* Check once more just in case */ - if (p->chan) - res = -1; - if (callbackmode && !res) { - /* Just say goodbye and be done with it */ - if (!ast_strlen_zero(p->loginchan)) { - if (p->loginstart == 0) - time(&p->loginstart); - manager_event(EVENT_FLAG_AGENT, "Agentcallbacklogin", - "Agent: %s\r\n" - "Loginchan: %s\r\n" - "Uniqueid: %s\r\n", - p->agent, p->loginchan, chan->uniqueid); - ast_queue_log("NONE", chan->uniqueid, agent, "AGENTCALLBACKLOGIN", "%s", p->loginchan); - if (option_verbose > 1) - ast_verbose(VERBOSE_PREFIX_2 "Callback Agent '%s' logged in on %s\n", p->agent, p->loginchan); - ast_device_state_changed("Agent/%s", p->agent); - } else { - logintime = time(NULL) - p->loginstart; - p->loginstart = 0; - manager_event(EVENT_FLAG_AGENT, "Agentcallbacklogoff", - "Agent: %s\r\n" - "Loginchan: %s\r\n" - "Logintime: %ld\r\n" - "Uniqueid: %s\r\n", - p->agent, last_loginchan, logintime, chan->uniqueid); - ast_queue_log("NONE", chan->uniqueid, agent, "AGENTCALLBACKLOGOFF", "%s|%ld|", last_loginchan, logintime); - if (option_verbose > 1) - ast_verbose(VERBOSE_PREFIX_2 "Callback Agent '%s' logged out\n", p->agent); - ast_device_state_changed("Agent/%s", p->agent); - } - ast_mutex_unlock(&agentlock); - if (!res) - res = ast_safe_sleep(chan, 500); - ast_mutex_unlock(&p->lock); - if (persistent_agents) - dump_agents(); - } else if (!res) { -#ifdef HONOR_MUSIC_CLASS - /* check if the moh class was changed with setmusiconhold */ - if (*(chan->musicclass)) - strncpy(p->moh, chan->musicclass, sizeof(p->moh) - 1); -#endif - ast_moh_start(chan, p->moh); - if (p->loginstart == 0) - time(&p->loginstart); - manager_event(EVENT_FLAG_AGENT, "Agentlogin", - "Agent: %s\r\n" - "Channel: %s\r\n" - "Uniqueid: %s\r\n", - p->agent, chan->name, chan->uniqueid); - if (update_cdr && chan->cdr) - snprintf(chan->cdr->channel, sizeof(chan->cdr->channel), "Agent/%s", p->agent); - ast_queue_log("NONE", chan->uniqueid, agent, "AGENTLOGIN", "%s", chan->name); - if (option_verbose > 1) - ast_verbose(VERBOSE_PREFIX_2 "Agent '%s' logged in (format %s/%s)\n", p->agent, - ast_getformatname(chan->readformat), ast_getformatname(chan->writeformat)); - /* Login this channel and wait for it to - go away */ - p->chan = chan; - if (p->ackcall > 1) - check_beep(p, 0); - else - check_availability(p, 0); - ast_mutex_unlock(&p->lock); - ast_mutex_unlock(&agentlock); - ast_device_state_changed("Agent/%s", p->agent); - while (res >= 0) { - ast_mutex_lock(&p->lock); - if (p->chan != chan) - res = -1; - ast_mutex_unlock(&p->lock); - /* Yield here so other interested threads can kick in. */ - sched_yield(); - if (res) - break; - - ast_mutex_lock(&agentlock); - ast_mutex_lock(&p->lock); - if (p->lastdisc.tv_sec) { - gettimeofday(&tv, NULL); - if ((tv.tv_sec - p->lastdisc.tv_sec) * 1000 + - (tv.tv_usec - p->lastdisc.tv_usec) / 1000 > p->wrapuptime) { - if (option_debug) - ast_log(LOG_DEBUG, "Wrapup time for %s expired!\n", p->agent); - memset(&p->lastdisc, 0, sizeof(p->lastdisc)); - if (p->ackcall > 1) - check_beep(p, 0); - else - check_availability(p, 0); - } - } - ast_mutex_unlock(&p->lock); - ast_mutex_unlock(&agentlock); - /* Synchronize channel ownership between call to agent and itself. */ - ast_mutex_lock( &p->app_lock ); - ast_mutex_lock(&p->lock); - p->owning_app = pthread_self(); - ast_mutex_unlock(&p->lock); - if (p->ackcall > 1) - res = agent_ack_sleep(p); - else - res = ast_safe_sleep_conditional( chan, 1000, - agent_cont_sleep, p ); - ast_mutex_unlock( &p->app_lock ); - if ((p->ackcall > 1) && (res == 1)) { - ast_mutex_lock(&agentlock); - ast_mutex_lock(&p->lock); - check_availability(p, 0); - ast_mutex_unlock(&p->lock); - ast_mutex_unlock(&agentlock); - res = 0; - } - sched_yield(); - } - ast_mutex_lock(&p->lock); - if (res && p->owner) - ast_log(LOG_WARNING, "Huh? We broke out when there was still an owner?\n"); - /* Log us off if appropriate */ - if (p->chan == chan) - p->chan = NULL; - p->acknowledged = 0; - logintime = time(NULL) - p->loginstart; - p->loginstart = 0; - ast_mutex_unlock(&p->lock); - manager_event(EVENT_FLAG_AGENT, "Agentlogoff", - "Agent: %s\r\n" - "Logintime: %ld\r\n" - "Uniqueid: %s\r\n", - p->agent, logintime, chan->uniqueid); - ast_queue_log("NONE", chan->uniqueid, agent, "AGENTLOGOFF", "%s|%ld", chan->name, logintime); - if (option_verbose > 1) - ast_verbose(VERBOSE_PREFIX_2 "Agent '%s' logged out\n", p->agent); - /* If there is no owner, go ahead and kill it now */ - ast_device_state_changed("Agent/%s", p->agent); - if (p->dead && !p->owner) { - ast_mutex_destroy(&p->lock); - ast_mutex_destroy(&p->app_lock); - free(p); - } - } - else { - ast_mutex_unlock(&p->lock); - p = NULL; - } - res = -1; - } else { - ast_mutex_unlock(&p->lock); - errmsg = "agent-alreadyon"; - p = NULL; - } - break; - } - ast_mutex_unlock(&p->lock); - p = p->next; - } - if (!p) - ast_mutex_unlock(&agentlock); - - if (!res && (max_login_tries==0 || tries < max_login_tries)) - res = ast_app_getdata(chan, errmsg, user, sizeof(user) - 1, 0); - } - - LOCAL_USER_REMOVE(u); - if (!res) - res = ast_safe_sleep(chan, 500); - - /* AgentLogin() exit */ - if (!callbackmode) { - return -1; - } - /* AgentCallbackLogin() exit*/ - else { - /* Set variables */ - if (login_state > 0) { - pbx_builtin_setvar_helper(chan, "AGENTNUMBER", user); - if (login_state==1) { - pbx_builtin_setvar_helper(chan, "AGENTSTATUS", "on"); - pbx_builtin_setvar_helper(chan, "AGENTEXTEN", exten); - } - else { - pbx_builtin_setvar_helper(chan, "AGENTSTATUS", "off"); - } - } - else { - pbx_builtin_setvar_helper(chan, "AGENTSTATUS", "fail"); - } - if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 1, chan->cid.cid_num)) - return 0; - /* Do we need to play agent-goodbye now that we will be hanging up? */ - if (play_announcement==1) { - if (!res) - res = ast_safe_sleep(chan, 1000); - res = ast_streamfile(chan, agent_goodbye, chan->language); - if (!res) - res = ast_waitstream(chan, ""); - if (!res) - res = ast_safe_sleep(chan, 1000); - } - } - /* We should never get here if next priority exists when in callbackmode */ - return -1; -} - -static int login_exec(struct ast_channel *chan, void *data) -{ - return __login_exec(chan, data, 0); -} - -static int callback_exec(struct ast_channel *chan, void *data) -{ - return __login_exec(chan, data, 1); -} - -static int agentmonitoroutgoing_exec(struct ast_channel *chan, void *data) -{ - int exitifnoagentid = 0; - int nowarnings = 0; - int changeoutgoing = 0; - int res = 0; - char agent[AST_MAX_AGENT], *tmp; - - if (data) { - if (strchr(data, 'd')) - exitifnoagentid = 1; - if (strchr(data, 'n')) - nowarnings = 1; - if (strchr(data, 'c')) - changeoutgoing = 1; - } - if (chan->cid.cid_num) { - char agentvar[AST_MAX_BUF]; - snprintf(agentvar, sizeof(agentvar), "%s_%s", GETAGENTBYCALLERID, chan->cid.cid_num); - if ((tmp = pbx_builtin_getvar_helper(NULL, agentvar))) { - struct agent_pvt *p = agents; - strncpy(agent, tmp, sizeof(agent) - 1); - ast_mutex_lock(&agentlock); - while (p) { - if (!strcasecmp(p->agent, tmp)) { - if (changeoutgoing) snprintf(chan->cdr->channel, sizeof(chan->cdr->channel), "Agent/%s", p->agent); - __agent_start_monitoring(chan, p, 1); - break; - } - p = p->next; - } - ast_mutex_unlock(&agentlock); - - } else { - res = -1; - if (!nowarnings) - ast_log(LOG_WARNING, "Couldn't find the global variable %s, so I can't figure out which agent (if it's an agent) is placing outgoing call.\n", agentvar); - } - } else { - res = -1; - if (!nowarnings) - ast_log(LOG_WARNING, "There is no callerid on that call, so I can't figure out which agent (if it's an agent) is placing outgoing call.\n"); - } - /* check if there is n + 101 priority */ - if (res) { - if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 101, chan->cid.cid_num)) { - chan->priority+=100; - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Going to %d priority because there is no callerid or the agentid cannot be found.\n",chan->priority); - } - else if (exitifnoagentid) - return res; - } - return 0; -} - -/* Dump AgentCallbackLogin agents to the database for persistence - */ - -static void dump_agents(void) -{ - struct agent_pvt *cur_agent = NULL; - - for (cur_agent = agents; cur_agent; cur_agent = cur_agent->next) { - if (cur_agent->chan) - continue; - - if (!ast_strlen_zero(cur_agent->loginchan)) { - if (ast_db_put(pa_family, cur_agent->agent, cur_agent->loginchan)) - ast_log(LOG_WARNING, "failed to create persistent entry!\n"); - else if (option_debug) - ast_log(LOG_DEBUG, "Saved Agent: %s on %s\n", cur_agent->agent, cur_agent->loginchan); - } else { - /* Delete - no agent or there is an error */ - ast_db_del(pa_family, cur_agent->agent); - } - } -} - -/* Reload the persistent agents from astdb */ -static void reload_agents(void) -{ - char *agent_num; - struct ast_db_entry *db_tree; - struct ast_db_entry *entry; - struct agent_pvt *cur_agent; - char agent_data[80]; - - db_tree = ast_db_gettree(pa_family, NULL); - - ast_mutex_lock(&agentlock); - for (entry = db_tree; entry; entry = entry->next) { - agent_num = entry->key + strlen(pa_family) + 2; - cur_agent = agents; - while (cur_agent) { - ast_mutex_lock(&cur_agent->lock); - if (strcmp(agent_num, cur_agent->agent) == 0) - break; - ast_mutex_unlock(&cur_agent->lock); - cur_agent = cur_agent->next; - } - if (!cur_agent) { - ast_db_del(pa_family, agent_num); - continue; - } else - ast_mutex_unlock(&cur_agent->lock); - if (!ast_db_get(pa_family, agent_num, agent_data, sizeof(agent_data)-1)) { - if (option_debug) - ast_log(LOG_DEBUG, "Reload Agent: %s on %s\n", cur_agent->agent, agent_data); - strncpy(cur_agent->loginchan, agent_data, sizeof(cur_agent->loginchan)-1); - if (cur_agent->loginstart == 0) - time(&cur_agent->loginstart); - ast_device_state_changed("Agent/%s", cur_agent->agent); - } - } - ast_mutex_unlock(&agentlock); - if (db_tree) { - ast_log(LOG_NOTICE, "Agents sucessfully reloaded from database.\n"); - ast_db_freetree(db_tree); - } -} - - -/*--- agent_devicestate: Part of PBX channel interface ---*/ -static int agent_devicestate(void *data) -{ - struct agent_pvt *p; - char *s; - ast_group_t groupmatch; - int groupoff; - int waitforagent=0; - int res = AST_DEVICE_INVALID; - - s = data; - if ((s[0] == '@') && (sscanf(s + 1, "%d", &groupoff) == 1)) { - groupmatch = (1 << groupoff); - } else if ((s[0] == ':') && (sscanf(s + 1, "%d", &groupoff) == 1)) { - groupmatch = (1 << groupoff); - waitforagent = 1; - } else { - groupmatch = 0; - } - - /* Check actual logged in agents first */ - ast_mutex_lock(&agentlock); - p = agents; - while(p) { - ast_mutex_lock(&p->lock); - if (!p->pending && ((groupmatch && (p->group & groupmatch)) || !strcmp(data, p->agent))) { - if (p->owner) { - if (res != AST_DEVICE_INUSE) - res = AST_DEVICE_BUSY; - } else { - if (res == AST_DEVICE_BUSY) - res = AST_DEVICE_INUSE; - if (p->chan || !ast_strlen_zero(p->loginchan)) { - if (res == AST_DEVICE_INVALID) - res = AST_DEVICE_UNKNOWN; - } else if (res == AST_DEVICE_INVALID) - res = AST_DEVICE_UNAVAILABLE; - } - if (!strcmp(data, p->agent)) { - ast_mutex_unlock(&p->lock); - break; - } - } - ast_mutex_unlock(&p->lock); - p = p->next; - } - ast_mutex_unlock(&agentlock); - return res; -} - -/*--- load_module: Initialize channel module ---*/ -int load_module() -{ - /* Make sure we can register our agent channel type */ - if (ast_channel_register(&agent_tech)) { - ast_log(LOG_ERROR, "Unable to register channel class %s\n", channeltype); - return -1; - } - /* Dialplan applications */ - ast_register_application(app, login_exec, synopsis, descrip); - ast_register_application(app2, callback_exec, synopsis2, descrip2); - ast_register_application(app3, agentmonitoroutgoing_exec, synopsis3, descrip3); - /* Manager command */ - ast_manager_register2("Agents", EVENT_FLAG_AGENT, action_agents, "Lists agents and their status", mandescr_agents); - /* CLI Application */ - ast_cli_register(&cli_show_agents); - ast_cli_register(&cli_agent_logoff); - /* Read in the config */ - read_agent_config(); - if (persistent_agents) - reload_agents(); - return 0; -} - -int reload() -{ - read_agent_config(); - if (persistent_agents) - reload_agents(); - return 0; -} - -int unload_module() -{ - struct agent_pvt *p; - /* First, take us out of the channel loop */ - /* Unregister CLI application */ - ast_cli_unregister(&cli_show_agents); - ast_cli_unregister(&cli_agent_logoff); - /* Unregister dialplan applications */ - ast_unregister_application(app); - ast_unregister_application(app2); - ast_unregister_application(app3); - /* Unregister manager command */ - ast_manager_unregister("Agents"); - /* Unregister channel */ - ast_channel_unregister(&agent_tech); - if (!ast_mutex_lock(&agentlock)) { - /* Hangup all interfaces if they have an owner */ - p = agents; - while(p) { - if (p->owner) - ast_softhangup(p->owner, AST_SOFTHANGUP_APPUNLOAD); - p = p->next; - } - agents = NULL; - ast_mutex_unlock(&agentlock); - } else { - ast_log(LOG_WARNING, "Unable to lock the monitor\n"); - return -1; - } - return 0; -} - -int usecount() -{ - int res; - ast_mutex_lock(&usecnt_lock); - res = usecnt; - ast_mutex_unlock(&usecnt_lock); - return res; -} - -char *key() -{ - return ASTERISK_GPL_KEY; -} - -char *description() -{ - return (char *) desc; -} - Index: apps/Makefile =================================================================== RCS file: /usr/cvsroot/asterisk/apps/Makefile,v retrieving revision 1.97 diff -u -u -r1.97 Makefile --- apps/Makefile 1 Apr 2005 17:00:50 -0000 1.97 +++ apps/Makefile 1 Apr 2005 22:11:01 -0000 @@ -19,7 +19,7 @@ app_system.so app_echo.so app_record.so app_image.so app_url.so app_disa.so \ app_adsiprog.so app_getcpeid.so app_milliwatt.so \ app_zapateller.so app_setcallerid.so app_festival.so \ - app_queue.so app_senddtmf.so app_parkandannounce.so app_striplsd.so \ + app_senddtmf.so app_parkandannounce.so app_striplsd.so \ app_setcidname.so app_lookupcidname.so app_substring.so app_macro.so \ app_authenticate.so app_softhangup.so app_lookupblacklist.so \ app_waitforring.so app_privacy.so app_db.so app_chanisavail.so \ Index: channels/Makefile =================================================================== RCS file: /usr/cvsroot/asterisk/channels/Makefile,v retrieving revision 1.68 diff -u -u -r1.68 Makefile --- channels/Makefile 28 Mar 2005 16:28:00 -0000 1.68 +++ channels/Makefile 1 Apr 2005 22:10:46 -0000 @@ -25,7 +25,7 @@ CHANNEL_LIBS=chan_modem.so chan_sip.so \ chan_modem_aopen.so \ chan_modem_bestdata.so \ - chan_agent.so chan_mgcp.so chan_iax2.so \ + chan_mgcp.so chan_iax2.so \ chan_local.so chan_skinny.so chan_features.so ifeq (${OSARCH},OpenBSD)