2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2006, Digium, Inc.
6 * Mark Spencer <markster@digium.com>
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
22 * \brief Implementation of Agents (proxy channel)
24 * \author Mark Spencer <markster@digium.com>
26 * This file is the implementation of Agents modules.
27 * It is a dynamic module that is loaded by Asterisk.
29 * \arg \ref Config_agent
31 * \ingroup channel_drivers
36 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
42 #include <sys/socket.h>
46 #include <netinet/in.h>
47 #include <arpa/inet.h>
48 #include <sys/signal.h>
50 #include "asterisk/lock.h"
51 #include "asterisk/channel.h"
52 #include "asterisk/config.h"
53 #include "asterisk/logger.h"
54 #include "asterisk/module.h"
55 #include "asterisk/pbx.h"
56 #include "asterisk/options.h"
57 #include "asterisk/lock.h"
58 #include "asterisk/sched.h"
59 #include "asterisk/io.h"
60 #include "asterisk/rtp.h"
61 #include "asterisk/acl.h"
62 #include "asterisk/callerid.h"
63 #include "asterisk/file.h"
64 #include "asterisk/cli.h"
65 #include "asterisk/app.h"
66 #include "asterisk/musiconhold.h"
67 #include "asterisk/manager.h"
68 #include "asterisk/features.h"
69 #include "asterisk/utils.h"
70 #include "asterisk/causes.h"
71 #include "asterisk/astdb.h"
72 #include "asterisk/devicestate.h"
73 #include "asterisk/monitor.h"
74 #include "asterisk/stringfields.h"
76 static const char tdesc[] = "Call Agent Proxy Channel";
77 static const char config[] = "agents.conf";
79 static const char app[] = "AgentLogin";
80 static const char app3[] = "AgentMonitorOutgoing";
82 static const char synopsis[] = "Call agent login";
83 static const char synopsis3[] = "Record agent's outgoing call";
85 static const char descrip[] =
86 " AgentLogin([AgentNo][|options]):\n"
87 "Asks the agent to login to the system. Always returns -1. While\n"
88 "logged in, the agent can receive calls and will hear a 'beep'\n"
89 "when a new call comes in. The agent can dump the call by pressing\n"
91 "The option string may contain zero or more of the following characters:\n"
92 " 's' -- silent login - do not announce the login ok segment after agent logged in/off\n";
94 static const char descrip3[] =
95 " AgentMonitorOutgoing([options]):\n"
96 "Tries to figure out the id of the agent who is placing outgoing call based on\n"
97 "comparison of the callerid of the current interface and the global variable \n"
98 "placed by the AgentCallbackLogin application. That's why it should be used only\n"
99 "with the AgentCallbackLogin app. Uses the monitoring functions in chan_agent \n"
100 "instead of Monitor application. That have to be configured in the agents.conf file.\n"
102 "Normally the app returns 0 unless the options are passed. Also if the callerid or\n"
103 "the agentid are not specified it'll look for n+101 priority.\n"
105 " 'd' - make the app return -1 if there is an error condition and there is\n"
106 " no extension n+101\n"
107 " 'c' - change the CDR so that the source of the call is 'Agent/agent_id'\n"
108 " 'n' - don't generate the warnings when there is no callerid or the\n"
109 " agentid is not known.\n"
110 " It's handy if you want to have one context for agent and non-agent calls.\n";
112 static const char mandescr_agents[] =
113 "Description: Will list info about all possible agents.\n"
116 static const char mandescr_agent_logoff[] =
117 "Description: Sets an agent as no longer logged in.\n"
118 "Variables: (Names marked with * are required)\n"
119 " *Agent: Agent ID of the agent to log off\n"
120 " Soft: Set to 'true' to not hangup existing calls\n";
122 static const char mandescr_agent_callback_login[] =
123 "Description: Sets an agent as logged in with callback.\n"
124 "Variables: (Names marked with * are required)\n"
125 " *Agent: Agent ID of the agent to login\n"
126 " *Exten: Extension to use for callback\n"
127 " Context: Context to use for callback\n"
128 " AckCall: Set to 'true' to require an acknowledgement by '#' when agent is called back\n"
129 " WrapupTime: the minimum amount of time after disconnecting before the caller can receive a new call\n";
131 static char moh[80] = "default";
133 #define AST_MAX_AGENT 80 /*!< Agent ID or Password max length */
134 #define AST_MAX_BUF 256
135 #define AST_MAX_FILENAME_LEN 256
137 static const char pa_family[] = "/Agents"; /*!< Persistent Agents astdb family */
138 #define PA_MAX_LEN 2048 /*!< The maximum length of each persistent member agent database entry */
140 static int persistent_agents = 0; /*!< queues.conf [general] option */
141 static void dump_agents(void);
143 static ast_group_t group;
144 static int autologoff;
145 static int wrapuptime;
148 static int multiplelogin = 1;
149 static int autologoffunavail = 0;
151 static int maxlogintries = 3;
152 static char agentgoodbye[AST_MAX_FILENAME_LEN] = "vm-goodbye";
154 static int recordagentcalls = 0;
155 static char recordformat[AST_MAX_BUF] = "";
156 static char recordformatext[AST_MAX_BUF] = "";
157 static char urlprefix[AST_MAX_BUF] = "";
158 static char savecallsin[AST_MAX_BUF] = "";
159 static int updatecdr = 0;
160 static char beep[AST_MAX_BUF] = "beep";
162 #define GETAGENTBYCALLERID "AGENTBYCALLERID"
164 /*! \brief Structure representing an agent. */
166 ast_mutex_t lock; /*!< Channel private lock */
167 int dead; /*!< Poised for destruction? */
168 int pending; /*!< Not a real agent -- just pending a match */
169 int abouttograb; /*!< About to grab */
170 int autologoff; /*!< Auto timeout time */
171 int ackcall; /*!< ackcall */
172 time_t loginstart; /*!< When agent first logged in (0 when logged off) */
173 time_t start; /*!< When call started */
174 struct timeval lastdisc; /*!< When last disconnected */
175 int wrapuptime; /*!< Wrapup time in ms */
176 ast_group_t group; /*!< Group memberships */
177 int acknowledged; /*!< Acknowledged */
178 char moh[80]; /*!< Which music on hold */
179 char agent[AST_MAX_AGENT]; /*!< Agent ID */
180 char password[AST_MAX_AGENT]; /*!< Password for Agent login */
181 char name[AST_MAX_AGENT];
182 ast_mutex_t app_lock; /**< Synchronization between owning applications */
183 volatile pthread_t owning_app; /**< Owning application thread id */
184 volatile int app_sleep_cond; /**< Sleep condition for the login app */
185 struct ast_channel *owner; /**< Agent */
186 char loginchan[80]; /**< channel they logged in from */
187 char logincallerid[80]; /**< Caller ID they had when they logged in */
188 struct ast_channel *chan; /**< Channel we use */
189 AST_LIST_ENTRY(agent_pvt) list; /**< Next Agent in the linked list. */
192 static AST_LIST_HEAD_STATIC(agents, agent_pvt); /*!< Holds the list of agents (loaded form agents.conf). */
194 #define CHECK_FORMATS(ast, p) do { \
196 if (ast->nativeformats != p->chan->nativeformats) { \
198 ast_log(LOG_DEBUG, "Native formats changing from %d to %d\n", ast->nativeformats, p->chan->nativeformats); \
199 /* Native formats changed, reset things */ \
200 ast->nativeformats = p->chan->nativeformats; \
202 ast_log(LOG_DEBUG, "Resetting read to %d and write to %d\n", ast->readformat, ast->writeformat);\
203 ast_set_read_format(ast, ast->readformat); \
204 ast_set_write_format(ast, ast->writeformat); \
206 if (p->chan->readformat != ast->rawreadformat) \
207 ast_set_read_format(p->chan, ast->rawreadformat); \
208 if (p->chan->writeformat != ast->rawwriteformat) \
209 ast_set_write_format(p->chan, ast->rawwriteformat); \
213 /*! \brief Cleanup moves all the relevant FD's from the 2nd to the first, but retains things
214 properly for a timingfd XXX This might need more work if agents were logged in as agents or other
215 totally impractical combinations XXX */
217 #define CLEANUP(ast, p) do { \
220 for (x=0;x<AST_MAX_FDS;x++) {\
221 if (x != AST_TIMING_FD) \
222 ast->fds[x] = p->chan->fds[x]; \
224 ast->fds[AST_AGENT_FD] = p->chan->fds[AST_TIMING_FD]; \
228 /*--- Forward declarations */
229 static struct ast_channel *agent_request(const char *type, int format, void *data, int *cause);
230 static int agent_devicestate(void *data);
231 static void agent_logoff_maintenance(struct agent_pvt *p, char *loginchan, long logintime, const char *uniqueid, char *logcommand);
232 static int agent_digit_begin(struct ast_channel *ast, char digit);
233 static int agent_digit_end(struct ast_channel *ast, char digit);
234 static int agent_call(struct ast_channel *ast, char *dest, int timeout);
235 static int agent_hangup(struct ast_channel *ast);
236 static int agent_answer(struct ast_channel *ast);
237 static struct ast_frame *agent_read(struct ast_channel *ast);
238 static int agent_write(struct ast_channel *ast, struct ast_frame *f);
239 static int agent_sendhtml(struct ast_channel *ast, int subclass, const char *data, int datalen);
240 static int agent_sendtext(struct ast_channel *ast, const char *text);
241 static int agent_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen);
242 static int agent_fixup(struct ast_channel *oldchan, struct ast_channel *newchan);
243 static struct ast_channel *agent_bridgedchannel(struct ast_channel *chan, struct ast_channel *bridge);
244 static void set_agentbycallerid(const char *callerid, const char *agent);
246 /*! \brief Channel interface description for PBX integration */
247 static const struct ast_channel_tech agent_tech = {
249 .description = tdesc,
251 .requester = agent_request,
252 .devicestate = agent_devicestate,
253 .send_digit_begin = agent_digit_begin,
254 .send_digit_end = agent_digit_end,
256 .hangup = agent_hangup,
257 .answer = agent_answer,
259 .write = agent_write,
260 .write_video = agent_write,
261 .send_html = agent_sendhtml,
262 .send_text = agent_sendtext,
263 .exception = agent_read,
264 .indicate = agent_indicate,
265 .fixup = agent_fixup,
266 .bridged_channel = agent_bridgedchannel,
270 * Adds an agent to the global list of agents.
272 * \param agent A string with the username, password and real name of an agent. As defined in agents.conf. Example: "13,169,John Smith"
273 * \param pending If it is pending or not.
274 * @return The just created agent.
275 * \sa agent_pvt, agents.
277 static struct agent_pvt *add_agent(char *agent, int pending)
280 AST_DECLARE_APP_ARGS(args,
282 AST_APP_ARG(password);
285 char *password = NULL;
290 parse = ast_strdupa(agent);
292 /* Extract username (agt), password and name from agent (args). */
293 AST_NONSTANDARD_APP_ARGS(args, parse, ',');
296 ast_log(LOG_WARNING, "A blank agent line!\n");
300 if(ast_strlen_zero(args.agt) ) {
301 ast_log(LOG_WARNING, "An agent line with no agentid!\n");
306 if(!ast_strlen_zero(args.password)) {
307 password = args.password;
308 while (*password && *password < 33) password++;
310 if(!ast_strlen_zero(args.name)) {
312 while (*name && *name < 33) name++;
315 /* Are we searching for the agent here ? To see if it exists already ? */
316 AST_LIST_TRAVERSE(&agents, p, list) {
317 if (!pending && !strcmp(p->agent, agt))
322 if (!(p = ast_calloc(1, sizeof(*p))))
324 ast_copy_string(p->agent, agt, sizeof(p->agent));
325 ast_mutex_init(&p->lock);
326 ast_mutex_init(&p->app_lock);
327 p->owning_app = (pthread_t) -1;
328 p->app_sleep_cond = 1;
330 p->pending = pending;
331 AST_LIST_INSERT_TAIL(&agents, p, list);
334 ast_copy_string(p->password, password ? password : "", sizeof(p->password));
335 ast_copy_string(p->name, name ? name : "", sizeof(p->name));
336 ast_copy_string(p->moh, moh, sizeof(p->moh));
337 p->ackcall = ackcall;
338 p->autologoff = autologoff;
340 /* If someone reduces the wrapuptime and reloads, we want it
341 * to change the wrapuptime immediately on all calls */
342 if (p->wrapuptime > wrapuptime) {
343 struct timeval now = ast_tvnow();
344 /* XXX check what is this exactly */
346 /* We won't be pedantic and check the tv_usec val */
347 if (p->lastdisc.tv_sec > (now.tv_sec + wrapuptime/1000)) {
348 p->lastdisc.tv_sec = now.tv_sec + wrapuptime/1000;
349 p->lastdisc.tv_usec = now.tv_usec;
352 p->wrapuptime = wrapuptime;
362 * Deletes an agent after doing some clean up.
363 * Further documentation: How safe is this function ? What state should the agent be to be cleaned.
364 * \param p Agent to be deleted.
367 static int agent_cleanup(struct agent_pvt *p)
369 struct ast_channel *chan = p->owner;
371 chan->tech_pvt = NULL;
372 p->app_sleep_cond = 1;
373 /* Release ownership of the agent to other threads (presumably running the login app). */
374 ast_mutex_unlock(&p->app_lock);
376 ast_channel_free(chan);
378 ast_mutex_destroy(&p->lock);
379 ast_mutex_destroy(&p->app_lock);
385 static int check_availability(struct agent_pvt *newlyavailable, int needlock);
387 static int agent_answer(struct ast_channel *ast)
389 ast_log(LOG_WARNING, "Huh? Agent is being asked to answer?\n");
393 static int __agent_start_monitoring(struct ast_channel *ast, struct agent_pvt *p, int needlock)
395 char tmp[AST_MAX_BUF],tmp2[AST_MAX_BUF], *pointer;
396 char filename[AST_MAX_BUF];
401 snprintf(filename, sizeof(filename), "agent-%s-%s",p->agent, ast->uniqueid);
402 /* substitute . for - */
403 if ((pointer = strchr(filename, '.')))
405 snprintf(tmp, sizeof(tmp), "%s%s",savecallsin ? savecallsin : "", filename);
406 ast_monitor_start(ast, recordformat, tmp, needlock);
407 ast_monitor_setjoinfiles(ast, 1);
408 snprintf(tmp2, sizeof(tmp2), "%s%s.%s", urlprefix ? urlprefix : "", filename, recordformatext);
410 ast_verbose("name is %s, link is %s\n",tmp, tmp2);
413 ast->cdr = ast_cdr_alloc();
414 ast_cdr_setuserfield(ast, tmp2);
417 ast_log(LOG_ERROR, "Recording already started on that call.\n");
421 static int agent_start_monitoring(struct ast_channel *ast, int needlock)
423 return __agent_start_monitoring(ast, ast->tech_pvt, needlock);
426 static struct ast_frame *agent_read(struct ast_channel *ast)
428 struct agent_pvt *p = ast->tech_pvt;
429 struct ast_frame *f = NULL;
430 static struct ast_frame answer_frame = { AST_FRAME_CONTROL, AST_CONTROL_ANSWER };
432 ast_mutex_lock(&p->lock);
433 CHECK_FORMATS(ast, p);
435 ast_copy_flags(p->chan, ast, AST_FLAG_EXCEPTION);
436 p->chan->fdno = (ast->fdno == AST_AGENT_FD) ? AST_TIMING_FD : ast->fdno;
437 f = ast_read(p->chan);
441 /* If there's a channel, hang it up (if it's on a callback) make it NULL */
443 p->chan->_bridge = NULL;
444 /* Note that we don't hangup if it's not a callback because Asterisk will do it
445 for us when the PBX instance that called login finishes */
446 if (!ast_strlen_zero(p->loginchan)) {
447 if (p->chan && option_debug)
448 ast_log(LOG_DEBUG, "Bridge on '%s' being cleared (2)\n", p->chan->name);
450 status = pbx_builtin_getvar_helper(p->chan, "CHANLOCALSTATUS");
451 if (autologoffunavail && status && !strcasecmp(status, "CHANUNAVAIL")) {
452 long logintime = time(NULL) - p->loginstart;
454 ast_log(LOG_NOTICE, "Agent read: '%s' is not available now, auto logoff\n", p->name);
455 agent_logoff_maintenance(p, p->loginchan, logintime, ast->uniqueid, "Chanunavail");
458 if (p->wrapuptime && p->acknowledged)
459 p->lastdisc = ast_tvadd(ast_tvnow(), ast_samp2tv(p->wrapuptime, 1000));
465 /* if acknowledgement is not required, and the channel is up, we may have missed
466 an AST_CONTROL_ANSWER (if there was one), so mark the call acknowledged anyway */
467 if (!p->ackcall && !p->acknowledged && p->chan && (p->chan->_state == AST_STATE_UP))
469 switch (f->frametype) {
470 case AST_FRAME_CONTROL:
471 if (f->subclass == AST_CONTROL_ANSWER) {
473 if (option_verbose > 2)
474 ast_verbose(VERBOSE_PREFIX_3 "%s answered, waiting for '#' to acknowledge\n", p->chan->name);
475 /* Don't pass answer along */
480 /* Use the builtin answer frame for the
481 recording start check below. */
487 case AST_FRAME_DTMF_BEGIN:
488 case AST_FRAME_DTMF_END:
489 if (!p->acknowledged && (f->subclass == '#')) {
490 if (option_verbose > 2)
491 ast_verbose(VERBOSE_PREFIX_3 "%s acknowledged\n", p->chan->name);
495 } else if (f->subclass == '*' && endcall) {
496 /* terminates call */
501 case AST_FRAME_VOICE:
502 case AST_FRAME_VIDEO:
503 /* don't pass voice or video until the call is acknowledged */
504 if (!p->acknowledged) {
509 /* pass everything else on through */
515 if (p->chan && !p->chan->_bridge) {
516 if (strcasecmp(p->chan->tech->type, "Local")) {
517 p->chan->_bridge = ast;
518 if (p->chan && option_debug)
519 ast_log(LOG_DEBUG, "Bridge on '%s' being set to '%s' (3)\n", p->chan->name, p->chan->_bridge->name);
522 ast_mutex_unlock(&p->lock);
523 if (recordagentcalls && f == &answer_frame)
524 agent_start_monitoring(ast,0);
528 static int agent_sendhtml(struct ast_channel *ast, int subclass, const char *data, int datalen)
530 struct agent_pvt *p = ast->tech_pvt;
532 ast_mutex_lock(&p->lock);
534 res = ast_channel_sendhtml(p->chan, subclass, data, datalen);
535 ast_mutex_unlock(&p->lock);
539 static int agent_sendtext(struct ast_channel *ast, const char *text)
541 struct agent_pvt *p = ast->tech_pvt;
543 ast_mutex_lock(&p->lock);
545 res = ast_sendtext(p->chan, text);
546 ast_mutex_unlock(&p->lock);
550 static int agent_write(struct ast_channel *ast, struct ast_frame *f)
552 struct agent_pvt *p = ast->tech_pvt;
554 CHECK_FORMATS(ast, p);
555 ast_mutex_lock(&p->lock);
559 if ((f->frametype != AST_FRAME_VOICE) ||
560 (f->frametype != AST_FRAME_VIDEO) ||
561 (f->subclass == p->chan->writeformat)) {
562 res = ast_write(p->chan, f);
565 ast_log(LOG_DEBUG, "Dropping one incompatible %s frame on '%s' to '%s'\n",
566 f->frametype == AST_FRAME_VOICE ? "audio" : "video",
567 ast->name, p->chan->name);
572 ast_mutex_unlock(&p->lock);
576 static int agent_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
578 struct agent_pvt *p = newchan->tech_pvt;
579 ast_mutex_lock(&p->lock);
580 if (p->owner != oldchan) {
581 ast_log(LOG_WARNING, "old channel wasn't %p but was %p\n", oldchan, p->owner);
582 ast_mutex_unlock(&p->lock);
586 ast_mutex_unlock(&p->lock);
590 static int agent_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen)
592 struct agent_pvt *p = ast->tech_pvt;
594 ast_mutex_lock(&p->lock);
596 res = ast_indicate_data(p->chan, condition, data, datalen);
599 ast_mutex_unlock(&p->lock);
603 static int agent_digit_begin(struct ast_channel *ast, char digit)
605 struct agent_pvt *p = ast->tech_pvt;
607 ast_mutex_lock(&p->lock);
608 ast_senddigit_begin(p->chan, digit);
609 ast_mutex_unlock(&p->lock);
613 static int agent_digit_end(struct ast_channel *ast, char digit)
615 struct agent_pvt *p = ast->tech_pvt;
617 ast_mutex_lock(&p->lock);
618 ast_senddigit_end(p->chan, digit);
619 ast_mutex_unlock(&p->lock);
623 static int agent_call(struct ast_channel *ast, char *dest, int timeout)
625 struct agent_pvt *p = ast->tech_pvt;
628 ast_mutex_lock(&p->lock);
633 ast_log(LOG_DEBUG, "Pretending to dial on pending agent\n");
634 newstate = AST_STATE_DIALING;
637 ast_log(LOG_NOTICE, "Whoa, they hung up between alloc and call... what are the odds of that?\n");
640 ast_mutex_unlock(&p->lock);
642 ast_setstate(ast, newstate);
644 } else if (!ast_strlen_zero(p->loginchan)) {
646 /* Call on this agent */
647 if (option_verbose > 2)
648 ast_verbose(VERBOSE_PREFIX_3 "outgoing agentcall, to agent '%s', on '%s'\n", p->agent, p->chan->name);
649 ast_set_callerid(p->chan,
650 ast->cid.cid_num, ast->cid.cid_name, NULL);
651 ast_channel_inherit_variables(ast, p->chan);
652 res = ast_call(p->chan, p->loginchan, 0);
654 ast_mutex_unlock(&p->lock);
657 ast_verbose( VERBOSE_PREFIX_3 "agent_call, call to agent '%s' call on '%s'\n", p->agent, p->chan->name);
658 if (option_debug > 2)
659 ast_log(LOG_DEBUG, "Playing beep, lang '%s'\n", p->chan->language);
660 res = ast_streamfile(p->chan, beep, p->chan->language);
661 if (option_debug > 2)
662 ast_log(LOG_DEBUG, "Played beep, result '%d'\n", res);
664 res = ast_waitstream(p->chan, "");
665 if (option_debug > 2)
666 ast_log(LOG_DEBUG, "Waited for stream, result '%d'\n", res);
669 res = ast_set_read_format(p->chan, ast_best_codec(p->chan->nativeformats));
670 if (option_debug > 2)
671 ast_log(LOG_DEBUG, "Set read format, result '%d'\n", res);
673 ast_log(LOG_WARNING, "Unable to set read format to %s\n", ast_getformatname(ast_best_codec(p->chan->nativeformats)));
680 res = ast_set_write_format(p->chan, ast_best_codec(p->chan->nativeformats));
681 if (option_debug > 2)
682 ast_log(LOG_DEBUG, "Set write format, result '%d'\n", res);
684 ast_log(LOG_WARNING, "Unable to set write format to %s\n", ast_getformatname(ast_best_codec(p->chan->nativeformats)));
687 /* Call is immediately up, or might need ack */
689 newstate = AST_STATE_RINGING;
691 newstate = AST_STATE_UP;
692 if (recordagentcalls)
693 agent_start_monitoring(ast, 0);
699 ast_mutex_unlock(&p->lock);
701 ast_setstate(ast, newstate);
705 /*! \brief store/clear the global variable that stores agentid based on the callerid */
706 static void set_agentbycallerid(const char *callerid, const char *agent)
708 char buf[AST_MAX_BUF];
710 /* if there is no Caller ID, nothing to do */
711 if (ast_strlen_zero(callerid))
714 snprintf(buf, sizeof(buf), "%s_%s", GETAGENTBYCALLERID, callerid);
715 pbx_builtin_setvar_helper(NULL, buf, agent);
718 static int agent_hangup(struct ast_channel *ast)
720 struct agent_pvt *p = ast->tech_pvt;
723 ast_mutex_lock(&p->lock);
725 ast->tech_pvt = NULL;
726 p->app_sleep_cond = 1;
729 /* if they really are hung up then set start to 0 so the test
730 * later if we're called on an already downed channel
731 * doesn't cause an agent to be logged out like when
732 * agent_request() is followed immediately by agent_hangup()
733 * as in apps/app_chanisavail.c:chanavail_exec()
737 ast_log(LOG_DEBUG, "Hangup called for state %s\n", ast_state2str(ast->_state));
738 if (p->start && (ast->_state != AST_STATE_UP)) {
739 howlong = time(NULL) - p->start;
741 } else if (ast->_state == AST_STATE_RESERVED)
746 p->chan->_bridge = NULL;
747 /* If they're dead, go ahead and hang up on the agent now */
748 if (!ast_strlen_zero(p->loginchan)) {
749 /* Store last disconnect time */
751 p->lastdisc = ast_tvadd(ast_tvnow(), ast_samp2tv(p->wrapuptime, 1000));
753 p->lastdisc = ast_tv(0,0);
755 status = pbx_builtin_getvar_helper(p->chan, "CHANLOCALSTATUS");
756 if (autologoffunavail && status && !strcasecmp(status, "CHANUNAVAIL")) {
757 long logintime = time(NULL) - p->loginstart;
759 ast_log(LOG_NOTICE, "Agent hangup: '%s' is not available now, auto logoff\n", p->name);
760 agent_logoff_maintenance(p, p->loginchan, logintime, ast->uniqueid, "Chanunavail");
762 /* Recognize the hangup and pass it along immediately */
767 ast_log(LOG_DEBUG, "Hungup, howlong is %d, autologoff is %d\n", howlong, p->autologoff);
768 if (howlong && p->autologoff && (howlong > p->autologoff)) {
769 long logintime = time(NULL) - p->loginstart;
771 ast_log(LOG_NOTICE, "Agent '%s' didn't answer/confirm within %d seconds (waited %d)\n", p->name, p->autologoff, howlong);
772 agent_logoff_maintenance(p, p->loginchan, logintime, ast->uniqueid, "Autologoff");
774 } else if (p->dead) {
775 ast_channel_lock(p->chan);
776 ast_softhangup(p->chan, AST_SOFTHANGUP_EXPLICIT);
777 ast_channel_unlock(p->chan);
778 } else if (p->loginstart) {
779 ast_channel_lock(p->chan);
780 ast_indicate_data(p->chan, AST_CONTROL_HOLD,
782 !ast_strlen_zero(p->moh) ? strlen(p->moh) + 1 : 0);
783 ast_channel_unlock(p->chan);
786 ast_mutex_unlock(&p->lock);
787 /* Only register a device state change if the agent is still logged in */
789 ast_device_state_changed("Agent/%s", p->agent);
792 AST_LIST_LOCK(&agents);
793 AST_LIST_REMOVE(&agents, p, list);
794 AST_LIST_UNLOCK(&agents);
796 if (p->abouttograb) {
797 /* Let the "about to grab" thread know this isn't valid anymore, and let it
800 } else if (p->dead) {
801 ast_mutex_destroy(&p->lock);
802 ast_mutex_destroy(&p->app_lock);
806 /* Not dead -- check availability now */
807 ast_mutex_lock(&p->lock);
808 /* Store last disconnect time */
809 p->lastdisc = ast_tvnow();
810 ast_mutex_unlock(&p->lock);
812 /* Release ownership of the agent to other threads (presumably running the login app). */
813 if (ast_strlen_zero(p->loginchan))
814 ast_mutex_unlock(&p->app_lock);
819 static int agent_cont_sleep( void *data )
824 p = (struct agent_pvt *)data;
826 ast_mutex_lock(&p->lock);
827 res = p->app_sleep_cond;
828 if (p->lastdisc.tv_sec) {
829 if (ast_tvdiff_ms(ast_tvnow(), p->lastdisc) > p->wrapuptime)
832 ast_mutex_unlock(&p->lock);
834 if (option_debug > 4 && !res)
835 ast_log(LOG_DEBUG, "agent_cont_sleep() returning %d\n", res );
840 static int agent_ack_sleep(void *data)
847 /* Wait a second and look for something */
849 p = (struct agent_pvt *) data;
854 to = ast_waitfor(p->chan, to);
859 f = ast_read(p->chan);
862 if (f->frametype == AST_FRAME_DTMF)
867 ast_mutex_lock(&p->lock);
868 if (!p->app_sleep_cond) {
869 ast_mutex_unlock(&p->lock);
871 } else if (res == '#') {
872 ast_mutex_unlock(&p->lock);
875 ast_mutex_unlock(&p->lock);
881 static struct ast_channel *agent_bridgedchannel(struct ast_channel *chan, struct ast_channel *bridge)
883 struct agent_pvt *p = bridge->tech_pvt;
884 struct ast_channel *ret = NULL;
888 ret = bridge->_bridge;
889 else if (chan == bridge->_bridge)
894 ast_log(LOG_DEBUG, "Asked for bridged channel on '%s'/'%s', returning '%s'\n", chan->name, bridge->name, ret ? ret->name : "<none>");
898 /*! \brief Create new agent channel */
899 static struct ast_channel *agent_new(struct agent_pvt *p, int state)
901 struct ast_channel *tmp;
904 ast_log(LOG_WARNING, "No channel? :(\n");
908 tmp = ast_channel_alloc(0);
910 ast_log(LOG_WARNING, "Unable to allocate agent channel structure\n");
914 tmp->tech = &agent_tech;
916 tmp->nativeformats = p->chan->nativeformats;
917 tmp->writeformat = p->chan->writeformat;
918 tmp->rawwriteformat = p->chan->writeformat;
919 tmp->readformat = p->chan->readformat;
920 tmp->rawreadformat = p->chan->readformat;
921 ast_string_field_set(tmp, language, p->chan->language);
922 ast_copy_string(tmp->context, p->chan->context, sizeof(tmp->context));
923 ast_copy_string(tmp->exten, p->chan->exten, sizeof(tmp->exten));
924 /* XXX Is this really all we copy form the originating channel?? */
926 tmp->nativeformats = AST_FORMAT_SLINEAR;
927 tmp->writeformat = AST_FORMAT_SLINEAR;
928 tmp->rawwriteformat = AST_FORMAT_SLINEAR;
929 tmp->readformat = AST_FORMAT_SLINEAR;
930 tmp->rawreadformat = AST_FORMAT_SLINEAR;
933 ast_string_field_build(tmp, name, "Agent/P%s-%d", p->agent, ast_random() & 0xffff);
935 ast_string_field_build(tmp, name, "Agent/%s", p->agent);
936 /* Safe, agentlock already held */
937 ast_setstate(tmp, state);
940 /* XXX: this needs fixing */
942 ast_atomic_fetchadd_int(&__mod_desc->usecnt, +1);
944 ast_update_use_count();
946 /* Wake up and wait for other applications (by definition the login app)
947 * to release this channel). Takes ownership of the agent channel
948 * to this thread only.
949 * For signalling the other thread, ast_queue_frame is used until we
950 * can safely use signals for this purpose. The pselect() needs to be
951 * implemented in the kernel for this.
953 p->app_sleep_cond = 0;
954 if(ast_strlen_zero(p->loginchan) && ast_mutex_trylock(&p->app_lock)) {
956 ast_queue_frame(p->chan, &ast_null_frame);
957 ast_mutex_unlock(&p->lock); /* For other thread to read the condition. */
958 ast_mutex_lock(&p->app_lock);
959 ast_mutex_lock(&p->lock);
961 ast_log(LOG_WARNING, "Agent disconnected while we were connecting the call\n");
963 tmp->tech_pvt = NULL;
964 p->app_sleep_cond = 1;
965 ast_channel_free( tmp );
966 ast_mutex_unlock(&p->lock); /* For other thread to read the condition. */
967 ast_mutex_unlock(&p->app_lock);
970 } else if (!ast_strlen_zero(p->loginchan)) {
972 ast_queue_frame(p->chan, &ast_null_frame);
974 ast_log(LOG_WARNING, "Agent disconnected while we were connecting the call\n");
976 tmp->tech_pvt = NULL;
977 p->app_sleep_cond = 1;
978 ast_channel_free( tmp );
979 ast_mutex_unlock(&p->lock); /* For other thread to read the condition. */
983 ast_indicate(p->chan, AST_CONTROL_UNHOLD);
984 p->owning_app = pthread_self();
985 /* After the above step, there should not be any blockers. */
987 if (ast_test_flag(p->chan, AST_FLAG_BLOCKING)) {
988 ast_log( LOG_ERROR, "A blocker exists after agent channel ownership acquired\n" );
997 * Read configuration data. The file named agents.conf.
999 * \returns Always 0, or so it seems.
1001 static int read_agent_config(void)
1003 struct ast_config *cfg;
1004 struct ast_config *ucfg;
1005 struct ast_variable *v;
1006 struct agent_pvt *p;
1007 const char *general_val;
1008 const char *catname;
1009 const char *hasagent;
1017 cfg = ast_config_load(config);
1019 ast_log(LOG_NOTICE, "No agent configuration found -- agent support disabled\n");
1022 AST_LIST_LOCK(&agents);
1023 AST_LIST_TRAVERSE(&agents, p, list) {
1026 strcpy(moh, "default");
1027 /* set the default recording values */
1028 recordagentcalls = 0;
1029 strcpy(recordformat, "wav");
1030 strcpy(recordformatext, "wav");
1031 urlprefix[0] = '\0';
1032 savecallsin[0] = '\0';
1034 /* Read in [general] section for persistence */
1035 if ((general_val = ast_variable_retrieve(cfg, "general", "persistentagents")))
1036 persistent_agents = ast_true(general_val);
1037 multiplelogin = ast_true(ast_variable_retrieve(cfg, "general", "multiplelogin"));
1039 /* Read in the [agents] section */
1040 v = ast_variable_browse(cfg, "agents");
1042 /* Create the interface list */
1043 if (!strcasecmp(v->name, "agent")) {
1044 add_agent(v->value, 0);
1045 } else if (!strcasecmp(v->name, "group")) {
1046 group = ast_get_group(v->value);
1047 } else if (!strcasecmp(v->name, "autologoff")) {
1048 autologoff = atoi(v->value);
1051 } else if (!strcasecmp(v->name, "ackcall")) {
1052 if (!strcasecmp(v->value, "always"))
1054 else if (ast_true(v->value))
1058 } else if (!strcasecmp(v->name, "endcall")) {
1059 endcall = ast_true(v->value);
1060 } else if (!strcasecmp(v->name, "wrapuptime")) {
1061 wrapuptime = atoi(v->value);
1064 } else if (!strcasecmp(v->name, "maxlogintries") && !ast_strlen_zero(v->value)) {
1065 maxlogintries = atoi(v->value);
1066 if (maxlogintries < 0)
1068 } else if (!strcasecmp(v->name, "goodbye") && !ast_strlen_zero(v->value)) {
1069 strcpy(agentgoodbye,v->value);
1070 } else if (!strcasecmp(v->name, "musiconhold")) {
1071 ast_copy_string(moh, v->value, sizeof(moh));
1072 } else if (!strcasecmp(v->name, "updatecdr")) {
1073 if (ast_true(v->value))
1077 } else if (!strcasecmp(v->name, "autologoffunavail")) {
1078 if (ast_true(v->value))
1079 autologoffunavail = 1;
1081 autologoffunavail = 0;
1082 } else if (!strcasecmp(v->name, "recordagentcalls")) {
1083 recordagentcalls = ast_true(v->value);
1084 } else if (!strcasecmp(v->name, "recordformat")) {
1085 ast_copy_string(recordformat, v->value, sizeof(recordformat));
1086 if (!strcasecmp(v->value, "wav49"))
1087 strcpy(recordformatext, "WAV");
1089 ast_copy_string(recordformatext, v->value, sizeof(recordformatext));
1090 } else if (!strcasecmp(v->name, "urlprefix")) {
1091 ast_copy_string(urlprefix, v->value, sizeof(urlprefix));
1092 if (urlprefix[strlen(urlprefix) - 1] != '/')
1093 strncat(urlprefix, "/", sizeof(urlprefix) - strlen(urlprefix) - 1);
1094 } else if (!strcasecmp(v->name, "savecallsin")) {
1095 if (v->value[0] == '/')
1096 ast_copy_string(savecallsin, v->value, sizeof(savecallsin));
1098 snprintf(savecallsin, sizeof(savecallsin) - 2, "/%s", v->value);
1099 if (savecallsin[strlen(savecallsin) - 1] != '/')
1100 strncat(savecallsin, "/", sizeof(savecallsin) - strlen(savecallsin) - 1);
1101 } else if (!strcasecmp(v->name, "custom_beep")) {
1102 ast_copy_string(beep, v->value, sizeof(beep));
1106 if ((ucfg = ast_config_load("users.conf"))) {
1107 genhasagent = ast_true(ast_variable_retrieve(ucfg, "general", "hasagent"));
1108 catname = ast_category_browse(ucfg, NULL);
1110 if (strcasecmp(catname, "general")) {
1111 hasagent = ast_variable_retrieve(ucfg, catname, "hasagent");
1112 if (ast_true(hasagent) || (!hasagent && genhasagent)) {
1114 const char *fullname = ast_variable_retrieve(ucfg, catname, "fullname");
1115 const char *secret = ast_variable_retrieve(ucfg, catname, "secret");
1120 snprintf(tmp, sizeof(tmp), "%s,%s,%s", catname, secret,fullname);
1124 catname = ast_category_browse(ucfg, catname);
1126 ast_config_destroy(ucfg);
1128 AST_LIST_TRAVERSE_SAFE_BEGIN(&agents, p, list) {
1130 AST_LIST_REMOVE_CURRENT(&agents, list);
1131 /* Destroy if appropriate */
1134 ast_mutex_destroy(&p->lock);
1135 ast_mutex_destroy(&p->app_lock);
1138 /* Cause them to hang up */
1139 ast_softhangup(p->chan, AST_SOFTHANGUP_EXPLICIT);
1144 AST_LIST_TRAVERSE_SAFE_END
1145 AST_LIST_UNLOCK(&agents);
1146 ast_config_destroy(cfg);
1150 static int check_availability(struct agent_pvt *newlyavailable, int needlock)
1152 struct ast_channel *chan=NULL, *parent=NULL;
1153 struct agent_pvt *p;
1157 ast_log(LOG_DEBUG, "Checking availability of '%s'\n", newlyavailable->agent);
1159 AST_LIST_LOCK(&agents);
1160 AST_LIST_TRAVERSE(&agents, p, list) {
1161 if (p == newlyavailable) {
1164 ast_mutex_lock(&p->lock);
1165 if (!p->abouttograb && p->pending && ((p->group && (newlyavailable->group & p->group)) || !strcmp(p->agent, newlyavailable->agent))) {
1167 ast_log(LOG_DEBUG, "Call '%s' looks like a winner for agent '%s'\n", p->owner->name, newlyavailable->agent);
1168 /* We found a pending call, time to merge */
1169 chan = agent_new(newlyavailable, AST_STATE_DOWN);
1172 ast_mutex_unlock(&p->lock);
1175 ast_mutex_unlock(&p->lock);
1178 AST_LIST_UNLOCK(&agents);
1179 if (parent && chan) {
1180 if (newlyavailable->ackcall > 1) {
1181 /* Don't do beep here */
1184 if (option_debug > 2)
1185 ast_log(LOG_DEBUG, "Playing beep, lang '%s'\n", newlyavailable->chan->language);
1186 res = ast_streamfile(newlyavailable->chan, beep, newlyavailable->chan->language);
1187 if (option_debug > 2)
1188 ast_log(LOG_DEBUG, "Played beep, result '%d'\n", res);
1190 res = ast_waitstream(newlyavailable->chan, "");
1192 ast_log(LOG_DEBUG, "Waited for stream, result '%d'\n", res);
1196 /* Note -- parent may have disappeared */
1197 if (p->abouttograb) {
1198 newlyavailable->acknowledged = 1;
1199 /* Safe -- agent lock already held */
1200 ast_setstate(parent, AST_STATE_UP);
1201 ast_setstate(chan, AST_STATE_UP);
1202 ast_copy_string(parent->context, chan->context, sizeof(parent->context));
1203 /* Go ahead and mark the channel as a zombie so that masquerade will
1204 destroy it for us, and we need not call ast_hangup */
1205 ast_mutex_lock(&parent->lock);
1206 ast_set_flag(chan, AST_FLAG_ZOMBIE);
1207 ast_channel_masquerade(parent, chan);
1208 ast_mutex_unlock(&parent->lock);
1212 ast_log(LOG_DEBUG, "Sneaky, parent disappeared in the mean time...\n");
1213 agent_cleanup(newlyavailable);
1217 ast_log(LOG_DEBUG, "Ugh... Agent hung up at exactly the wrong time\n");
1218 agent_cleanup(newlyavailable);
1224 static int check_beep(struct agent_pvt *newlyavailable, int needlock)
1226 struct agent_pvt *p;
1230 ast_log(LOG_DEBUG, "Checking beep availability of '%s'\n", newlyavailable->agent);
1232 AST_LIST_LOCK(&agents);
1233 AST_LIST_TRAVERSE(&agents, p, list) {
1234 if (p == newlyavailable) {
1237 ast_mutex_lock(&p->lock);
1238 if (!p->abouttograb && p->pending && ((p->group && (newlyavailable->group & p->group)) || !strcmp(p->agent, newlyavailable->agent))) {
1240 ast_log(LOG_DEBUG, "Call '%s' looks like a would-be winner for agent '%s'\n", p->owner->name, newlyavailable->agent);
1241 ast_mutex_unlock(&p->lock);
1244 ast_mutex_unlock(&p->lock);
1247 AST_LIST_UNLOCK(&agents);
1249 ast_mutex_unlock(&newlyavailable->lock);
1250 if (option_debug > 2)
1251 ast_log(LOG_DEBUG, "Playing beep, lang '%s'\n", newlyavailable->chan->language);
1252 res = ast_streamfile(newlyavailable->chan, beep, newlyavailable->chan->language);
1253 if (option_debug > 2)
1254 ast_log(LOG_DEBUG, "Played beep, result '%d'\n", res);
1256 res = ast_waitstream(newlyavailable->chan, "");
1258 ast_log(LOG_DEBUG, "Waited for stream, result '%d'\n", res);
1260 ast_mutex_lock(&newlyavailable->lock);
1265 /* return 1 if multiple login is fine, 0 if it is not and we find a match, -1 if multiplelogin is not allowed and we don't find a match. */
1266 static int allow_multiple_login(char *chan, char *context)
1268 struct agent_pvt *p;
1276 snprintf(loginchan, sizeof(loginchan), "%s@%s", chan, S_OR(context, "default"));
1278 AST_LIST_TRAVERSE(&agents, p, list) {
1279 if(!strcasecmp(chan, p->loginchan))
1285 /*! \brief Part of the Asterisk PBX interface */
1286 static struct ast_channel *agent_request(const char *type, int format, void *data, int *cause)
1288 struct agent_pvt *p;
1289 struct ast_channel *chan = NULL;
1291 ast_group_t groupmatch;
1298 if ((s[0] == '@') && (sscanf(s + 1, "%d", &groupoff) == 1)) {
1299 groupmatch = (1 << groupoff);
1300 } else if ((s[0] == ':') && (sscanf(s + 1, "%d", &groupoff) == 1)) {
1301 groupmatch = (1 << groupoff);
1306 /* Check actual logged in agents first */
1307 AST_LIST_LOCK(&agents);
1308 AST_LIST_TRAVERSE(&agents, p, list) {
1309 ast_mutex_lock(&p->lock);
1310 if (!p->pending && ((groupmatch && (p->group & groupmatch)) || !strcmp(data, p->agent)) &&
1311 ast_strlen_zero(p->loginchan)) {
1314 if (!p->lastdisc.tv_sec) {
1315 /* Agent must be registered, but not have any active call, and not be in a waiting state */
1316 if (!p->owner && p->chan) {
1318 chan = agent_new(p, AST_STATE_DOWN);
1321 ast_mutex_unlock(&p->lock);
1326 ast_mutex_unlock(&p->lock);
1329 AST_LIST_TRAVERSE(&agents, p, list) {
1330 ast_mutex_lock(&p->lock);
1331 if (!p->pending && ((groupmatch && (p->group & groupmatch)) || !strcmp(data, p->agent))) {
1332 if (p->chan || !ast_strlen_zero(p->loginchan))
1336 ast_log(LOG_NOTICE, "Time now: %ld, Time of lastdisc: %ld\n", tv.tv_sec, p->lastdisc.tv_sec);
1338 if (!p->lastdisc.tv_sec || (tv.tv_sec > p->lastdisc.tv_sec)) {
1339 p->lastdisc = ast_tv(0, 0);
1340 /* Agent must be registered, but not have any active call, and not be in a waiting state */
1341 if (!p->owner && p->chan) {
1342 /* Could still get a fixed agent */
1343 chan = agent_new(p, AST_STATE_DOWN);
1344 } else if (!p->owner && !ast_strlen_zero(p->loginchan)) {
1345 /* Adjustable agent */
1346 p->chan = ast_request("Local", format, p->loginchan, cause);
1348 chan = agent_new(p, AST_STATE_DOWN);
1351 ast_mutex_unlock(&p->lock);
1356 ast_mutex_unlock(&p->lock);
1360 if (!chan && waitforagent) {
1361 /* No agent available -- but we're requesting to wait for one.
1362 Allocate a place holder */
1365 ast_log(LOG_DEBUG, "Creating place holder for '%s'\n", s);
1366 p = add_agent(data, 1);
1367 p->group = groupmatch;
1368 chan = agent_new(p, AST_STATE_DOWN);
1370 ast_log(LOG_WARNING, "Weird... Fix this to drop the unused pending agent\n");
1373 ast_log(LOG_DEBUG, "Not creating place holder for '%s' since nobody logged in\n", s);
1376 *cause = hasagent ? AST_CAUSE_BUSY : AST_CAUSE_UNREGISTERED;
1377 AST_LIST_UNLOCK(&agents);
1381 static force_inline int powerof(unsigned int d)
1392 * Lists agents and their status to the Manager API.
1393 * It is registered on load_module() and it gets called by the manager backend.
1397 * \sa action_agent_logoff(), action_agent_callback_login(), load_module().
1399 static int action_agents(struct mansession *s, struct message *m)
1401 char *id = astman_get_header(m,"ActionID");
1402 char idText[256] = "";
1404 struct agent_pvt *p;
1405 char *username = NULL;
1406 char *loginChan = NULL;
1407 char *talkingtoChan = NULL;
1408 char *status = NULL;
1410 if (!ast_strlen_zero(id))
1411 snprintf(idText, sizeof(idText) ,"ActionID: %s\r\n", id);
1412 astman_send_ack(s, m, "Agents will follow");
1413 AST_LIST_LOCK(&agents);
1414 AST_LIST_TRAVERSE(&agents, p, list) {
1415 ast_mutex_lock(&p->lock);
1418 AGENT_LOGGEDOFF - Agent isn't logged in
1419 AGENT_IDLE - Agent is logged in, and waiting for call
1420 AGENT_ONCALL - Agent is logged in, and on a call
1421 AGENT_UNKNOWN - Don't know anything about agent. Shouldn't ever get this. */
1423 username = S_OR(p->name, "None");
1425 /* Set a default status. It 'should' get changed. */
1426 status = "AGENT_UNKNOWN";
1428 if (!ast_strlen_zero(p->loginchan) && !p->chan) {
1429 loginChan = p->loginchan;
1430 talkingtoChan = "n/a";
1431 status = "AGENT_IDLE";
1432 if (p->acknowledged) {
1433 snprintf(chanbuf, sizeof(chanbuf), " %s (Confirmed)", p->loginchan);
1434 loginChan = chanbuf;
1436 } else if (p->chan) {
1437 loginChan = ast_strdupa(p->chan->name);
1438 if (p->owner && p->owner->_bridge) {
1439 talkingtoChan = p->chan->cid.cid_num;
1440 status = "AGENT_ONCALL";
1442 talkingtoChan = "n/a";
1443 status = "AGENT_IDLE";
1447 talkingtoChan = "n/a";
1448 status = "AGENT_LOGGEDOFF";
1451 astman_append(s, "Event: Agents\r\n"
1455 "LoggedInChan: %s\r\n"
1456 "LoggedInTime: %d\r\n"
1460 p->agent, username, status, loginChan, (int)p->loginstart, talkingtoChan, idText);
1461 ast_mutex_unlock(&p->lock);
1463 AST_LIST_UNLOCK(&agents);
1464 astman_append(s, "Event: AgentsComplete\r\n"
1470 static void agent_logoff_maintenance(struct agent_pvt *p, char *loginchan, long logintime, const char *uniqueid, char *logcommand)
1473 char agent[AST_MAX_AGENT];
1475 if (!ast_strlen_zero(logcommand))
1478 tmp = ast_strdupa("");
1480 snprintf(agent, sizeof(agent), "Agent/%s", p->agent);
1482 if (!ast_strlen_zero(uniqueid)) {
1483 manager_event(EVENT_FLAG_AGENT, "Agentcallbacklogoff",
1487 "Logintime: %ld\r\n"
1489 p->agent, tmp, loginchan, logintime, uniqueid);
1491 manager_event(EVENT_FLAG_AGENT, "Agentcallbacklogoff",
1495 "Logintime: %ld\r\n",
1496 p->agent, tmp, loginchan, logintime);
1499 ast_queue_log("NONE", ast_strlen_zero(uniqueid) ? "NONE" : uniqueid, agent, "AGENTCALLBACKLOGOFF", "%s|%ld|%s", loginchan, logintime, tmp);
1500 set_agentbycallerid(p->logincallerid, NULL);
1501 p->loginchan[0] ='\0';
1502 p->logincallerid[0] = '\0';
1503 ast_device_state_changed("Agent/%s", p->agent);
1504 if (persistent_agents)
1509 static int agent_logoff(char *agent, int soft)
1511 struct agent_pvt *p;
1513 int ret = -1; /* Return -1 if no agent if found */
1515 AST_LIST_TRAVERSE(&agents, p, list) {
1516 if (!strcasecmp(p->agent, agent)) {
1519 ast_softhangup(p->owner, AST_SOFTHANGUP_EXPLICIT);
1521 ast_softhangup(p->chan, AST_SOFTHANGUP_EXPLICIT);
1523 ret = 0; /* found an agent => return 0 */
1524 logintime = time(NULL) - p->loginstart;
1526 agent_logoff_maintenance(p, p->loginchan, logintime, NULL, "CommandLogoff");
1534 static int agent_logoff_cmd(int fd, int argc, char **argv)
1539 if (argc < 3 || argc > 4)
1540 return RESULT_SHOWUSAGE;
1541 if (argc == 4 && strcasecmp(argv[3], "soft"))
1542 return RESULT_SHOWUSAGE;
1544 agent = argv[2] + 6;
1545 ret = agent_logoff(agent, argc == 4);
1547 ast_cli(fd, "Logging out %s\n", agent);
1549 return RESULT_SUCCESS;
1553 * Sets an agent as no longer logged in in the Manager API.
1554 * It is registered on load_module() and it gets called by the manager backend.
1558 * \sa action_agents(), action_agent_callback_login(), load_module().
1560 static int action_agent_logoff(struct mansession *s, struct message *m)
1562 char *agent = astman_get_header(m, "Agent");
1563 char *soft_s = astman_get_header(m, "Soft"); /* "true" is don't hangup */
1565 int ret; /* return value of agent_logoff */
1567 if (ast_strlen_zero(agent)) {
1568 astman_send_error(s, m, "No agent specified");
1572 soft = ast_true(soft_s) ? 1 : 0;
1573 ret = agent_logoff(agent, soft);
1575 astman_send_ack(s, m, "Agent logged out");
1577 astman_send_error(s, m, "No such agent");
1582 static char *complete_agent_logoff_cmd(const char *line, const char *word, int pos, int state)
1585 struct agent_pvt *p;
1586 char name[AST_MAX_AGENT];
1587 int which = 0, len = strlen(word);
1589 AST_LIST_TRAVERSE(&agents, p, list) {
1590 snprintf(name, sizeof(name), "Agent/%s", p->agent);
1591 if (!strncasecmp(word, name, len) && ++which > state)
1592 return ast_strdup(name);
1594 } else if (pos == 3 && state == 0)
1595 return ast_strdup("soft");
1601 * Show agents in cli.
1603 static int agents_show(int fd, int argc, char **argv)
1605 struct agent_pvt *p;
1606 char username[AST_MAX_BUF];
1607 char location[AST_MAX_BUF] = "";
1608 char talkingto[AST_MAX_BUF] = "";
1609 char moh[AST_MAX_BUF];
1610 int count_agents = 0; /*!< Number of agents configured */
1611 int online_agents = 0; /*!< Number of online agents */
1612 int offline_agents = 0; /*!< Number of offline agents */
1614 return RESULT_SHOWUSAGE;
1615 AST_LIST_LOCK(&agents);
1616 AST_LIST_TRAVERSE(&agents, p, list) {
1617 ast_mutex_lock(&p->lock);
1620 ast_cli(fd, "-- Pending call to group %d\n", powerof(p->group));
1622 ast_cli(fd, "-- Pending call to agent %s\n", p->agent);
1624 if (!ast_strlen_zero(p->name))
1625 snprintf(username, sizeof(username), "(%s) ", p->name);
1629 snprintf(location, sizeof(location), "logged in on %s", p->chan->name);
1630 if (p->owner && ast_bridged_channel(p->owner))
1631 snprintf(talkingto, sizeof(talkingto), " talking to %s", ast_bridged_channel(p->owner)->name);
1633 strcpy(talkingto, " is idle");
1635 } else if (!ast_strlen_zero(p->loginchan)) {
1636 if (ast_tvdiff_ms(ast_tvnow(), p->lastdisc) > 0 || !(p->lastdisc.tv_sec))
1637 snprintf(location, sizeof(location) - 20, "available at '%s'", p->loginchan);
1639 snprintf(location, sizeof(location) - 20, "wrapping up at '%s'", p->loginchan);
1640 talkingto[0] = '\0';
1642 if (p->acknowledged)
1643 strncat(location, " (Confirmed)", sizeof(location) - strlen(location) - 1);
1645 strcpy(location, "not logged in");
1646 talkingto[0] = '\0';
1649 if (!ast_strlen_zero(p->moh))
1650 snprintf(moh, sizeof(moh), " (musiconhold is '%s')", p->moh);
1651 ast_cli(fd, "%-12.12s %s%s%s%s\n", p->agent,
1652 username, location, talkingto, moh);
1655 ast_mutex_unlock(&p->lock);
1657 AST_LIST_UNLOCK(&agents);
1658 if ( !count_agents )
1659 ast_cli(fd, "No Agents are configured in %s\n",config);
1661 ast_cli(fd, "%d agents configured [%d online , %d offline]\n",count_agents, online_agents, offline_agents);
1664 return RESULT_SUCCESS;
1668 static int agents_show_online(int fd, int argc, char **argv)
1670 struct agent_pvt *p;
1671 char username[AST_MAX_BUF];
1672 char location[AST_MAX_BUF] = "";
1673 char talkingto[AST_MAX_BUF] = "";
1674 char moh[AST_MAX_BUF];
1675 int count_agents = 0; /* Number of agents configured */
1676 int online_agents = 0; /* Number of online agents */
1677 int agent_status = 0; /* 0 means offline, 1 means online */
1679 return RESULT_SHOWUSAGE;
1680 AST_LIST_LOCK(&agents);
1681 AST_LIST_TRAVERSE(&agents, p, list) {
1682 agent_status = 0; /* reset it to offline */
1683 ast_mutex_lock(&p->lock);
1684 if (!ast_strlen_zero(p->name))
1685 snprintf(username, sizeof(username), "(%s) ", p->name);
1689 snprintf(location, sizeof(location), "logged in on %s", p->chan->name);
1690 if (p->owner && ast_bridged_channel(p->owner))
1691 snprintf(talkingto, sizeof(talkingto), " talking to %s", ast_bridged_channel(p->owner)->name);
1693 strcpy(talkingto, " is idle");
1696 } else if (!ast_strlen_zero(p->loginchan)) {
1697 snprintf(location, sizeof(location) - 20, "available at '%s'", p->loginchan);
1698 talkingto[0] = '\0';
1701 if (p->acknowledged)
1702 strncat(location, " (Confirmed)", sizeof(location) - strlen(location) - 1);
1704 if (!ast_strlen_zero(p->moh))
1705 snprintf(moh, sizeof(moh), " (musiconhold is '%s')", p->moh);
1707 ast_cli(fd, "%-12.12s %s%s%s%s\n", p->agent, username, location, talkingto, moh);
1709 ast_mutex_unlock(&p->lock);
1711 AST_LIST_UNLOCK(&agents);
1713 ast_cli(fd, "No Agents are configured in %s\n", config);
1715 ast_cli(fd, "%d agents online\n", online_agents);
1717 return RESULT_SUCCESS;
1722 static char show_agents_usage[] =
1723 "Usage: agent list\n"
1724 " Provides summary information on agents.\n";
1726 static char show_agents_online_usage[] =
1727 "Usage: agent list online\n"
1728 " Provides a list of all online agents.\n";
1730 static char agent_logoff_usage[] =
1731 "Usage: agent logoff <channel> [soft]\n"
1732 " Sets an agent as no longer logged in.\n"
1733 " If 'soft' is specified, do not hangup existing calls.\n";
1735 static struct ast_cli_entry cli_agents[] = {
1736 { { "agent", "list", NULL },
1737 agents_show, "Show status of agents",
1738 show_agents_usage },
1740 { { "agent", "list", "online" },
1741 agents_show_online, "Show all online agents",
1742 show_agents_online_usage },
1744 { { "agent", "logoff", NULL },
1745 agent_logoff_cmd, "Sets an agent offline",
1746 agent_logoff_usage, complete_agent_logoff_cmd },
1750 * \brief Log in agent application.
1754 * \param callbackmode non-zero for AgentCallbackLogin
1756 static int __login_exec(struct ast_channel *chan, void *data, int callbackmode)
1760 int max_login_tries = maxlogintries;
1761 struct agent_pvt *p;
1762 struct ast_module_user *u;
1763 int login_state = 0;
1764 char user[AST_MAX_AGENT] = "";
1765 char pass[AST_MAX_AGENT];
1766 char agent[AST_MAX_AGENT] = "";
1767 char xpass[AST_MAX_AGENT] = "";
1770 AST_DECLARE_APP_ARGS(args,
1771 AST_APP_ARG(agent_id);
1772 AST_APP_ARG(options);
1773 AST_APP_ARG(extension);
1775 const char *tmpoptions = NULL;
1776 char *context = NULL;
1777 int play_announcement = 1;
1778 char agent_goodbye[AST_MAX_FILENAME_LEN];
1779 int update_cdr = updatecdr;
1780 char *filename = "agent-loginok";
1781 char tmpchan[AST_MAX_BUF] = "";
1783 u = ast_module_user_add(chan);
1785 parse = ast_strdupa(data);
1787 AST_STANDARD_APP_ARGS(args, parse);
1789 ast_copy_string(agent_goodbye, agentgoodbye, sizeof(agent_goodbye));
1791 /* Set Channel Specific Login Overrides */
1792 if (pbx_builtin_getvar_helper(chan, "AGENTLMAXLOGINTRIES") && strlen(pbx_builtin_getvar_helper(chan, "AGENTLMAXLOGINTRIES"))) {
1793 max_login_tries = atoi(pbx_builtin_getvar_helper(chan, "AGENTMAXLOGINTRIES"));
1794 if (max_login_tries < 0)
1795 max_login_tries = 0;
1796 tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTMAXLOGINTRIES");
1797 if (option_verbose > 2)
1798 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);
1800 if (pbx_builtin_getvar_helper(chan, "AGENTUPDATECDR") && !ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENTUPDATECDR"))) {
1801 if (ast_true(pbx_builtin_getvar_helper(chan, "AGENTUPDATECDR")))
1805 tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTUPDATECDR");
1806 if (option_verbose > 2)
1807 ast_verbose(VERBOSE_PREFIX_3 "Saw variable AGENTUPDATECDR=%s, setting update_cdr to: %d on Channel '%s'.\n",tmpoptions,update_cdr,chan->name);
1809 if (pbx_builtin_getvar_helper(chan, "AGENTGOODBYE") && !ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENTGOODBYE"))) {
1810 strcpy(agent_goodbye, pbx_builtin_getvar_helper(chan, "AGENTGOODBYE"));
1811 tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTGOODBYE");
1812 if (option_verbose > 2)
1813 ast_verbose(VERBOSE_PREFIX_3 "Saw variable AGENTGOODBYE=%s, setting agent_goodbye to: %s on Channel '%s'.\n",tmpoptions,agent_goodbye,chan->name);
1815 /* End Channel Specific Login Overrides */
1817 if (callbackmode && args.extension) {
1818 parse = args.extension;
1819 args.extension = strsep(&parse, "@");
1823 if (!ast_strlen_zero(args.options)) {
1824 if (strchr(args.options, 's')) {
1825 play_announcement = 0;
1829 if (chan->_state != AST_STATE_UP)
1830 res = ast_answer(chan);
1832 if (!ast_strlen_zero(args.agent_id))
1833 ast_copy_string(user, args.agent_id, AST_MAX_AGENT);
1835 res = ast_app_getdata(chan, "agent-user", user, sizeof(user) - 1, 0);
1837 while (!res && (max_login_tries==0 || tries < max_login_tries)) {
1839 /* Check for password */
1840 AST_LIST_LOCK(&agents);
1841 AST_LIST_TRAVERSE(&agents, p, list) {
1842 if (!strcmp(p->agent, user) && !p->pending)
1843 ast_copy_string(xpass, p->password, sizeof(xpass));
1845 AST_LIST_UNLOCK(&agents);
1847 if (!ast_strlen_zero(xpass))
1848 res = ast_app_getdata(chan, "agent-pass", pass, sizeof(pass) - 1, 0);
1852 errmsg = "agent-incorrect";
1855 ast_log(LOG_NOTICE, "user: %s, pass: %s\n", user, pass);
1858 /* Check again for accuracy */
1859 AST_LIST_LOCK(&agents);
1860 AST_LIST_TRAVERSE(&agents, p, list) {
1861 ast_mutex_lock(&p->lock);
1862 if (!strcmp(p->agent, user) &&
1863 !strcmp(p->password, pass) && !p->pending) {
1864 login_state = 1; /* Successful Login */
1866 /* Ensure we can't be gotten until we're done */
1867 gettimeofday(&p->lastdisc, NULL);
1868 p->lastdisc.tv_sec++;
1870 /* Set Channel Specific Agent Overrides */
1871 if (pbx_builtin_getvar_helper(chan, "AGENTACKCALL") && strlen(pbx_builtin_getvar_helper(chan, "AGENTACKCALL"))) {
1872 if (!strcasecmp(pbx_builtin_getvar_helper(chan, "AGENTACKCALL"), "always"))
1874 else if (ast_true(pbx_builtin_getvar_helper(chan, "AGENTACKCALL")))
1878 tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTACKCALL");
1879 if (option_verbose > 2)
1880 ast_verbose(VERBOSE_PREFIX_3 "Saw variable AGENTACKCALL=%s, setting ackcall to: %d for Agent '%s'.\n",tmpoptions,p->ackcall,p->agent);
1882 if (pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF") && strlen(pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF"))) {
1883 p->autologoff = atoi(pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF"));
1884 if (p->autologoff < 0)
1886 tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF");
1887 if (option_verbose > 2)
1888 ast_verbose(VERBOSE_PREFIX_3 "Saw variable AGENTAUTOLOGOFF=%s, setting autologff to: %d for Agent '%s'.\n",tmpoptions,p->autologoff,p->agent);
1890 if (pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME") && strlen(pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME"))) {
1891 p->wrapuptime = atoi(pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME"));
1892 if (p->wrapuptime < 0)
1894 tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME");
1895 if (option_verbose > 2)
1896 ast_verbose(VERBOSE_PREFIX_3 "Saw variable AGENTWRAPUPTIME=%s, setting wrapuptime to: %d for Agent '%s'.\n",tmpoptions,p->wrapuptime,p->agent);
1898 /* End Channel Specific Agent Overrides */
1900 char last_loginchan[80] = "";
1902 snprintf(agent, sizeof(agent), "Agent/%s", p->agent);
1906 /* Retrieve login chan */
1908 if (!ast_strlen_zero(args.extension)) {
1909 ast_copy_string(tmpchan, args.extension, sizeof(tmpchan));
1912 res = ast_app_getdata(chan, "agent-newlocation", tmpchan+pos, sizeof(tmpchan) - 2, 0);
1913 if (ast_strlen_zero(tmpchan) )
1915 if(ast_exists_extension(chan, S_OR(context,"default"), tmpchan,1, NULL) ) {
1916 if(!allow_multiple_login(tmpchan,context) ) {
1917 args.extension = NULL;
1922 if (args.extension) {
1923 ast_log(LOG_WARNING, "Extension '%s' is not valid for automatic login of agent '%s'\n", args.extension, p->agent);
1924 args.extension = NULL;
1927 ast_log(LOG_WARNING, "Extension '%s@%s' is not valid for automatic login of agent '%s'\n", tmpchan, S_OR(context, "default"), p->agent);
1928 res = ast_streamfile(chan, "invalid", chan->language);
1930 res = ast_waitstream(chan, AST_DIGIT_ANY);
1941 args.extension = tmpchan;
1943 set_agentbycallerid(p->logincallerid, NULL);
1944 if (!ast_strlen_zero(context) && !ast_strlen_zero(tmpchan))
1945 snprintf(p->loginchan, sizeof(p->loginchan), "%s@%s", tmpchan, context);
1947 ast_copy_string(last_loginchan, p->loginchan, sizeof(last_loginchan));
1948 ast_copy_string(p->loginchan, tmpchan, sizeof(p->loginchan));
1950 p->acknowledged = 0;
1951 if (ast_strlen_zero(p->loginchan)) {
1953 filename = "agent-loggedoff";
1955 if (chan->cid.cid_num) {
1956 ast_copy_string(p->logincallerid, chan->cid.cid_num, sizeof(p->logincallerid));
1957 set_agentbycallerid(p->logincallerid, p->agent);
1959 p->logincallerid[0] = '\0';
1962 if(update_cdr && chan->cdr)
1963 snprintf(chan->cdr->channel, sizeof(chan->cdr->channel), "Agent/%s", p->agent);
1967 p->loginchan[0] = '\0';
1968 p->logincallerid[0] = '\0';
1969 p->acknowledged = 0;
1971 ast_mutex_unlock(&p->lock);
1972 AST_LIST_UNLOCK(&agents);
1973 if( !res && play_announcement==1 )
1974 res = ast_streamfile(chan, filename, chan->language);
1976 ast_waitstream(chan, "");
1977 AST_LIST_LOCK(&agents);
1978 ast_mutex_lock(&p->lock);
1980 res = ast_set_read_format(chan, ast_best_codec(chan->nativeformats));
1982 ast_log(LOG_WARNING, "Unable to set read format to %d\n", ast_best_codec(chan->nativeformats));
1985 res = ast_set_write_format(chan, ast_best_codec(chan->nativeformats));
1987 ast_log(LOG_WARNING, "Unable to set write format to %d\n", ast_best_codec(chan->nativeformats));
1989 /* Check once more just in case */
1992 if (callbackmode && !res) {
1993 /* Just say goodbye and be done with it */
1994 if (!ast_strlen_zero(p->loginchan)) {
1995 if (p->loginstart == 0)
1996 time(&p->loginstart);
1997 manager_event(EVENT_FLAG_AGENT, "Agentcallbacklogin",
2001 p->agent, p->loginchan, chan->uniqueid);
2002 ast_queue_log("NONE", chan->uniqueid, agent, "AGENTCALLBACKLOGIN", "%s", p->loginchan);
2003 if (option_verbose > 1)
2004 ast_verbose(VERBOSE_PREFIX_2 "Callback Agent '%s' logged in on %s\n", p->agent, p->loginchan);
2005 ast_device_state_changed("Agent/%s", p->agent);
2006 if (persistent_agents)
2009 logintime = time(NULL) - p->loginstart;
2012 agent_logoff_maintenance(p, last_loginchan, logintime, chan->uniqueid, NULL);
2013 if (option_verbose > 1)
2014 ast_verbose(VERBOSE_PREFIX_2 "Callback Agent '%s' logged out\n", p->agent);
2016 AST_LIST_UNLOCK(&agents);
2018 res = ast_safe_sleep(chan, 500);
2019 ast_mutex_unlock(&p->lock);
2021 ast_indicate_data(chan, AST_CONTROL_HOLD,
2023 !ast_strlen_zero(p->moh) ? strlen(p->moh) + 1 : 0);
2024 if (p->loginstart == 0)
2025 time(&p->loginstart);
2026 manager_event(EVENT_FLAG_AGENT, "Agentlogin",
2030 p->agent, chan->name, chan->uniqueid);
2031 if (update_cdr && chan->cdr)
2032 snprintf(chan->cdr->channel, sizeof(chan->cdr->channel), "Agent/%s", p->agent);
2033 ast_queue_log("NONE", chan->uniqueid, agent, "AGENTLOGIN", "%s", chan->name);
2034 if (option_verbose > 1)
2035 ast_verbose(VERBOSE_PREFIX_2 "Agent '%s' logged in (format %s/%s)\n", p->agent,
2036 ast_getformatname(chan->readformat), ast_getformatname(chan->writeformat));
2037 /* Login this channel and wait for it to go away */
2042 check_availability(p, 0);
2043 ast_mutex_unlock(&p->lock);
2044 AST_LIST_UNLOCK(&agents);
2045 ast_device_state_changed("Agent/%s", p->agent);
2047 ast_mutex_lock(&p->lock);
2048 if (p->chan != chan)
2050 ast_mutex_unlock(&p->lock);
2051 /* Yield here so other interested threads can kick in. */
2056 AST_LIST_LOCK(&agents);
2057 ast_mutex_lock(&p->lock);
2058 if (p->lastdisc.tv_sec) {
2059 if (ast_tvdiff_ms(ast_tvnow(), p->lastdisc) > p->wrapuptime) {
2061 ast_log(LOG_DEBUG, "Wrapup time for %s expired!\n", p->agent);
2062 p->lastdisc = ast_tv(0, 0);
2066 check_availability(p, 0);
2069 ast_mutex_unlock(&p->lock);
2070 AST_LIST_UNLOCK(&agents);
2071 /* Synchronize channel ownership between call to agent and itself. */
2072 ast_mutex_lock( &p->app_lock );
2073 ast_mutex_lock(&p->lock);
2074 p->owning_app = pthread_self();
2075 ast_mutex_unlock(&p->lock);
2077 res = agent_ack_sleep(p);
2079 res = ast_safe_sleep_conditional( chan, 1000, agent_cont_sleep, p );
2080 ast_mutex_unlock( &p->app_lock );
2081 if ((p->ackcall > 1) && (res == 1)) {
2082 AST_LIST_LOCK(&agents);
2083 ast_mutex_lock(&p->lock);
2084 check_availability(p, 0);
2085 ast_mutex_unlock(&p->lock);
2086 AST_LIST_UNLOCK(&agents);
2091 ast_mutex_lock(&p->lock);
2092 if (res && p->owner)
2093 ast_log(LOG_WARNING, "Huh? We broke out when there was still an owner?\n");
2094 /* Log us off if appropriate */
2095 if (p->chan == chan)
2097 p->acknowledged = 0;
2098 logintime = time(NULL) - p->loginstart;
2100 ast_mutex_unlock(&p->lock);
2101 manager_event(EVENT_FLAG_AGENT, "Agentlogoff",
2103 "Logintime: %ld\r\n"
2105 p->agent, logintime, chan->uniqueid);
2106 ast_queue_log("NONE", chan->uniqueid, agent, "AGENTLOGOFF", "%s|%ld", chan->name, logintime);
2107 if (option_verbose > 1)
2108 ast_verbose(VERBOSE_PREFIX_2 "Agent '%s' logged out\n", p->agent);
2109 /* If there is no owner, go ahead and kill it now */
2110 ast_device_state_changed("Agent/%s", p->agent);
2111 if (p->dead && !p->owner) {
2112 ast_mutex_destroy(&p->lock);
2113 ast_mutex_destroy(&p->app_lock);
2118 ast_mutex_unlock(&p->lock);
2123 ast_mutex_unlock(&p->lock);
2124 errmsg = "agent-alreadyon";
2129 ast_mutex_unlock(&p->lock);
2132 AST_LIST_UNLOCK(&agents);
2134 if (!res && (max_login_tries==0 || tries < max_login_tries))
2135 res = ast_app_getdata(chan, errmsg, user, sizeof(user) - 1, 0);
2139 res = ast_safe_sleep(chan, 500);
2141 /* AgentLogin() exit */
2142 if (!callbackmode) {
2143 ast_module_user_remove(u);
2145 } else { /* AgentCallbackLogin() exit*/
2147 if (login_state > 0) {
2148 pbx_builtin_setvar_helper(chan, "AGENTNUMBER", user);
2149 if (login_state==1) {
2150 pbx_builtin_setvar_helper(chan, "AGENTSTATUS", "on");
2151 pbx_builtin_setvar_helper(chan, "AGENTEXTEN", args.extension);
2153 pbx_builtin_setvar_helper(chan, "AGENTSTATUS", "off");
2155 pbx_builtin_setvar_helper(chan, "AGENTSTATUS", "fail");
2157 if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 1, chan->cid.cid_num)) {
2158 ast_module_user_remove(u);
2161 /* Do we need to play agent-goodbye now that we will be hanging up? */
2162 if (play_announcement) {
2164 res = ast_safe_sleep(chan, 1000);
2165 res = ast_streamfile(chan, agent_goodbye, chan->language);
2167 res = ast_waitstream(chan, "");
2169 res = ast_safe_sleep(chan, 1000);
2173 ast_module_user_remove(u);
2175 /* We should never get here if next priority exists when in callbackmode */
2180 * Called by the AgentLogin application (from the dial plan).
2185 * \sa callback_login_exec(), agentmonitoroutgoing_exec(), load_module().
2187 static int login_exec(struct ast_channel *chan, void *data)
2189 return __login_exec(chan, data, 0);
2193 * \brief Called by the AgentMonitorOutgoing application (from the dial plan).
2198 * \sa login_exec(), callback_login_exec(), load_module().
2200 static int agentmonitoroutgoing_exec(struct ast_channel *chan, void *data)
2202 int exitifnoagentid = 0;
2204 int changeoutgoing = 0;
2206 char agent[AST_MAX_AGENT];
2209 if (strchr(data, 'd'))
2210 exitifnoagentid = 1;
2211 if (strchr(data, 'n'))
2213 if (strchr(data, 'c'))
2216 if (chan->cid.cid_num) {
2218 char agentvar[AST_MAX_BUF];
2219 snprintf(agentvar, sizeof(agentvar), "%s_%s", GETAGENTBYCALLERID, chan->cid.cid_num);
2220 if ((tmp = pbx_builtin_getvar_helper(NULL, agentvar))) {
2221 struct agent_pvt *p;
2222 ast_copy_string(agent, tmp, sizeof(agent));
2223 AST_LIST_LOCK(&agents);
2224 AST_LIST_TRAVERSE(&agents, p, list) {
2225 if (!strcasecmp(p->agent, tmp)) {
2226 if (changeoutgoing) snprintf(chan->cdr->channel, sizeof(chan->cdr->channel), "Agent/%s", p->agent);
2227 __agent_start_monitoring(chan, p, 1);
2231 AST_LIST_UNLOCK(&agents);
2236 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);
2241 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");
2243 /* check if there is n + 101 priority */
2244 /*! \todo XXX Needs to check option priorityjump etc etc */
2246 if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 101, chan->cid.cid_num)) {
2247 chan->priority+=100;
2248 if (option_verbose > 2)
2249 ast_verbose(VERBOSE_PREFIX_3 "Going to %d priority because there is no callerid or the agentid cannot be found.\n",chan->priority);
2250 } else if (exitifnoagentid)
2257 * \brief Dump AgentCallbackLogin agents to the ASTdb database for persistence
2259 static void dump_agents(void)
2261 struct agent_pvt *cur_agent = NULL;
2264 AST_LIST_TRAVERSE(&agents, cur_agent, list) {
2265 if (cur_agent->chan)
2268 if (!ast_strlen_zero(cur_agent->loginchan)) {
2269 snprintf(buf, sizeof(buf), "%s;%s", cur_agent->loginchan, cur_agent->logincallerid);
2270 if (ast_db_put(pa_family, cur_agent->agent, buf))
2271 ast_log(LOG_WARNING, "failed to create persistent entry in ASTdb for %s!\n", buf);
2272 else if (option_debug)
2273 ast_log(LOG_DEBUG, "Saved Agent: %s on %s\n", cur_agent->agent, cur_agent->loginchan);
2275 /* Delete - no agent or there is an error */
2276 ast_db_del(pa_family, cur_agent->agent);
2282 * \brief Reload the persistent agents from astdb.
2284 static void reload_agents(void)
2287 struct ast_db_entry *db_tree;
2288 struct ast_db_entry *entry;
2289 struct agent_pvt *cur_agent;
2290 char agent_data[256];
2293 char *agent_callerid;
2295 db_tree = ast_db_gettree(pa_family, NULL);
2297 AST_LIST_LOCK(&agents);
2298 for (entry = db_tree; entry; entry = entry->next) {
2299 agent_num = entry->key + strlen(pa_family) + 2;
2300 AST_LIST_TRAVERSE(&agents, cur_agent, list) {
2301 ast_mutex_lock(&cur_agent->lock);
2302 if (strcmp(agent_num, cur_agent->agent) == 0)
2304 ast_mutex_unlock(&cur_agent->lock);
2307 ast_db_del(pa_family, agent_num);
2310 ast_mutex_unlock(&cur_agent->lock);
2311 if (!ast_db_get(pa_family, agent_num, agent_data, sizeof(agent_data)-1)) {
2313 ast_log(LOG_DEBUG, "Reload Agent from AstDB: %s on %s\n", cur_agent->agent, agent_data);
2315 agent_chan = strsep(&parse, ";");
2316 agent_callerid = strsep(&parse, ";");
2317 ast_copy_string(cur_agent->loginchan, agent_chan, sizeof(cur_agent->loginchan));
2318 if (agent_callerid) {
2319 ast_copy_string(cur_agent->logincallerid, agent_callerid, sizeof(cur_agent->logincallerid));
2320 set_agentbycallerid(cur_agent->logincallerid, cur_agent->agent);
2322 cur_agent->logincallerid[0] = '\0';
2323 if (cur_agent->loginstart == 0)
2324 time(&cur_agent->loginstart);
2325 ast_device_state_changed("Agent/%s", cur_agent->agent);
2328 AST_LIST_UNLOCK(&agents);
2330 ast_log(LOG_NOTICE, "Agents successfully reloaded from database.\n");
2331 ast_db_freetree(db_tree);
2335 /*! \brief Part of PBX channel interface */
2336 static int agent_devicestate(void *data)
2338 struct agent_pvt *p;
2340 ast_group_t groupmatch;
2343 int res = AST_DEVICE_INVALID;
2346 if ((s[0] == '@') && (sscanf(s + 1, "%d", &groupoff) == 1))
2347 groupmatch = (1 << groupoff);
2348 else if ((s[0] == ':') && (sscanf(s + 1, "%d", &groupoff) == 1)) {
2349 groupmatch = (1 << groupoff);
2354 /* Check actual logged in agents first */
2355 AST_LIST_LOCK(&agents);
2356 AST_LIST_TRAVERSE(&agents, p, list) {
2357 ast_mutex_lock(&p->lock);
2358 if (!p->pending && ((groupmatch && (p->group & groupmatch)) || !strcmp(data, p->agent))) {
2360 if (res != AST_DEVICE_INUSE)
2361 res = AST_DEVICE_BUSY;
2363 if (res == AST_DEVICE_BUSY)
2364 res = AST_DEVICE_INUSE;
2365 if (p->chan || !ast_strlen_zero(p->loginchan)) {
2366 if (res == AST_DEVICE_INVALID)
2367 res = AST_DEVICE_UNKNOWN;
2368 } else if (res == AST_DEVICE_INVALID)
2369 res = AST_DEVICE_UNAVAILABLE;
2371 if (!strcmp(data, p->agent)) {
2372 ast_mutex_unlock(&p->lock);
2376 ast_mutex_unlock(&p->lock);
2378 AST_LIST_UNLOCK(&agents);
2382 static struct agent_pvt *find_agent(char *agentid)
2384 struct agent_pvt *cur;
2386 AST_LIST_TRAVERSE(&agents, cur, list) {
2387 if (!strcmp(cur->agent, agentid))
2394 static int function_agent(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
2397 AST_DECLARE_APP_ARGS(args,
2398 AST_APP_ARG(agentid);
2402 struct agent_pvt *agent;
2406 if (ast_strlen_zero(data)) {
2407 ast_log(LOG_WARNING, "The AGENT function requires an argument - agentid!\n");
2411 parse = ast_strdupa(data);
2413 AST_NONSTANDARD_APP_ARGS(args, parse, ':');
2415 args.item = "status";
2417 if (!(agent = find_agent(args.agentid))) {
2418 ast_log(LOG_WARNING, "Agent '%s' not found!\n", args.agentid);
2422 if (!strcasecmp(args.item, "status")) {
2423 char *status = "LOGGEDOUT";
2424 if (agent->chan || !ast_strlen_zero(agent->loginchan))
2425 status = "LOGGEDIN";
2426 ast_copy_string(buf, status, len);
2427 } else if (!strcasecmp(args.item, "password"))
2428 ast_copy_string(buf, agent->password, len);
2429 else if (!strcasecmp(args.item, "name"))
2430 ast_copy_string(buf, agent->name, len);
2431 else if (!strcasecmp(args.item, "mohclass"))
2432 ast_copy_string(buf, agent->moh, len);
2433 else if (!strcasecmp(args.item, "channel")) {
2435 ast_copy_string(buf, agent->chan->name, len);
2436 tmp = strrchr(buf, '-');
2440 } else if (!strcasecmp(args.item, "exten"))
2441 ast_copy_string(buf, agent->loginchan, len);
2446 struct ast_custom_function agent_function = {
2448 .synopsis = "Gets information about an Agent",
2449 .syntax = "AGENT(<agentid>[:item])",
2450 .read = function_agent,
2451 .desc = "The valid items to retrieve are:\n"
2452 "- status (default) The status of the agent\n"
2453 " LOGGEDIN | LOGGEDOUT\n"
2454 "- password The password of the agent\n"
2455 "- name The name of the agent\n"
2456 "- mohclass MusicOnHold class\n"
2457 "- exten The callback extension for the Agent (AgentCallbackLogin)\n"
2458 "- channel The name of the active channel for the Agent (AgentLogin)\n"
2463 * \brief Initialize the Agents module.
2464 * This function is being called by Asterisk when loading the module.
2465 * Among other things it registers applications, cli commands and reads the cofiguration file.
2467 * \returns int Always 0.
2469 static int load_module(void)
2471 /* Make sure we can register our agent channel type */
2472 if (ast_channel_register(&agent_tech)) {
2473 ast_log(LOG_ERROR, "Unable to register channel class 'Agent'\n");
2476 /* Read in the config */
2477 if (!read_agent_config())
2478 return AST_MODULE_LOAD_DECLINE;
2479 if (persistent_agents)
2481 /* Dialplan applications */
2482 ast_register_application(app, login_exec, synopsis, descrip);
2483 ast_register_application(app3, agentmonitoroutgoing_exec, synopsis3, descrip3);
2485 /* Manager commands */
2486 ast_manager_register2("Agents", EVENT_FLAG_AGENT, action_agents, "Lists agents and their status", mandescr_agents);
2487 ast_manager_register2("AgentLogoff", EVENT_FLAG_AGENT, action_agent_logoff, "Sets an agent as no longer logged in", mandescr_agent_logoff);
2490 ast_cli_register_multiple(cli_agents, sizeof(cli_agents) / sizeof(struct ast_cli_entry));
2492 /* Dialplan Functions */
2493 ast_custom_function_register(&agent_function);
2498 static int reload(void)
2500 read_agent_config();
2501 if (persistent_agents)
2506 static int unload_module(void)
2508 struct agent_pvt *p;
2509 /* First, take us out of the channel loop */
2510 ast_channel_unregister(&agent_tech);
2511 /* Unregister dialplan functions */
2512 ast_custom_function_unregister(&agent_function);
2513 /* Unregister CLI commands */
2514 ast_cli_unregister_multiple(cli_agents, sizeof(cli_agents) / sizeof(struct ast_cli_entry));
2515 /* Unregister dialplan applications */
2516 ast_unregister_application(app);
2517 ast_unregister_application(app3);
2518 /* Unregister manager command */
2519 ast_manager_unregister("Agents");
2520 ast_manager_unregister("AgentLogoff");
2521 /* Unregister channel */
2522 AST_LIST_LOCK(&agents);
2523 /* Hangup all interfaces if they have an owner */
2524 while ((p = AST_LIST_REMOVE_HEAD(&agents, list))) {
2526 ast_softhangup(p->owner, AST_SOFTHANGUP_APPUNLOAD);
2529 AST_LIST_UNLOCK(&agents);
2530 AST_LIST_HEAD_DESTROY(&agents);
2534 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Agent Proxy Channel",
2535 .load = load_module,
2536 .unload = unload_module,