diff options
Diffstat (limited to '1.2-netsec/channels/chan_agent.c')
-rw-r--r-- | 1.2-netsec/channels/chan_agent.c | 2507 |
1 files changed, 0 insertions, 2507 deletions
diff --git a/1.2-netsec/channels/chan_agent.c b/1.2-netsec/channels/chan_agent.c deleted file mode 100644 index 0193d8cdd..000000000 --- a/1.2-netsec/channels/chan_agent.c +++ /dev/null @@ -1,2507 +0,0 @@ -/* - * Asterisk -- An open source telephony toolkit. - * - * Copyright (C) 1999 - 2005, Digium, Inc. - * - * Mark Spencer <markster@digium.com> - * - * See http://www.asterisk.org for more information about - * the Asterisk project. Please do not directly contact - * any of the maintainers of this project for assistance; - * the project provides a web site, mailing lists and IRC - * channels for your use. - * - * This program is free software, distributed under the terms of - * the GNU General Public License Version 2. See the LICENSE file - * at the top of the source tree. - */ - - -/*! \file - * \brief Implementation of Agents (proxy channel) - * - * This file is the implementation of Agents modules. - * It is a dynamic module that is loaded by Asterisk. - * \par See also - * \arg \ref Config_agent - * - * \ingroup channel_drivers - */ - -#include <stdio.h> -#include <string.h> -#include <errno.h> -#include <unistd.h> -#include <sys/socket.h> -#include <stdlib.h> -#include <fcntl.h> -#include <netdb.h> -#include <netinet/in.h> -#include <arpa/inet.h> -#include <sys/signal.h> - -#include "asterisk.h" - -ASTERISK_FILE_VERSION(__FILE__, "$Revision$") - -#include "asterisk/lock.h" -#include "asterisk/channel.h" -#include "asterisk/config.h" -#include "asterisk/logger.h" -#include "asterisk/module.h" -#include "asterisk/pbx.h" -#include "asterisk/options.h" -#include "asterisk/lock.h" -#include "asterisk/sched.h" -#include "asterisk/io.h" -#include "asterisk/rtp.h" -#include "asterisk/acl.h" -#include "asterisk/callerid.h" -#include "asterisk/file.h" -#include "asterisk/cli.h" -#include "asterisk/app.h" -#include "asterisk/musiconhold.h" -#include "asterisk/manager.h" -#include "asterisk/features.h" -#include "asterisk/utils.h" -#include "asterisk/causes.h" -#include "asterisk/astdb.h" -#include "asterisk/devicestate.h" -#include "asterisk/monitor.h" - -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" -"comparison 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 const char mandescr_agent_logoff[] = -"Description: Sets an agent as no longer logged in.\n" -"Variables: (Names marked with * are required)\n" -" *Agent: Agent ID of the agent to log off\n" -" Soft: Set to 'true' to not hangup existing calls\n"; - -static const char mandescr_agent_callback_login[] = -"Description: Sets an agent as logged in with callback.\n" -"Variables: (Names marked with * are required)\n" -" *Agent: Agent ID of the agent to login\n" -" *Exten: Extension to use for callback\n" -" Context: Context to use for callback\n" -" AckCall: Set to 'true' to require an acknowledgement by '#' when agent is called back\n" -" WrapupTime: the minimum amount of time after disconnecting before the caller can receive a new call\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 length 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" - -/** - * Structure representing an agent. - */ -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]; /**< channel they logged in from */ - char logincallerid[80]; /**< Caller ID they had when they logged in */ - struct ast_channel *chan; /**< Channel we use */ - struct agent_pvt *next; /**< Next Agent in the linked list. */ -}; - -static struct agent_pvt *agents = NULL; /**< Holds the list of agents (loaded form agents.conf). */ - -#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;x<AST_MAX_FDS;x++) {\ - if (x != AST_MAX_FDS - 2) \ - ast->fds[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_sendtext(struct ast_channel *ast, const char *text); -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, - .send_text = agent_sendtext, - .exception = agent_read, - .indicate = agent_indicate, - .fixup = agent_fixup, - .bridged_channel = agent_bridgedchannel, -}; - -/** - * Unlink (that is, take outside of the linked list) an agent. - * - * @param agent Agent to be unlinked. - */ -static void agent_unlink(struct agent_pvt *agent) -{ - struct agent_pvt *p, *prev; - prev = NULL; - p = agents; - // Iterate over all agents looking for the one. - while(p) { - if (p == agent) { - // Once it wal found, check if it is the first one. - if (prev) - // If it is not, tell the previous agent that the next one is the next one of the current (jumping the current). - prev->next = agent->next; - else - // If it is the first one, just change the general pointer to point to the second one. - agents = agent->next; - // We are done. - break; - } - prev = p; - p = p->next; - } -} - -/** - * Adds an agent to the global list of agents. - * - * @param agent A string with the username, password and real name of an agent. As defined in agents.conf. Example: "13,169,John Smith" - * @param pending If it is pending or not. - * @return The just created agent. - * @sa agent_pvt, agents. - */ -static struct agent_pvt *add_agent(char *agent, int pending) -{ - int argc; - char *argv[3]; - char *args; - char *password = NULL; - char *name = NULL; - char *agt = NULL; - struct agent_pvt *p, *prev; - - args = ast_strdupa(agent); - - // Extract username (agt), password and name from agent (args). - if ((argc = ast_app_separate_args(args, ',', argv, sizeof(argv) / sizeof(argv[0])))) { - agt = argv[0]; - if (argc > 1) { - password = argv[1]; - while (*password && *password < 33) password++; - } - if (argc > 2) { - name = argv[2]; - while (*name && *name < 33) name++; - } - } else { - ast_log(LOG_WARNING, "A blank agent line!\n"); - } - - // Are we searching for the agent here ? to see if it exists already ? - prev=NULL; - p = agents; - while(p) { - if (!pending && !strcmp(p->agent, agt)) - break; - prev = p; - p = p->next; - } - if (!p) { - // Build the agent. - p = malloc(sizeof(struct agent_pvt)); - if (p) { - memset(p, 0, sizeof(struct agent_pvt)); - ast_copy_string(p->agent, agt, sizeof(p->agent)); - 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; - - } else { - return NULL; - } - } - - ast_copy_string(p->password, password ? password : "", sizeof(p->password)); - ast_copy_string(p->name, name ? name : "", sizeof(p->name)); - ast_copy_string(p->moh, moh, sizeof(p->moh)); - p->ackcall = ackcall; - p->autologoff = autologoff; - - /* If someone reduces the wrapuptime and reloads, we want it - * to change the wrapuptime immediately on all calls */ - if (p->wrapuptime > wrapuptime) { - struct timeval now = ast_tvnow(); - /* XXX check what is this exactly */ - - /* We won't be pedantic and check the tv_usec val */ - if (p->lastdisc.tv_sec > (now.tv_sec + wrapuptime/1000)) { - p->lastdisc.tv_sec = now.tv_sec + wrapuptime/1000; - p->lastdisc.tv_usec = now.tv_usec; - } - } - p->wrapuptime = wrapuptime; - - if (pending) - p->dead = 1; - else - p->dead = 0; - return p; -} - -/** - * Deletes an agent after doing some clean up. - * Further documentation: How safe is this function ? What state should the agent be to be cleaned. - * @param p Agent to be deleted. - * @returns Always 0. - */ -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) - p->lastdisc = ast_tvadd(ast_tvnow(), ast_samp2tv(p->wrapuptime, 1000)); - } - p->chan = NULL; - p->acknowledged = 0; - } - } else { - /* if acknowledgement is not required, and the channel is up, we may have missed - an AST_CONTROL_ANSWER (if there was one), so mark the call acknowledged anyway */ - if (!p->ackcall && !p->acknowledged && p->chan && (p->chan->_state == AST_STATE_UP)) - p->acknowledged = 1; - switch (f->frametype) { - case AST_FRAME_CONTROL: - if (f->subclass == AST_CONTROL_ANSWER) { - 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; - /* Use the builtin answer frame for the - recording start check below. */ - ast_frfree(f); - f = &answer_frame; - } - } - break; - case AST_FRAME_DTMF: - if (!p->acknowledged && (f->subclass == '#')) { - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "%s acknowledged\n", p->chan->name); - p->acknowledged = 1; - ast_frfree(f); - f = &answer_frame; - } else if (f->subclass == '*') { - /* terminates call */ - ast_frfree(f); - f = NULL; - } - break; - case AST_FRAME_VOICE: - /* don't pass voice until the call is acknowledged */ - if (!p->acknowledged) { - ast_frfree(f); - f = &null_frame; - } - break; - } - } - - 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_sendtext(struct ast_channel *ast, const char *text) -{ - struct agent_pvt *p = ast->tech_pvt; - int res = -1; - ast_mutex_lock(&p->lock); - if (p->chan) - res = ast_sendtext(p->chan, text); - 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; -} - -/* store/clear the global variable that stores agentid based on the callerid */ -static void set_agentbycallerid(const char *callerid, const char *agent) -{ - char buf[AST_MAX_BUF]; - - /* if there is no Caller ID, nothing to do */ - if (ast_strlen_zero(callerid)) - return; - - snprintf(buf, sizeof(buf), "%s_%s",GETAGENTBYCALLERID, callerid); - pbx_builtin_setvar_helper(NULL, buf, agent); -} - -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->lastdisc = ast_tvadd(ast_tvnow(), ast_samp2tv(p->wrapuptime, 1000)); - else - p->lastdisc = ast_tv(0,0); - 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"); - set_agentbycallerid(p->logincallerid, NULL); - ast_device_state_changed("Agent/%s", p->agent); - p->loginchan[0] = '\0'; - p->logincallerid[0] = '\0'; - if (persistent_agents) - dump_agents(); - } - } 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_mutex_lock(&p->chan->lock); - ast_moh_start(p->chan, p->moh); - ast_mutex_unlock(&p->chan->lock); - } - } - ast_mutex_unlock(&p->lock); - ast_device_state_changed("Agent/%s", p->agent); - - 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 */ - p->lastdisc = ast_tvnow(); - 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; - int res; - - p = (struct agent_pvt *)data; - - ast_mutex_lock(&p->lock); - res = p->app_sleep_cond; - if (p->lastdisc.tv_sec) { - if (ast_tvdiff_ms(ast_tvnow(), p->lastdisc) > 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 = bridge->tech_pvt; - struct ast_channel *ret=NULL; - - if (p) { - 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 : "<none>"); - 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; - ast_copy_string(tmp->language, p->chan->language, sizeof(tmp->language)); - ast_copy_string(tmp->context, p->chan->context, sizeof(tmp->context)); - ast_copy_string(tmp->exten, p->chan->exten, sizeof(tmp->exten)); - } 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 configuration data. The file named agents.conf. - * - * @returns Always 0, or so it seems. - */ -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; - } - strcpy(moh, "default"); - /* set the default recording values */ - recordagentcalls = 0; - createlink = 0; - strcpy(recordformat, "wav"); - strcpy(recordformatext, "wav"); - urlprefix[0] = '\0'; - savecallsin[0] = '\0'; - - /* Read in [general] section for persistence */ - 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")) { - ast_copy_string(moh, v->value, sizeof(moh)); - } 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")) { - ast_copy_string(recordformat, v->value, sizeof(recordformat)); - if (!strcasecmp(v->value, "wav49")) - strcpy(recordformatext, "WAV"); - else - ast_copy_string(recordformatext, v->value, sizeof(recordformatext)); - } else if (!strcasecmp(v->name, "urlprefix")) { - ast_copy_string(urlprefix, v->value, sizeof(urlprefix)); - if (urlprefix[strlen(urlprefix) - 1] != '/') - strncat(urlprefix, "/", sizeof(urlprefix) - strlen(urlprefix) - 1); - } else if (!strcasecmp(v->name, "savecallsin")) { - if (v->value[0] == '/') - ast_copy_string(savecallsin, v->value, sizeof(savecallsin)); - 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")) { - ast_copy_string(beep, v->value, sizeof(beep)); - } - 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); - ast_copy_string(parent->context, chan->context, sizeof(parent->context)); - /* 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++; - tv = ast_tvnow(); -#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)) { - p->lastdisc = ast_tv(0, 0); - /* 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; -} - -/** - * Lists agents and their status to the Manager API. - * It is registered on load_module() and it gets called by the manager backend. - * @param s - * @param m - * @returns - * @sa action_agent_logoff(), action_agent_callback_login(), load_module(). - */ -static int action_agents(struct mansession *s, struct message *m) -{ - char *id = astman_get_header(m,"ActionID"); - char idText[256] = ""; - char chanbuf[256]; - struct agent_pvt *p; - char *username = NULL; - char *loginChan = NULL; - char *talkingtoChan = NULL; - char *status = NULL; - - if (!ast_strlen_zero(id)) - snprintf(idText, sizeof(idText) ,"ActionID: %s\r\n", id); - astman_send_ack(s, m, "Agents will follow"); - 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 (!ast_strlen_zero(p->loginchan) && !p->chan) { - loginChan = p->loginchan; - talkingtoChan = "n/a"; - status = "AGENT_IDLE"; - if (p->acknowledged) { - snprintf(chanbuf, sizeof(chanbuf), " %s (Confirmed)", p->loginchan); - loginChan = chanbuf; - } - } else if (p->chan) { - loginChan = ast_strdupa(p->chan->name); - if (p->owner && p->owner->_bridge) { - talkingtoChan = p->chan->cid.cid_num; - status = "AGENT_ONCALL"; - } else { - talkingtoChan = "n/a"; - status = "AGENT_IDLE"; - } - } 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: %d\r\n" - "TalkingTo: %s\r\n" - "%s" - "\r\n", - p->agent, username, status, loginChan, (int)p->loginstart, talkingtoChan, idText); - ast_mutex_unlock(&p->lock); - p = p->next; - } - ast_mutex_unlock(&agentlock); - ast_cli(s->fd, "Event: AgentsComplete\r\n" - "%s" - "\r\n",idText); - return 0; -} - -static int agent_logoff(char *agent, int soft) -{ - struct agent_pvt *p; - long logintime; - int ret = -1; /* Return -1 if no agent if found */ - - for (p=agents; p; p=p->next) { - if (!strcasecmp(p->agent, agent)) { - if (!soft) { - if (p->owner) { - ast_softhangup(p->owner, AST_SOFTHANGUP_EXPLICIT); - } - if (p->chan) { - ast_softhangup(p->chan, AST_SOFTHANGUP_EXPLICIT); - } - } - ret = 0; /* found an agent => return 0 */ - 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"); - set_agentbycallerid(p->logincallerid, NULL); - p->loginchan[0] = '\0'; - p->logincallerid[0] = '\0'; - ast_device_state_changed("Agent/%s", p->agent); - if (persistent_agents) - dump_agents(); - break; - } - } - - return ret; -} - -static int agent_logoff_cmd(int fd, int argc, char **argv) -{ - int ret; - char *agent; - - if (argc < 3 || argc > 4) - return RESULT_SHOWUSAGE; - if (argc == 4 && strcasecmp(argv[3], "soft")) - return RESULT_SHOWUSAGE; - - agent = argv[2] + 6; - ret = agent_logoff(agent, argc == 4); - if (ret == 0) - ast_cli(fd, "Logging out %s\n", agent); - - return RESULT_SUCCESS; -} - -/** - * Sets an agent as no longer logged in in the Manager API. - * It is registered on load_module() and it gets called by the manager backend. - * @param s - * @param m - * @returns - * @sa action_agents(), action_agent_callback_login(), load_module(). - */ -static int action_agent_logoff(struct mansession *s, struct message *m) -{ - char *agent = astman_get_header(m, "Agent"); - char *soft_s = astman_get_header(m, "Soft"); /* "true" is don't hangup */ - int soft; - int ret; /* return value of agent_logoff */ - - if (ast_strlen_zero(agent)) { - astman_send_error(s, m, "No agent specified"); - return 0; - } - - if (ast_true(soft_s)) - soft = 1; - else - soft = 0; - - ret = agent_logoff(agent, soft); - if (ret == 0) - astman_send_ack(s, m, "Agent logged out"); - else - astman_send_error(s, m, "No such agent"); - - return 0; -} - -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; -} - -/** - * 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]; - int count_agents = 0; /* Number of agents configured */ - int online_agents = 0; /* Number of online agents */ - int offline_agents = 0; /* Number of offline agents */ - 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 { - strcpy(talkingto, " is idle"); - } - online_agents++; - } else if (!ast_strlen_zero(p->loginchan)) { - snprintf(location, sizeof(location) - 20, "available at '%s'", p->loginchan); - talkingto[0] = '\0'; - online_agents++; - if (p->acknowledged) - strncat(location, " (Confirmed)", sizeof(location) - strlen(location) - 1); - } else { - strcpy(location, "not logged in"); - talkingto[0] = '\0'; - offline_agents++; - } - 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); - count_agents++; - } - ast_mutex_unlock(&p->lock); - p = p->next; - } - ast_mutex_unlock(&agentlock); - if ( !count_agents ) { - ast_cli(fd, "No Agents are configured in %s\n",config); - } else { - ast_cli(fd, "%d agents configured [%d online , %d offline]\n",count_agents, online_agents, offline_agents); - } - ast_cli(fd, "\n"); - - 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 <channel> [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; - -/*! - * \brief Log in agent application. - * - * \param chan - * \param data - * \param callbackmode non-zero for AgentCallbackLogin - */ -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; - 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 *parse; - AST_DECLARE_APP_ARGS(args, - AST_APP_ARG(agent_id); - AST_APP_ARG(options); - AST_APP_ARG(extension); - ); - char *tmpoptions = NULL; - char *context = NULL; - int play_announcement = 1; - char agent_goodbye[AST_MAX_FILENAME_LEN]; - int update_cdr = updatecdr; - char *filename = "agent-loginok"; - char tmpchan[AST_MAX_BUF] = ""; - - LOCAL_USER_ADD(u); - - if (!(parse = ast_strdupa(data))) { - ast_log(LOG_ERROR, "Out of memory!\n"); - LOCAL_USER_REMOVE(u); - return -1; - } - - AST_STANDARD_APP_ARGS(args, parse); - - ast_copy_string(agent_goodbye, agentgoodbye, sizeof(agent_goodbye)); - - /* 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 */ - - if (callbackmode && args.extension) { - parse = args.extension; - args.extension = strsep(&parse, "@"); - context = parse; - } - - if (!ast_strlen_zero(args.options)) { - if (strchr(args.options, 's')) { - play_announcement = 0; - } - } - - if (chan->_state != AST_STATE_UP) - res = ast_answer(chan); - if (!res) { - if (!ast_strlen_zero(args.agent_id)) - ast_copy_string(user, args.agent_id, AST_MAX_AGENT); - 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) - ast_copy_string(xpass, p->password, sizeof(xpass)); - 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 */ - - /* Ensure we can't be gotten until we're done */ - gettimeofday(&p->lastdisc, NULL); - p->lastdisc.tv_sec++; - - /* Set Channel Specific Agent Overrides */ - 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 Overrides */ - if (!p->chan) { - char last_loginchan[80] = ""; - long logintime; - snprintf(agent, sizeof(agent), "Agent/%s", p->agent); - - if (callbackmode) { - int pos = 0; - /* Retrieve login chan */ - for (;;) { - if (!ast_strlen_zero(args.extension)) { - ast_copy_string(tmpchan, args.extension, sizeof(tmpchan)); - 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, !ast_strlen_zero(context) ? context : "default", tmpchan, - 1, NULL)) - break; - if (args.extension) { - ast_log(LOG_WARNING, "Extension '%s' is not valid for automatic login of agent '%s'\n", args.extension, p->agent); - args.extension = NULL; - pos = 0; - } else { - ast_log(LOG_WARNING, "Extension '%s@%s' is not valid for automatic login of agent '%s'\n", tmpchan, !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; - } - } - } - args.extension = tmpchan; - if (!res) { - set_agentbycallerid(p->logincallerid, NULL); - if (!ast_strlen_zero(context) && !ast_strlen_zero(tmpchan)) - snprintf(p->loginchan, sizeof(p->loginchan), "%s@%s", tmpchan, context); - else { - ast_copy_string(last_loginchan, p->loginchan, sizeof(last_loginchan)); - ast_copy_string(p->loginchan, tmpchan, sizeof(p->loginchan)); - } - p->acknowledged = 0; - if (ast_strlen_zero(p->loginchan)) { - login_state = 2; - filename = "agent-loggedoff"; - } else { - if (chan->cid.cid_num) { - ast_copy_string(p->logincallerid, chan->cid.cid_num, sizeof(p->logincallerid)); - set_agentbycallerid(p->logincallerid, p->agent); - } else - p->logincallerid[0] = '\0'; - } - - if(update_cdr && chan->cdr) - snprintf(chan->cdr->channel, sizeof(chan->cdr->channel), "Agent/%s", p->agent); - - } - } else { - p->loginchan[0] = '\0'; - p->logincallerid[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) { - 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)) - ast_copy_string(p->moh, chan->musicclass, sizeof(p->moh)); -#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) { - if (ast_tvdiff_ms(ast_tvnow(), p->lastdisc) > p->wrapuptime) { - if (option_debug) - ast_log(LOG_DEBUG, "Wrapup time for %s expired!\n", p->agent); - p->lastdisc = ast_tv(0, 0); - 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); - } - - if (!res) - res = ast_safe_sleep(chan, 500); - - /* AgentLogin() exit */ - if (!callbackmode) { - LOCAL_USER_REMOVE(u); - 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", args.extension); - } - 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)) { - LOCAL_USER_REMOVE(u); - return 0; - } - /* Do we need to play agent-goodbye now that we will be hanging up? */ - if (play_announcement) { - 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); - } - } - - LOCAL_USER_REMOVE(u); - - /* We should never get here if next priority exists when in callbackmode */ - return -1; -} - -/** - * Called by the AgentLogin application (from the dial plan). - * - * @param chan - * @param data - * @returns - * @sa callback_login_exec(), agentmonitoroutgoing_exec(), load_module(). - */ -static int login_exec(struct ast_channel *chan, void *data) -{ - return __login_exec(chan, data, 0); -} - -/** - * Called by the AgentCallbackLogin application (from the dial plan). - * - * @param chan - * @param data - * @returns - * @sa login_exec(), agentmonitoroutgoing_exec(), load_module(). - */ -static int callback_exec(struct ast_channel *chan, void *data) -{ - return __login_exec(chan, data, 1); -} - -/** - * Sets an agent as logged in by callback in the Manager API. - * It is registered on load_module() and it gets called by the manager backend. - * @param s - * @param m - * @returns - * @sa action_agents(), action_agent_logoff(), load_module(). - */ -static int action_agent_callback_login(struct mansession *s, struct message *m) -{ - char *agent = astman_get_header(m, "Agent"); - char *exten = astman_get_header(m, "Exten"); - char *context = astman_get_header(m, "Context"); - char *wrapuptime_s = astman_get_header(m, "WrapupTime"); - char *ackcall_s = astman_get_header(m, "AckCall"); - struct agent_pvt *p; - int login_state = 0; - - if (ast_strlen_zero(agent)) { - astman_send_error(s, m, "No agent specified"); - return 0; - } - - if (ast_strlen_zero(exten)) { - astman_send_error(s, m, "No extension specified"); - return 0; - } - - ast_mutex_lock(&agentlock); - p = agents; - while(p) { - if (strcmp(p->agent, agent) || p->pending) { - p = p->next; - continue; - } - if (p->chan) { - login_state = 2; /* already logged in (and on the phone)*/ - break; - } - ast_mutex_lock(&p->lock); - login_state = 1; /* Successful Login */ - - if (ast_strlen_zero(context)) - ast_copy_string(p->loginchan, exten, sizeof(p->loginchan)); - else - snprintf(p->loginchan, sizeof(p->loginchan), "%s@%s", exten, context); - - if (!ast_strlen_zero(wrapuptime_s)) { - p->wrapuptime = atoi(wrapuptime_s); - if (p->wrapuptime < 0) - p->wrapuptime = 0; - } - - if (ast_true(ackcall_s)) - p->ackcall = 1; - else - p->ackcall = 0; - - if (p->loginstart == 0) - time(&p->loginstart); - manager_event(EVENT_FLAG_AGENT, "Agentcallbacklogin", - "Agent: %s\r\n" - "Loginchan: %s\r\n", - p->agent, p->loginchan); - ast_queue_log("NONE", "NONE", 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); - ast_mutex_unlock(&p->lock); - p = p->next; - if (persistent_agents) - dump_agents(); - } - ast_mutex_unlock(&agentlock); - - if (login_state == 1) - astman_send_ack(s, m, "Agent logged in"); - else if (login_state == 0) - astman_send_error(s, m, "No such agent"); - else if (login_state == 2) - astman_send_error(s, m, "Agent already logged in"); - - return 0; -} - -/** - * Called by the AgentMonitorOutgoing application (from the dial plan). - * - * @param chan - * @param data - * @returns - * @sa login_exec(), callback_login_exec(), load_module(). - */ -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; - ast_copy_string(agent, tmp, sizeof(agent)); - 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; - char buf[256]; - - for (cur_agent = agents; cur_agent; cur_agent = cur_agent->next) { - if (cur_agent->chan) - continue; - - if (!ast_strlen_zero(cur_agent->loginchan)) { - snprintf(buf, sizeof(buf), "%s;%s", cur_agent->loginchan, cur_agent->logincallerid); - if (ast_db_put(pa_family, cur_agent->agent, buf)) - 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[256]; - char *parse; - char *agent_chan; - char *agent_callerid; - - 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); - parse = agent_data; - agent_chan = strsep(&parse, ";"); - agent_callerid = strsep(&parse, ";"); - ast_copy_string(cur_agent->loginchan, agent_chan, sizeof(cur_agent->loginchan)); - if (agent_callerid) { - ast_copy_string(cur_agent->logincallerid, agent_callerid, sizeof(cur_agent->logincallerid)); - set_agentbycallerid(cur_agent->logincallerid, cur_agent->agent); - } else - cur_agent->logincallerid[0] = '\0'; - 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 successfully 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; -} - -/** - * Initialize the Agents module. - * This function is being called by Asterisk when loading the module. Among other thing it registers applications, cli commands and reads the cofiguration file. - * - * @returns int Always 0. - */ -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 commands */ - ast_manager_register2("Agents", EVENT_FLAG_AGENT, action_agents, "Lists agents and their status", mandescr_agents); - ast_manager_register2("AgentLogoff", EVENT_FLAG_AGENT, action_agent_logoff, "Sets an agent as no longer logged in", mandescr_agent_logoff); - ast_manager_register2("AgentCallbackLogin", EVENT_FLAG_AGENT, action_agent_callback_login, "Sets an agent as logged in by callback", mandescr_agent_callback_login); - /* 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"); - ast_manager_unregister("AgentLogoff"); - ast_manager_unregister("AgentCallbackLogin"); - /* 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() -{ - return usecnt; -} - -char *key() -{ - return ASTERISK_GPL_KEY; -} - -char *description() -{ - return (char *) desc; -} - |