Newer
Older
AST_LIST_UNLOCK(&agents);
Mark Spencer
committed
}
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
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);
ast_mutex_unlock(&p->lock);
res = -1;
} else {
ast_mutex_unlock(&p->lock);
errmsg = "agent-alreadyon";
p = NULL;
}
break;
ast_mutex_unlock(&p->lock);
AST_LIST_UNLOCK(&agents);
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);
}
/* 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_LIST_LOCK(&agents);
AST_LIST_TRAVERSE(&agents, p, list) {
if (strcmp(p->agent, agent) || p->pending) {
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))
Kevin P. Fleming
committed
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);
if (persistent_agents)
dump_agents();
}
AST_LIST_UNLOCK(&agents);
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().
*/
Martin Pycko
committed
static int agentmonitoroutgoing_exec(struct ast_channel *chan, void *data)
{
int exitifnoagentid = 0;
int nowarnings = 0;
int changeoutgoing = 0;
Martin Pycko
committed
int res = 0;
char agent[AST_MAX_AGENT];
Martin Pycko
committed
if (data) {
if (strchr(data, 'd'))
exitifnoagentid = 1;
if (strchr(data, 'n'))
nowarnings = 1;
if (strchr(data, 'c'))
changeoutgoing = 1;
Martin Pycko
committed
}
if (chan->cid.cid_num) {
const char *tmp;
Martin Pycko
committed
char agentvar[AST_MAX_BUF];
snprintf(agentvar, sizeof(agentvar), "%s_%s", GETAGENTBYCALLERID, chan->cid.cid_num);
Martin Pycko
committed
if ((tmp = pbx_builtin_getvar_helper(NULL, agentvar))) {
struct agent_pvt *p;
ast_copy_string(agent, tmp, sizeof(agent));
AST_LIST_LOCK(&agents);
AST_LIST_TRAVERSE(&agents, p, list) {
Martin Pycko
committed
if (!strcasecmp(p->agent, tmp)) {
if (changeoutgoing) snprintf(chan->cdr->channel, sizeof(chan->cdr->channel), "Agent/%s", p->agent);
Martin Pycko
committed
__agent_start_monitoring(chan, p, 1);
break;
}
}
AST_LIST_UNLOCK(&agents);
Martin Pycko
committed
} 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)) {
Martin Pycko
committed
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);
Martin Pycko
committed
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;
Kevin P. Fleming
committed
char buf[256];
AST_LIST_TRAVERSE(&agents, cur_agent, list) {
if (!ast_strlen_zero(cur_agent->loginchan)) {
Kevin P. Fleming
committed
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.
*/
char *agent_num;
struct ast_db_entry *db_tree;
struct ast_db_entry *entry;
struct agent_pvt *cur_agent;
Kevin P. Fleming
committed
char agent_data[256];
char *parse;
char *agent_chan;
char *agent_callerid;
db_tree = ast_db_gettree(pa_family, NULL);
AST_LIST_LOCK(&agents);
for (entry = db_tree; entry; entry = entry->next) {
AST_LIST_TRAVERSE(&agents, cur_agent, list) {
if (strcmp(agent_num, cur_agent->agent) == 0)
break;
ast_mutex_unlock(&cur_agent->lock);
}
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);
Kevin P. Fleming
committed
parse = agent_data;
agent_chan = strsep(&parse, ";");
agent_callerid = strsep(&parse, ";");
ast_copy_string(cur_agent->loginchan, agent_chan, sizeof(cur_agent->loginchan));
Kevin P. Fleming
committed
if (agent_callerid) {
Kevin P. Fleming
committed
ast_copy_string(cur_agent->logincallerid, agent_callerid, sizeof(cur_agent->logincallerid));
Kevin P. Fleming
committed
set_agentbycallerid(cur_agent->logincallerid, cur_agent->agent);
Kevin P. Fleming
committed
} else
Kevin P. Fleming
committed
cur_agent->logincallerid[0] = '\0';
if (cur_agent->loginstart == 0)
time(&cur_agent->loginstart);
ast_device_state_changed("Agent/%s", cur_agent->agent);
}
}
AST_LIST_UNLOCK(&agents);
ast_log(LOG_NOTICE, "Agents successfully reloaded from database.\n");
static int agent_devicestate(void *data)
{
struct agent_pvt *p;
char *s;
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_LIST_LOCK(&agents);
AST_LIST_TRAVERSE(&agents, p, list) {
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
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);
}
AST_LIST_UNLOCK(&agents);
struct agent_pvt *find_agent(char *agentid)
{
struct agent_pvt *cur;
AST_LIST_TRAVERSE(&agents, cur, list) {
if (!strcmp(cur->agent, agentid))
break;
}
return cur;
}
static int function_agent(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
Russell Bryant
committed
char *parse;
AST_DECLARE_APP_ARGS(args,
AST_APP_ARG(agentid);
AST_APP_ARG(item);
);
char *tmp;
struct agent_pvt *agent;
buf[0] = '\0';
if (ast_strlen_zero(data)) {
ast_log(LOG_WARNING, "The AGENT function requires an argument - agentid!\n");
}
Russell Bryant
committed
if (!(parse = ast_strdupa(data)))
Russell Bryant
committed
AST_NONSTANDARD_APP_ARGS(args, parse, ':');
if (!args.item)
args.item = "status";
Russell Bryant
committed
if (!(agent = find_agent(args.agentid))) {
ast_log(LOG_WARNING, "Agent '%s' not found!\n", args.agentid);
}
Russell Bryant
committed
if (!strcasecmp(args.item, "status")) {
if (agent->chan || !ast_strlen_zero(agent->loginchan)) {
ast_copy_string(buf, "LOGGEDIN", len);
} else {
ast_copy_string(buf, "LOGGEDOUT", len);
}
Russell Bryant
committed
} else if (!strcasecmp(args.item, "password")) {
ast_copy_string(buf, agent->password, len);
Russell Bryant
committed
} else if (!strcasecmp(args.item, "name")) {
ast_copy_string(buf, agent->name, len);
Russell Bryant
committed
} else if (!strcasecmp(args.item, "mohclass")) {
ast_copy_string(buf, agent->moh, len);
Russell Bryant
committed
} else if (!strcasecmp(args.item, "channel")) {
if (agent->chan) {
ast_copy_string(buf, agent->chan->name, len);
tmp = strrchr(buf, '-');
if (tmp)
*tmp = '\0';
}
Russell Bryant
committed
} else if (!strcasecmp(args.item, "exten")) {
ast_copy_string(buf, agent->loginchan, len);
}
}
struct ast_custom_function agent_function = {
.name = "AGENT",
.synopsis = "Gets information about an Agent",
.syntax = "AGENT(<agentid>[:item])",
.read = function_agent,
.desc = "The valid items to retrieve are:\n"
"- status (default) The status of the agent\n"
" LOGGEDIN | LOGGEDOUT\n"
"- password The password of the agent\n"
"- name The name of the agent\n"
"- mohclass MusicOnHold class\n"
"- exten The callback extension for the Agent (AgentCallbackLogin)\n"
"- channel The name of the active channel for the Agent (AgentLogin)\n"
};
/**
* 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.
*/
/* Make sure we can register our agent channel type */
if (ast_channel_register(&agent_tech)) {
ast_log(LOG_ERROR, "Unable to register channel class 'Agent'\n");
/* Dialplan applications */
ast_register_application(app, login_exec, synopsis, descrip);
ast_register_application(app2, callback_exec, synopsis2, descrip2);
Martin Pycko
committed
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 Commands */
ast_cli_register(&cli_agent_logoff);
/* Dialplan Functions */
ast_custom_function_register(&agent_function);
/* Read in the config */
read_agent_config();
return 0;
}
int reload()
{
read_agent_config();
return 0;
}
int unload_module()
{
struct agent_pvt *p;
/* First, take us out of the channel loop */
/* Unregister dialplan functions */
ast_custom_function_unregister(&agent_function);
/* Unregister CLI commands */
ast_cli_unregister(&cli_agent_logoff);
/* Unregister dialplan applications */
ast_unregister_application(app2);
Martin Pycko
committed
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_LIST_LOCK(&agents)) {
/* Hangup all interfaces if they have an owner */
AST_LIST_TRAVERSE(&agents, p, list) {
if (p->owner)
ast_softhangup(p->owner, AST_SOFTHANGUP_APPUNLOAD);
}
AST_LIST_UNLOCK(&agents);
AST_LIST_HEAD_INIT(&agents);
} 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;