Skip to content
Snippets Groups Projects
chan_agent.c 77.6 KiB
Newer Older
  • Learn to ignore specific revisions
  • 								res = 0;
    
    							sched_yield();
    
    Mark Spencer's avatar
    Mark Spencer committed
    						}
    
    						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);
    
    Mark Spencer's avatar
    Mark Spencer committed
    						}
    
    Mark Spencer's avatar
    Mark Spencer committed
    						p = NULL;
    					}
    
    					res = -1;
    				} else {
    					ast_mutex_unlock(&p->lock);
    					errmsg = "agent-alreadyon";
    					p = NULL;
    				}
    				break;
    
    Mark Spencer's avatar
    Mark Spencer committed
    			}
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    		if (!p)
    
    		if (!res && (max_login_tries==0 || tries < max_login_tries))
    
    Mark Spencer's avatar
    Mark Spencer committed
    			res = ast_app_getdata(chan, errmsg, user, sizeof(user) - 1, 0);
    	}
    		
    
    	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", 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 (!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;
    
    /**
     * 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))
    
    			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();
    
    
    	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;
    
    	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) {
    
    		snprintf(agentvar, sizeof(agentvar), "%s_%s", GETAGENTBYCALLERID, chan->cid.cid_num);
    
    		if ((tmp = pbx_builtin_getvar_helper(NULL, agentvar))) {
    
    			ast_copy_string(agent, tmp, sizeof(agent));
    
    			AST_LIST_LOCK(&agents);
    			AST_LIST_TRAVERSE(&agents, p, list) {
    
    					if (changeoutgoing) snprintf(chan->cdr->channel, sizeof(chan->cdr->channel), "Agent/%s", p->agent);
    
    			
    		} 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)) {
    
    			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);
    
    /**
     * Dump AgentCallbackLogin agents to the database for persistence
    
     */
    static void dump_agents(void)
    {
    	struct agent_pvt *cur_agent = NULL;
    
    	AST_LIST_TRAVERSE(&agents, cur_agent, list) {
    
    		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);
    
    	for (entry = db_tree; entry; entry = entry->next) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		agent_num = entry->key + strlen(pa_family) + 2;
    
    		AST_LIST_TRAVERSE(&agents, cur_agent, list) {
    
    			ast_mutex_lock(&cur_agent->lock);
    
    			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);
    
    			parse = agent_data;
    			agent_chan = strsep(&parse, ";");
    			agent_callerid = strsep(&parse, ";");
    			ast_copy_string(cur_agent->loginchan, agent_chan, sizeof(cur_agent->loginchan));
    
    				ast_copy_string(cur_agent->logincallerid, agent_callerid, sizeof(cur_agent->logincallerid));
    
    				set_agentbycallerid(cur_agent->logincallerid, cur_agent->agent);
    
    			if (cur_agent->loginstart == 0)
    				time(&cur_agent->loginstart);
    			ast_device_state_changed("Agent/%s", cur_agent->agent);	
    		}
    	}
    
    		ast_log(LOG_NOTICE, "Agents successfully reloaded from database.\n");
    
    		ast_db_freetree(db_tree);
    
    Olle Johansson's avatar
    Olle Johansson committed
    /*! \brief 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_LIST_LOCK(&agents);
    	AST_LIST_TRAVERSE(&agents, p, list) {
    
    		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);
    	}
    
    struct agent_pvt *find_agent(char *agentid)
    {
    
    	AST_LIST_TRAVERSE(&agents, cur, list) {
    
    static int function_agent(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
    
    	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");
    
    		return -1;
    
    		return -1;
    
    	AST_NONSTANDARD_APP_ARGS(args, parse, ':');
    	if (!args.item)
    		args.item = "status";
    
    	if (!(agent = find_agent(args.agentid))) {
    		ast_log(LOG_WARNING, "Agent '%s' not found!\n", args.agentid);
    
    		return -1;
    
    		if (agent->chan || !ast_strlen_zero(agent->loginchan)) {
    			ast_copy_string(buf, "LOGGEDIN", len);
    		} else {
    			ast_copy_string(buf, "LOGGEDOUT", len);
    		}
    
    	} else if (!strcasecmp(args.item, "password")) {
    
    		ast_copy_string(buf, agent->password, len);
    
    	} else if (!strcasecmp(args.item, "name")) {
    
    		ast_copy_string(buf, agent->name, len);
    
    	} else if (!strcasecmp(args.item, "mohclass")) {
    
    		ast_copy_string(buf, agent->moh, len);
    
    	} else if (!strcasecmp(args.item, "channel")) {
    
    		if (agent->chan) {
    			ast_copy_string(buf, agent->chan->name, len);
    			tmp = strrchr(buf, '-');
    			if (tmp)
    				*tmp = '\0';
    		} 
    
    	} 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.
     */
    
    Mark Spencer's avatar
    Mark Spencer committed
    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 'Agent'\n");
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return -1;
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	ast_register_application(app, login_exec, synopsis, descrip);
    
    	ast_register_application(app2, callback_exec, synopsis2, descrip2);
    
    	ast_register_application(app3, agentmonitoroutgoing_exec, synopsis3, descrip3);
    
    	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);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	ast_cli_register(&cli_show_agents);
    
    	ast_cli_register(&cli_agent_logoff);
    
    	/* Dialplan Functions */
    	ast_custom_function_register(&agent_function);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Read in the config */
    	read_agent_config();
    
    	if (persistent_agents)
    
    Mark Spencer's avatar
    Mark Spencer committed
    		reload_agents();
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return 0;
    }
    
    int reload()
    {
    	read_agent_config();
    
    	if (persistent_agents)
    
    Mark Spencer's avatar
    Mark Spencer committed
    		reload_agents();
    
    Mark Spencer's avatar
    Mark Spencer committed
    	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 */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	ast_cli_unregister(&cli_show_agents);
    
    	ast_cli_unregister(&cli_agent_logoff);
    
    	/* Unregister dialplan applications */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	ast_unregister_application(app);
    
    	ast_unregister_application(app2);
    
    	ast_manager_unregister("Agents");
    
    	ast_manager_unregister("AgentLogoff");
    	ast_manager_unregister("AgentCallbackLogin");
    
    	ast_channel_unregister(&agent_tech);
    
    	if (!AST_LIST_LOCK(&agents)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/* Hangup all interfaces if they have an owner */
    
    		AST_LIST_TRAVERSE(&agents, p, list) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			if (p->owner)
    				ast_softhangup(p->owner, AST_SOFTHANGUP_APPUNLOAD);
    		}
    
    		AST_LIST_UNLOCK(&agents);
    		AST_LIST_HEAD_INIT(&agents);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	} else {
    		ast_log(LOG_WARNING, "Unable to lock the monitor\n");
    		return -1;
    	}		
    	return 0;
    }
    
    int usecount()
    {
    
    Mark Spencer's avatar
    Mark Spencer committed
    }
    
    char *key()
    {
    	return ASTERISK_GPL_KEY;
    }
    
    char *description()
    {