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) { \
197 ast_log(LOG_DEBUG, "Native formats changing from %d to %d\n", ast->nativeformats, p->chan->nativeformats); \
198 /* Native formats changed, reset things */ \
199 ast->nativeformats = p->chan->nativeformats; \
200 ast_log(LOG_DEBUG, "Resetting read to %d and write to %d\n", ast->readformat, ast->writeformat);\
201 ast_set_read_format(ast, ast->readformat); \
202 ast_set_write_format(ast, ast->writeformat); \
204 if (p->chan->readformat != ast->rawreadformat) \
205 ast_set_read_format(p->chan, ast->rawreadformat); \
206 if (p->chan->writeformat != ast->rawwriteformat) \
207 ast_set_write_format(p->chan, ast->rawwriteformat); \
211 /*! \brief Cleanup moves all the relevant FD's from the 2nd to the first, but retains things
212 properly for a timingfd XXX This might need more work if agents were logged in as agents or other
213 totally impractical combinations XXX */
215 #define CLEANUP(ast, p) do { \
218 for (x=0;x<AST_MAX_FDS;x++) {\
219 if (x != AST_TIMING_FD) \
220 ast->fds[x] = p->chan->fds[x]; \
222 ast->fds[AST_AGENT_FD] = p->chan->fds[AST_TIMING_FD]; \
226 /*--- Forward declarations */
227 static struct ast_channel *agent_request(const char *type, int format, void *data, int *cause);
228 static int agent_devicestate(void *data);
229 static void agent_logoff_maintenance(struct agent_pvt *p, char *loginchan, long logintime, const char *uniqueid, char *logcommand);
230 static int agent_digit_begin(struct ast_channel *ast, char digit);
231 static int agent_digit_end(struct ast_channel *ast, char digit);
232 static int agent_call(struct ast_channel *ast, char *dest, int timeout);
233 static int agent_hangup(struct ast_channel *ast);
234 static int agent_answer(struct ast_channel *ast);
235 static struct ast_frame *agent_read(struct ast_channel *ast);
236 static int agent_write(struct ast_channel *ast, struct ast_frame *f);
237 static int agent_sendhtml(struct ast_channel *ast, int subclass, const char *data, int datalen);
238 static int agent_sendtext(struct ast_channel *ast, const char *text);
239 static int agent_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen);
240 static int agent_fixup(struct ast_channel *oldchan, struct ast_channel *newchan);
241 static struct ast_channel *agent_bridgedchannel(struct ast_channel *chan, struct ast_channel *bridge);
242 static void set_agentbycallerid(const char *callerid, const char *agent);
244 /*! \brief Channel interface description for PBX integration */
245 static const struct ast_channel_tech agent_tech = {
247 .description = tdesc,
249 .requester = agent_request,
250 .devicestate = agent_devicestate,
251 .send_digit_begin = agent_digit_begin,
252 .send_digit_end = agent_digit_end,
254 .hangup = agent_hangup,
255 .answer = agent_answer,
257 .write = agent_write,
258 .write_video = agent_write,
259 .send_html = agent_sendhtml,
260 .send_text = agent_sendtext,
261 .exception = agent_read,
262 .indicate = agent_indicate,
263 .fixup = agent_fixup,
264 .bridged_channel = agent_bridgedchannel,
268 * Adds an agent to the global list of agents.
270 * \param agent A string with the username, password and real name of an agent. As defined in agents.conf. Example: "13,169,John Smith"
271 * \param pending If it is pending or not.
272 * @return The just created agent.
273 * \sa agent_pvt, agents.
275 static struct agent_pvt *add_agent(char *agent, int pending)
278 AST_DECLARE_APP_ARGS(args,
280 AST_APP_ARG(password);
283 char *password = NULL;
288 parse = ast_strdupa(agent);
290 /* Extract username (agt), password and name from agent (args). */
291 AST_NONSTANDARD_APP_ARGS(args, parse, ',');
294 ast_log(LOG_WARNING, "A blank agent line!\n");
298 if(ast_strlen_zero(args.agt) ) {
299 ast_log(LOG_WARNING, "An agent line with no agentid!\n");
304 if(!ast_strlen_zero(args.password)) {
305 password = args.password;
306 while (*password && *password < 33) password++;
308 if(!ast_strlen_zero(args.name)) {
310 while (*name && *name < 33) name++;
313 /* Are we searching for the agent here ? To see if it exists already ? */
314 AST_LIST_TRAVERSE(&agents, p, list) {
315 if (!pending && !strcmp(p->agent, agt))
320 if (!(p = ast_calloc(1, sizeof(*p))))
322 ast_copy_string(p->agent, agt, sizeof(p->agent));
323 ast_mutex_init(&p->lock);
324 ast_mutex_init(&p->app_lock);
325 p->owning_app = (pthread_t) -1;
326 p->app_sleep_cond = 1;
328 p->pending = pending;
329 AST_LIST_INSERT_TAIL(&agents, p, list);
332 ast_copy_string(p->password, password ? password : "", sizeof(p->password));
333 ast_copy_string(p->name, name ? name : "", sizeof(p->name));
334 ast_copy_string(p->moh, moh, sizeof(p->moh));
335 p->ackcall = ackcall;
336 p->autologoff = autologoff;
338 /* If someone reduces the wrapuptime and reloads, we want it
339 * to change the wrapuptime immediately on all calls */
340 if (p->wrapuptime > wrapuptime) {
341 struct timeval now = ast_tvnow();
342 /* XXX check what is this exactly */
344 /* We won't be pedantic and check the tv_usec val */
345 if (p->lastdisc.tv_sec > (now.tv_sec + wrapuptime/1000)) {
346 p->lastdisc.tv_sec = now.tv_sec + wrapuptime/1000;
347 p->lastdisc.tv_usec = now.tv_usec;
350 p->wrapuptime = wrapuptime;
360 * Deletes an agent after doing some clean up.
361 * Further documentation: How safe is this function ? What state should the agent be to be cleaned.
362 * \param p Agent to be deleted.
365 static int agent_cleanup(struct agent_pvt *p)
367 struct ast_channel *chan = p->owner;
369 chan->tech_pvt = NULL;
370 p->app_sleep_cond = 1;
371 /* Release ownership of the agent to other threads (presumably running the login app). */
372 ast_mutex_unlock(&p->app_lock);
374 ast_channel_free(chan);
376 ast_mutex_destroy(&p->lock);
377 ast_mutex_destroy(&p->app_lock);
383 static int check_availability(struct agent_pvt *newlyavailable, int needlock);
385 static int agent_answer(struct ast_channel *ast)
387 ast_log(LOG_WARNING, "Huh? Agent is being asked to answer?\n");
391 static int __agent_start_monitoring(struct ast_channel *ast, struct agent_pvt *p, int needlock)
393 char tmp[AST_MAX_BUF],tmp2[AST_MAX_BUF], *pointer;
394 char filename[AST_MAX_BUF];
399 snprintf(filename, sizeof(filename), "agent-%s-%s",p->agent, ast->uniqueid);
400 /* substitute . for - */
401 if ((pointer = strchr(filename, '.')))
403 snprintf(tmp, sizeof(tmp), "%s%s",savecallsin ? savecallsin : "", filename);
404 ast_monitor_start(ast, recordformat, tmp, needlock);
405 ast_monitor_setjoinfiles(ast, 1);
406 snprintf(tmp2, sizeof(tmp2), "%s%s.%s", urlprefix ? urlprefix : "", filename, recordformatext);
408 ast_verbose("name is %s, link is %s\n",tmp, tmp2);
411 ast->cdr = ast_cdr_alloc();
412 ast_cdr_setuserfield(ast, tmp2);
415 ast_log(LOG_ERROR, "Recording already started on that call.\n");
419 static int agent_start_monitoring(struct ast_channel *ast, int needlock)
421 return __agent_start_monitoring(ast, ast->tech_pvt, needlock);
424 static struct ast_frame *agent_read(struct ast_channel *ast)
426 struct agent_pvt *p = ast->tech_pvt;
427 struct ast_frame *f = NULL;
428 static struct ast_frame answer_frame = { AST_FRAME_CONTROL, AST_CONTROL_ANSWER };
430 ast_mutex_lock(&p->lock);
431 CHECK_FORMATS(ast, p);
433 ast_copy_flags(p->chan, ast, AST_FLAG_EXCEPTION);
434 p->chan->fdno = (ast->fdno == AST_AGENT_FD) ? AST_TIMING_FD : ast->fdno;
435 f = ast_read(p->chan);
439 /* If there's a channel, hang it up (if it's on a callback) make it NULL */
441 p->chan->_bridge = NULL;
442 /* Note that we don't hangup if it's not a callback because Asterisk will do it
443 for us when the PBX instance that called login finishes */
444 if (!ast_strlen_zero(p->loginchan)) {
446 ast_log(LOG_DEBUG, "Bridge on '%s' being cleared (2)\n", p->chan->name);
448 status = pbx_builtin_getvar_helper(p->chan, "CHANLOCALSTATUS");
449 if (autologoffunavail && status && !strcasecmp(status, "CHANUNAVAIL")) {
450 long logintime = time(NULL) - p->loginstart;
452 ast_log(LOG_NOTICE, "Agent read: '%s' is not available now, auto logoff\n", p->name);
453 agent_logoff_maintenance(p, p->loginchan, logintime, ast->uniqueid, "Chanunavail");
456 if (p->wrapuptime && p->acknowledged)
457 p->lastdisc = ast_tvadd(ast_tvnow(), ast_samp2tv(p->wrapuptime, 1000));
463 /* if acknowledgement is not required, and the channel is up, we may have missed
464 an AST_CONTROL_ANSWER (if there was one), so mark the call acknowledged anyway */
465 if (!p->ackcall && !p->acknowledged && p->chan && (p->chan->_state == AST_STATE_UP))
467 switch (f->frametype) {
468 case AST_FRAME_CONTROL:
469 if (f->subclass == AST_CONTROL_ANSWER) {
471 if (option_verbose > 2)
472 ast_verbose(VERBOSE_PREFIX_3 "%s answered, waiting for '#' to acknowledge\n", p->chan->name);
473 /* Don't pass answer along */
478 /* Use the builtin answer frame for the
479 recording start check below. */
485 case AST_FRAME_DTMF_BEGIN:
486 case AST_FRAME_DTMF_END:
487 if (!p->acknowledged && (f->subclass == '#')) {
488 if (option_verbose > 2)
489 ast_verbose(VERBOSE_PREFIX_3 "%s acknowledged\n", p->chan->name);
493 } else if (f->subclass == '*' && endcall) {
494 /* terminates call */
499 case AST_FRAME_VOICE:
500 case AST_FRAME_VIDEO:
501 /* don't pass voice or video until the call is acknowledged */
502 if (!p->acknowledged) {
507 /* pass everything else on through */
513 if (p->chan && !p->chan->_bridge) {
514 if (strcasecmp(p->chan->tech->type, "Local")) {
515 p->chan->_bridge = ast;
517 ast_log(LOG_DEBUG, "Bridge on '%s' being set to '%s' (3)\n", p->chan->name, p->chan->_bridge->name);
520 ast_mutex_unlock(&p->lock);
521 if (recordagentcalls && f == &answer_frame)
522 agent_start_monitoring(ast,0);
526 static int agent_sendhtml(struct ast_channel *ast, int subclass, const char *data, int datalen)
528 struct agent_pvt *p = ast->tech_pvt;
530 ast_mutex_lock(&p->lock);
532 res = ast_channel_sendhtml(p->chan, subclass, data, datalen);
533 ast_mutex_unlock(&p->lock);
537 static int agent_sendtext(struct ast_channel *ast, const char *text)
539 struct agent_pvt *p = ast->tech_pvt;
541 ast_mutex_lock(&p->lock);
543 res = ast_sendtext(p->chan, text);
544 ast_mutex_unlock(&p->lock);
548 static int agent_write(struct ast_channel *ast, struct ast_frame *f)
550 struct agent_pvt *p = ast->tech_pvt;
552 CHECK_FORMATS(ast, p);
553 ast_mutex_lock(&p->lock);
557 if ((f->frametype != AST_FRAME_VOICE) ||
558 (f->frametype != AST_FRAME_VIDEO) ||
559 (f->subclass == p->chan->writeformat)) {
560 res = ast_write(p->chan, f);
562 ast_log(LOG_DEBUG, "Dropping one incompatible %s frame on '%s' to '%s'\n",
563 f->frametype == AST_FRAME_VOICE ? "audio" : "video",
564 ast->name, p->chan->name);
569 ast_mutex_unlock(&p->lock);
573 static int agent_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
575 struct agent_pvt *p = newchan->tech_pvt;
576 ast_mutex_lock(&p->lock);
577 if (p->owner != oldchan) {
578 ast_log(LOG_WARNING, "old channel wasn't %p but was %p\n", oldchan, p->owner);
579 ast_mutex_unlock(&p->lock);
583 ast_mutex_unlock(&p->lock);
587 static int agent_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen)
589 struct agent_pvt *p = ast->tech_pvt;
591 ast_mutex_lock(&p->lock);
593 res = ast_indicate_data(p->chan, condition, data, datalen);
596 ast_mutex_unlock(&p->lock);
600 static int agent_digit_begin(struct ast_channel *ast, char digit)
602 struct agent_pvt *p = ast->tech_pvt;
604 ast_mutex_lock(&p->lock);
605 ast_senddigit_begin(p->chan, digit);
606 ast_mutex_unlock(&p->lock);
610 static int agent_digit_end(struct ast_channel *ast, char digit)
612 struct agent_pvt *p = ast->tech_pvt;
614 ast_mutex_lock(&p->lock);
615 ast_senddigit_end(p->chan, digit);
616 ast_mutex_unlock(&p->lock);
620 static int agent_call(struct ast_channel *ast, char *dest, int timeout)
622 struct agent_pvt *p = ast->tech_pvt;
625 ast_mutex_lock(&p->lock);
629 ast_log(LOG_DEBUG, "Pretending to dial on pending agent\n");
630 newstate = AST_STATE_DIALING;
633 ast_log(LOG_NOTICE, "Whoa, they hung up between alloc and call... what are the odds of that?\n");
636 ast_mutex_unlock(&p->lock);
638 ast_setstate(ast, newstate);
640 } else if (!ast_strlen_zero(p->loginchan)) {
642 /* Call on this agent */
643 if (option_verbose > 2)
644 ast_verbose(VERBOSE_PREFIX_3 "outgoing agentcall, to agent '%s', on '%s'\n", p->agent, p->chan->name);
645 ast_set_callerid(p->chan,
646 ast->cid.cid_num, ast->cid.cid_name, NULL);
647 ast_channel_inherit_variables(ast, p->chan);
648 res = ast_call(p->chan, p->loginchan, 0);
650 ast_mutex_unlock(&p->lock);
653 ast_verbose( VERBOSE_PREFIX_3 "agent_call, call to agent '%s' call on '%s'\n", p->agent, p->chan->name);
654 if (option_debug > 2)
655 ast_log(LOG_DEBUG, "Playing beep, lang '%s'\n", p->chan->language);
656 res = ast_streamfile(p->chan, beep, p->chan->language);
657 if (option_debug > 2)
658 ast_log(LOG_DEBUG, "Played beep, result '%d'\n", res);
660 res = ast_waitstream(p->chan, "");
661 if (option_debug > 2)
662 ast_log(LOG_DEBUG, "Waited for stream, result '%d'\n", res);
665 res = ast_set_read_format(p->chan, ast_best_codec(p->chan->nativeformats));
666 if (option_debug > 2)
667 ast_log(LOG_DEBUG, "Set read format, result '%d'\n", res);
669 ast_log(LOG_WARNING, "Unable to set read format to %s\n", ast_getformatname(ast_best_codec(p->chan->nativeformats)));
676 res = ast_set_write_format(p->chan, ast_best_codec(p->chan->nativeformats));
677 if (option_debug > 2)
678 ast_log(LOG_DEBUG, "Set write format, result '%d'\n", res);
680 ast_log(LOG_WARNING, "Unable to set write format to %s\n", ast_getformatname(ast_best_codec(p->chan->nativeformats)));
683 /* Call is immediately up, or might need ack */
685 newstate = AST_STATE_RINGING;
687 newstate = AST_STATE_UP;
688 if (recordagentcalls)
689 agent_start_monitoring(ast, 0);
695 ast_mutex_unlock(&p->lock);
697 ast_setstate(ast, newstate);
701 /*! \brief store/clear the global variable that stores agentid based on the callerid */
702 static void set_agentbycallerid(const char *callerid, const char *agent)
704 char buf[AST_MAX_BUF];
706 /* if there is no Caller ID, nothing to do */
707 if (ast_strlen_zero(callerid))
710 snprintf(buf, sizeof(buf), "%s_%s", GETAGENTBYCALLERID, callerid);
711 pbx_builtin_setvar_helper(NULL, buf, agent);
714 static int agent_hangup(struct ast_channel *ast)
716 struct agent_pvt *p = ast->tech_pvt;
719 ast_mutex_lock(&p->lock);
721 ast->tech_pvt = NULL;
722 p->app_sleep_cond = 1;
725 /* if they really are hung up then set start to 0 so the test
726 * later if we're called on an already downed channel
727 * doesn't cause an agent to be logged out like when
728 * agent_request() is followed immediately by agent_hangup()
729 * as in apps/app_chanisavail.c:chanavail_exec()
733 ast_log(LOG_DEBUG, "Hangup called for state %s\n", ast_state2str(ast->_state));
734 if (p->start && (ast->_state != AST_STATE_UP)) {
735 howlong = time(NULL) - p->start;
737 } else if (ast->_state == AST_STATE_RESERVED)
742 p->chan->_bridge = NULL;
743 /* If they're dead, go ahead and hang up on the agent now */
744 if (!ast_strlen_zero(p->loginchan)) {
745 /* Store last disconnect time */
747 p->lastdisc = ast_tvadd(ast_tvnow(), ast_samp2tv(p->wrapuptime, 1000));
749 p->lastdisc = ast_tv(0,0);
751 status = pbx_builtin_getvar_helper(p->chan, "CHANLOCALSTATUS");
752 if (autologoffunavail && status && !strcasecmp(status, "CHANUNAVAIL")) {
753 long logintime = time(NULL) - p->loginstart;
755 ast_log(LOG_NOTICE, "Agent hangup: '%s' is not available now, auto logoff\n", p->name);
756 agent_logoff_maintenance(p, p->loginchan, logintime, ast->uniqueid, "Chanunavail");
758 /* Recognize the hangup and pass it along immediately */
762 ast_log(LOG_DEBUG, "Hungup, howlong is %d, autologoff is %d\n", howlong, p->autologoff);
763 if (howlong && p->autologoff && (howlong > p->autologoff)) {
764 long logintime = time(NULL) - p->loginstart;
766 ast_log(LOG_NOTICE, "Agent '%s' didn't answer/confirm within %d seconds (waited %d)\n", p->name, p->autologoff, howlong);
767 agent_logoff_maintenance(p, p->loginchan, logintime, ast->uniqueid, "Autologoff");
769 } else if (p->dead) {
770 ast_channel_lock(p->chan);
771 ast_softhangup(p->chan, AST_SOFTHANGUP_EXPLICIT);
772 ast_channel_unlock(p->chan);
773 } else if (p->loginstart) {
774 ast_channel_lock(p->chan);
775 ast_indicate_data(p->chan, AST_CONTROL_HOLD,
777 !ast_strlen_zero(p->moh) ? strlen(p->moh) + 1 : 0);
778 ast_channel_unlock(p->chan);
781 ast_mutex_unlock(&p->lock);
782 /* Only register a device state change if the agent is still logged in */
784 ast_device_state_changed("Agent/%s", p->agent);
787 AST_LIST_LOCK(&agents);
788 AST_LIST_REMOVE(&agents, p, list);
789 AST_LIST_UNLOCK(&agents);
791 if (p->abouttograb) {
792 /* Let the "about to grab" thread know this isn't valid anymore, and let it
795 } else if (p->dead) {
796 ast_mutex_destroy(&p->lock);
797 ast_mutex_destroy(&p->app_lock);
801 /* Not dead -- check availability now */
802 ast_mutex_lock(&p->lock);
803 /* Store last disconnect time */
804 p->lastdisc = ast_tvnow();
805 ast_mutex_unlock(&p->lock);
807 /* Release ownership of the agent to other threads (presumably running the login app). */
808 if (ast_strlen_zero(p->loginchan))
809 ast_mutex_unlock(&p->app_lock);
814 static int agent_cont_sleep( void *data )
819 p = (struct agent_pvt *)data;
821 ast_mutex_lock(&p->lock);
822 res = p->app_sleep_cond;
823 if (p->lastdisc.tv_sec) {
824 if (ast_tvdiff_ms(ast_tvnow(), p->lastdisc) > p->wrapuptime)
827 ast_mutex_unlock(&p->lock);
829 if(option_debug > 4 && !res )
830 ast_log(LOG_DEBUG, "agent_cont_sleep() returning %d\n", res );
835 static int agent_ack_sleep(void *data)
842 /* Wait a second and look for something */
844 p = (struct agent_pvt *) data;
849 to = ast_waitfor(p->chan, to);
854 f = ast_read(p->chan);
857 if (f->frametype == AST_FRAME_DTMF)
862 ast_mutex_lock(&p->lock);
863 if (!p->app_sleep_cond) {
864 ast_mutex_unlock(&p->lock);
866 } else if (res == '#') {
867 ast_mutex_unlock(&p->lock);
870 ast_mutex_unlock(&p->lock);
876 static struct ast_channel *agent_bridgedchannel(struct ast_channel *chan, struct ast_channel *bridge)
878 struct agent_pvt *p = bridge->tech_pvt;
879 struct ast_channel *ret = NULL;
883 ret = bridge->_bridge;
884 else if (chan == bridge->_bridge)
889 ast_log(LOG_DEBUG, "Asked for bridged channel on '%s'/'%s', returning '%s'\n", chan->name, bridge->name, ret ? ret->name : "<none>");
893 /*! \brief Create new agent channel */
894 static struct ast_channel *agent_new(struct agent_pvt *p, int state)
896 struct ast_channel *tmp;
899 ast_log(LOG_WARNING, "No channel? :(\n");
903 tmp = ast_channel_alloc(0);
905 ast_log(LOG_WARNING, "Unable to allocate agent channel structure\n");
909 tmp->tech = &agent_tech;
911 tmp->nativeformats = p->chan->nativeformats;
912 tmp->writeformat = p->chan->writeformat;
913 tmp->rawwriteformat = p->chan->writeformat;
914 tmp->readformat = p->chan->readformat;
915 tmp->rawreadformat = p->chan->readformat;
916 ast_string_field_set(tmp, language, p->chan->language);
917 ast_copy_string(tmp->context, p->chan->context, sizeof(tmp->context));
918 ast_copy_string(tmp->exten, p->chan->exten, sizeof(tmp->exten));
919 /* XXX Is this really all we copy form the originating channel?? */
921 tmp->nativeformats = AST_FORMAT_SLINEAR;
922 tmp->writeformat = AST_FORMAT_SLINEAR;
923 tmp->rawwriteformat = AST_FORMAT_SLINEAR;
924 tmp->readformat = AST_FORMAT_SLINEAR;
925 tmp->rawreadformat = AST_FORMAT_SLINEAR;
928 ast_string_field_build(tmp, name, "Agent/P%s-%d", p->agent, ast_random() & 0xffff);
930 ast_string_field_build(tmp, name, "Agent/%s", p->agent);
931 /* Safe, agentlock already held */
932 ast_setstate(tmp, state);
935 /* XXX: this needs fixing */
937 ast_atomic_fetchadd_int(&__mod_desc->usecnt, +1);
939 ast_update_use_count();
941 /* Wake up and wait for other applications (by definition the login app)
942 * to release this channel). Takes ownership of the agent channel
943 * to this thread only.
944 * For signalling the other thread, ast_queue_frame is used until we
945 * can safely use signals for this purpose. The pselect() needs to be
946 * implemented in the kernel for this.
948 p->app_sleep_cond = 0;
949 if(ast_strlen_zero(p->loginchan) && ast_mutex_trylock(&p->app_lock)) {
951 ast_queue_frame(p->chan, &ast_null_frame);
952 ast_mutex_unlock(&p->lock); /* For other thread to read the condition. */
953 ast_mutex_lock(&p->app_lock);
954 ast_mutex_lock(&p->lock);
956 ast_log(LOG_WARNING, "Agent disconnected while we were connecting the call\n");
958 tmp->tech_pvt = NULL;
959 p->app_sleep_cond = 1;
960 ast_channel_free( tmp );
961 ast_mutex_unlock(&p->lock); /* For other thread to read the condition. */
962 ast_mutex_unlock(&p->app_lock);
965 } else if (!ast_strlen_zero(p->loginchan)) {
967 ast_queue_frame(p->chan, &ast_null_frame);
969 ast_log(LOG_WARNING, "Agent disconnected while we were connecting the call\n");
971 tmp->tech_pvt = NULL;
972 p->app_sleep_cond = 1;
973 ast_channel_free( tmp );
974 ast_mutex_unlock(&p->lock); /* For other thread to read the condition. */
978 ast_indicate(p->chan, AST_CONTROL_UNHOLD);
979 p->owning_app = pthread_self();
980 /* After the above step, there should not be any blockers. */
982 if (ast_test_flag(p->chan, AST_FLAG_BLOCKING)) {
983 ast_log( LOG_ERROR, "A blocker exists after agent channel ownership acquired\n" );
992 * Read configuration data. The file named agents.conf.
994 * \returns Always 0, or so it seems.
996 static int read_agent_config(void)
998 struct ast_config *cfg;
999 struct ast_config *ucfg;
1000 struct ast_variable *v;
1001 struct agent_pvt *p;
1002 const char *general_val;
1003 const char *catname;
1004 const char *hasagent;
1012 cfg = ast_config_load(config);
1014 ast_log(LOG_NOTICE, "No agent configuration found -- agent support disabled\n");
1017 AST_LIST_LOCK(&agents);
1018 AST_LIST_TRAVERSE(&agents, p, list) {
1021 strcpy(moh, "default");
1022 /* set the default recording values */
1023 recordagentcalls = 0;
1024 strcpy(recordformat, "wav");
1025 strcpy(recordformatext, "wav");
1026 urlprefix[0] = '\0';
1027 savecallsin[0] = '\0';
1029 /* Read in [general] section for persistence */
1030 if ((general_val = ast_variable_retrieve(cfg, "general", "persistentagents")))
1031 persistent_agents = ast_true(general_val);
1032 multiplelogin = ast_true(ast_variable_retrieve(cfg, "general", "multiplelogin"));
1034 /* Read in the [agents] section */
1035 v = ast_variable_browse(cfg, "agents");
1037 /* Create the interface list */
1038 if (!strcasecmp(v->name, "agent")) {
1039 add_agent(v->value, 0);
1040 } else if (!strcasecmp(v->name, "group")) {
1041 group = ast_get_group(v->value);
1042 } else if (!strcasecmp(v->name, "autologoff")) {
1043 autologoff = atoi(v->value);
1046 } else if (!strcasecmp(v->name, "ackcall")) {
1047 if (!strcasecmp(v->value, "always"))
1049 else if (ast_true(v->value))
1053 } else if (!strcasecmp(v->name, "endcall")) {
1054 endcall = ast_true(v->value);
1055 } else if (!strcasecmp(v->name, "wrapuptime")) {
1056 wrapuptime = atoi(v->value);
1059 } else if (!strcasecmp(v->name, "maxlogintries") && !ast_strlen_zero(v->value)) {
1060 maxlogintries = atoi(v->value);
1061 if (maxlogintries < 0)
1063 } else if (!strcasecmp(v->name, "goodbye") && !ast_strlen_zero(v->value)) {
1064 strcpy(agentgoodbye,v->value);
1065 } else if (!strcasecmp(v->name, "musiconhold")) {
1066 ast_copy_string(moh, v->value, sizeof(moh));
1067 } else if (!strcasecmp(v->name, "updatecdr")) {
1068 if (ast_true(v->value))
1072 } else if (!strcasecmp(v->name, "autologoffunavail")) {
1073 if (ast_true(v->value))
1074 autologoffunavail = 1;
1076 autologoffunavail = 0;
1077 } else if (!strcasecmp(v->name, "recordagentcalls")) {
1078 recordagentcalls = ast_true(v->value);
1079 } else if (!strcasecmp(v->name, "recordformat")) {
1080 ast_copy_string(recordformat, v->value, sizeof(recordformat));
1081 if (!strcasecmp(v->value, "wav49"))
1082 strcpy(recordformatext, "WAV");
1084 ast_copy_string(recordformatext, v->value, sizeof(recordformatext));
1085 } else if (!strcasecmp(v->name, "urlprefix")) {
1086 ast_copy_string(urlprefix, v->value, sizeof(urlprefix));
1087 if (urlprefix[strlen(urlprefix) - 1] != '/')
1088 strncat(urlprefix, "/", sizeof(urlprefix) - strlen(urlprefix) - 1);
1089 } else if (!strcasecmp(v->name, "savecallsin")) {
1090 if (v->value[0] == '/')
1091 ast_copy_string(savecallsin, v->value, sizeof(savecallsin));
1093 snprintf(savecallsin, sizeof(savecallsin) - 2, "/%s", v->value);
1094 if (savecallsin[strlen(savecallsin) - 1] != '/')
1095 strncat(savecallsin, "/", sizeof(savecallsin) - strlen(savecallsin) - 1);
1096 } else if (!strcasecmp(v->name, "custom_beep")) {
1097 ast_copy_string(beep, v->value, sizeof(beep));
1101 if ((ucfg = ast_config_load("users.conf"))) {
1102 genhasagent = ast_true(ast_variable_retrieve(ucfg, "general", "hasagent"));
1103 catname = ast_category_browse(ucfg, NULL);
1105 if (strcasecmp(catname, "general")) {
1106 hasagent = ast_variable_retrieve(ucfg, catname, "hasagent");
1107 if (ast_true(hasagent) || (!hasagent && genhasagent)) {
1109 const char *fullname = ast_variable_retrieve(ucfg, catname, "fullname");
1110 const char *secret = ast_variable_retrieve(ucfg, catname, "secret");
1115 snprintf(tmp, sizeof(tmp), "%s,%s,%s", catname, secret,fullname);
1119 catname = ast_category_browse(ucfg, catname);
1121 ast_config_destroy(ucfg);
1123 AST_LIST_TRAVERSE_SAFE_BEGIN(&agents, p, list) {
1125 AST_LIST_REMOVE_CURRENT(&agents, list);
1126 /* Destroy if appropriate */
1129 ast_mutex_destroy(&p->lock);
1130 ast_mutex_destroy(&p->app_lock);
1133 /* Cause them to hang up */
1134 ast_softhangup(p->chan, AST_SOFTHANGUP_EXPLICIT);
1139 AST_LIST_TRAVERSE_SAFE_END
1140 AST_LIST_UNLOCK(&agents);
1141 ast_config_destroy(cfg);
1145 static int check_availability(struct agent_pvt *newlyavailable, int needlock)
1147 struct ast_channel *chan=NULL, *parent=NULL;
1148 struct agent_pvt *p;
1152 ast_log(LOG_DEBUG, "Checking availability of '%s'\n", newlyavailable->agent);
1154 AST_LIST_LOCK(&agents);
1155 AST_LIST_TRAVERSE(&agents, p, list) {
1156 if (p == newlyavailable) {
1159 ast_mutex_lock(&p->lock);
1160 if (!p->abouttograb && p->pending && ((p->group && (newlyavailable->group & p->group)) || !strcmp(p->agent, newlyavailable->agent))) {
1162 ast_log(LOG_DEBUG, "Call '%s' looks like a winner for agent '%s'\n", p->owner->name, newlyavailable->agent);
1163 /* We found a pending call, time to merge */
1164 chan = agent_new(newlyavailable, AST_STATE_DOWN);
1167 ast_mutex_unlock(&p->lock);
1170 ast_mutex_unlock(&p->lock);
1173 AST_LIST_UNLOCK(&agents);
1174 if (parent && chan) {
1175 if (newlyavailable->ackcall > 1) {
1176 /* Don't do beep here */
1179 if (option_debug > 2)
1180 ast_log( LOG_DEBUG, "Playing beep, lang '%s'\n", newlyavailable->chan->language);
1181 res = ast_streamfile(newlyavailable->chan, beep, newlyavailable->chan->language);
1182 if (option_debug > 2)
1183 ast_log( LOG_DEBUG, "Played beep, result '%d'\n", res);
1185 res = ast_waitstream(newlyavailable->chan, "");
1186 ast_log( LOG_DEBUG, "Waited for stream, result '%d'\n", res);
1190 /* Note -- parent may have disappeared */
1191 if (p->abouttograb) {
1192 newlyavailable->acknowledged = 1;
1193 /* Safe -- agent lock already held */
1194 ast_setstate(parent, AST_STATE_UP);
1195 ast_setstate(chan, AST_STATE_UP);
1196 ast_copy_string(parent->context, chan->context, sizeof(parent->context));
1197 /* Go ahead and mark the channel as a zombie so that masquerade will
1198 destroy it for us, and we need not call ast_hangup */
1199 ast_mutex_lock(&parent->lock);
1200 ast_set_flag(chan, AST_FLAG_ZOMBIE);
1201 ast_channel_masquerade(parent, chan);
1202 ast_mutex_unlock(&parent->lock);
1206 ast_log(LOG_DEBUG, "Sneaky, parent disappeared in the mean time...\n");
1207 agent_cleanup(newlyavailable);
1211 ast_log(LOG_DEBUG, "Ugh... Agent hung up at exactly the wrong time\n");
1212 agent_cleanup(newlyavailable);
1218 static int check_beep(struct agent_pvt *newlyavailable, int needlock)
1220 struct agent_pvt *p;
1223 ast_log(LOG_DEBUG, "Checking beep availability of '%s'\n", newlyavailable->agent);
1225 AST_LIST_LOCK(&agents);
1226 AST_LIST_TRAVERSE(&agents, p, list) {
1227 if (p == newlyavailable) {
1230 ast_mutex_lock(&p->lock);
1231 if (!p->abouttograb && p->pending && ((p->group && (newlyavailable->group & p->group)) || !strcmp(p->agent, newlyavailable->agent))) {
1233 ast_log(LOG_DEBUG, "Call '%s' looks like a would-be winner for agent '%s'\n", p->owner->name, newlyavailable->agent);
1234 ast_mutex_unlock(&p->lock);
1237 ast_mutex_unlock(&p->lock);
1240 AST_LIST_UNLOCK(&agents);
1242 ast_mutex_unlock(&newlyavailable->lock);
1243 if (option_debug > 2)
1244 ast_log( LOG_DEBUG, "Playing beep, lang '%s'\n", newlyavailable->chan->language);
1245 res = ast_streamfile(newlyavailable->chan, beep, newlyavailable->chan->language);
1246 if (option_debug > 2)
1247 ast_log( LOG_DEBUG, "Played beep, result '%d'\n", res);
1249 res = ast_waitstream(newlyavailable->chan, "");
1251 ast_log( LOG_DEBUG, "Waited for stream, result '%d'\n", res);
1253 ast_mutex_lock(&newlyavailable->lock);
1258 /* 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. */
1259 static int allow_multiple_login(char *chan, char *context)
1261 struct agent_pvt *p;
1269 snprintf(loginchan, sizeof(loginchan), "%s@%s", chan, S_OR(context, "default"));
1271 AST_LIST_TRAVERSE(&agents, p, list) {
1272 if(!strcasecmp(chan, p->loginchan))
1278 /*! \brief Part of the Asterisk PBX interface */
1279 static struct ast_channel *agent_request(const char *type, int format, void *data, int *cause)
1281 struct agent_pvt *p;
1282 struct ast_channel *chan = NULL;
1284 ast_group_t groupmatch;
1291 if ((s[0] == '@') && (sscanf(s + 1, "%d", &groupoff) == 1)) {
1292 groupmatch = (1 << groupoff);
1293 } else if ((s[0] == ':') && (sscanf(s + 1, "%d", &groupoff) == 1)) {
1294 groupmatch = (1 << groupoff);
1299 /* Check actual logged in agents first */
1300 AST_LIST_LOCK(&agents);
1301 AST_LIST_TRAVERSE(&agents, p, list) {
1302 ast_mutex_lock(&p->lock);
1303 if (!p->pending && ((groupmatch && (p->group & groupmatch)) || !strcmp(data, p->agent)) &&
1304 ast_strlen_zero(p->loginchan)) {
1307 if (!p->lastdisc.tv_sec) {
1308 /* Agent must be registered, but not have any active call, and not be in a waiting state */
1309 if (!p->owner && p->chan) {
1311 chan = agent_new(p, AST_STATE_DOWN);
1314 ast_mutex_unlock(&p->lock);
1319 ast_mutex_unlock(&p->lock);
1322 AST_LIST_TRAVERSE(&agents, p, list) {
1323 ast_mutex_lock(&p->lock);
1324 if (!p->pending && ((groupmatch && (p->group & groupmatch)) || !strcmp(data, p->agent))) {
1325 if (p->chan || !ast_strlen_zero(p->loginchan))
1329 ast_log(LOG_NOTICE, "Time now: %ld, Time of lastdisc: %ld\n", tv.tv_sec, p->lastdisc.tv_sec);
1331 if (!p->lastdisc.tv_sec || (tv.tv_sec > p->lastdisc.tv_sec)) {
1332 p->lastdisc = ast_tv(0, 0);
1333 /* Agent must be registered, but not have any active call, and not be in a waiting state */
1334 if (!p->owner && p->chan) {
1335 /* Could still get a fixed agent */
1336 chan = agent_new(p, AST_STATE_DOWN);
1337 } else if (!p->owner && !ast_strlen_zero(p->loginchan)) {
1338 /* Adjustable agent */
1339 p->chan = ast_request("Local", format, p->loginchan, cause);
1341 chan = agent_new(p, AST_STATE_DOWN);
1344 ast_mutex_unlock(&p->lock);
1349 ast_mutex_unlock(&p->lock);
1353 if (!chan && waitforagent) {
1354 /* No agent available -- but we're requesting to wait for one.
1355 Allocate a place holder */
1358 ast_log(LOG_DEBUG, "Creating place holder for '%s'\n", s);
1359 p = add_agent(data, 1);
1360 p->group = groupmatch;
1361 chan = agent_new(p, AST_STATE_DOWN);
1363 ast_log(LOG_WARNING, "Weird... Fix this to drop the unused pending agent\n");
1365 ast_log(LOG_DEBUG, "Not creating place holder for '%s' since nobody logged in\n", s);
1367 *cause = hasagent ? AST_CAUSE_BUSY : AST_CAUSE_UNREGISTERED;
1368 AST_LIST_UNLOCK(&agents);
1372 static force_inline int powerof(unsigned int d)
1383 * Lists agents and their status to the Manager API.
1384 * It is registered on load_module() and it gets called by the manager backend.
1388 * \sa action_agent_logoff(), action_agent_callback_login(), load_module().
1390 static int action_agents(struct mansession *s, struct message *m)
1392 char *id = astman_get_header(m,"ActionID");
1393 char idText[256] = "";
1395 struct agent_pvt *p;
1396 char *username = NULL;
1397 char *loginChan = NULL;
1398 char *talkingtoChan = NULL;
1399 char *status = NULL;
1401 if (!ast_strlen_zero(id))
1402 snprintf(idText, sizeof(idText) ,"ActionID: %s\r\n", id);
1403 astman_send_ack(s, m, "Agents will follow");
1404 AST_LIST_LOCK(&agents);
1405 AST_LIST_TRAVERSE(&agents, p, list) {
1406 ast_mutex_lock(&p->lock);
1409 AGENT_LOGGEDOFF - Agent isn't logged in
1410 AGENT_IDLE - Agent is logged in, and waiting for call
1411 AGENT_ONCALL - Agent is logged in, and on a call
1412 AGENT_UNKNOWN - Don't know anything about agent. Shouldn't ever get this. */
1414 username = S_OR(p->name, "None");
1416 /* Set a default status. It 'should' get changed. */
1417 status = "AGENT_UNKNOWN";
1419 if (!ast_strlen_zero(p->loginchan) && !p->chan) {
1420 loginChan = p->loginchan;
1421 talkingtoChan = "n/a";
1422 status = "AGENT_IDLE";
1423 if (p->acknowledged) {
1424 snprintf(chanbuf, sizeof(chanbuf), " %s (Confirmed)", p->loginchan);
1425 loginChan = chanbuf;
1427 } else if (p->chan) {
1428 loginChan = ast_strdupa(p->chan->name);
1429 if (p->owner && p->owner->_bridge) {
1430 talkingtoChan = p->chan->cid.cid_num;
1431 status = "AGENT_ONCALL";
1433 talkingtoChan = "n/a";
1434 status = "AGENT_IDLE";
1438 talkingtoChan = "n/a";
1439 status = "AGENT_LOGGEDOFF";
1442 astman_append(s, "Event: Agents\r\n"
1446 "LoggedInChan: %s\r\n"
1447 "LoggedInTime: %d\r\n"
1451 p->agent, username, status, loginChan, (int)p->loginstart, talkingtoChan, idText);
1452 ast_mutex_unlock(&p->lock);
1454 AST_LIST_UNLOCK(&agents);
1455 astman_append(s, "Event: AgentsComplete\r\n"
1461 static void agent_logoff_maintenance(struct agent_pvt *p, char *loginchan, long logintime, const char *uniqueid, char *logcommand)
1464 char agent[AST_MAX_AGENT];
1466 if (!ast_strlen_zero(logcommand))
1469 tmp = ast_strdupa("");
1471 snprintf(agent, sizeof(agent), "Agent/%s", p->agent);
1473 if (!ast_strlen_zero(uniqueid)) {
1474 manager_event(EVENT_FLAG_AGENT, "Agentcallbacklogoff",
1478 "Logintime: %ld\r\n"
1480 p->agent, tmp, loginchan, logintime, uniqueid);
1482 manager_event(EVENT_FLAG_AGENT, "Agentcallbacklogoff",
1486 "Logintime: %ld\r\n",
1487 p->agent, tmp, loginchan, logintime);
1490 ast_queue_log("NONE", ast_strlen_zero(uniqueid) ? "NONE" : uniqueid, agent, "AGENTCALLBACKLOGOFF", "%s|%ld|%s", loginchan, logintime, tmp);
1491 set_agentbycallerid(p->logincallerid, NULL);
1492 p->loginchan[0] ='\0';
1493 p->logincallerid[0] = '\0';
1494 ast_device_state_changed("Agent/%s", p->agent);
1495 if (persistent_agents)
1500 static int agent_logoff(char *agent, int soft)
1502 struct agent_pvt *p;
1504 int ret = -1; /* Return -1 if no agent if found */
1506 AST_LIST_TRAVERSE(&agents, p, list) {
1507 if (!strcasecmp(p->agent, agent)) {
1510 ast_softhangup(p->owner, AST_SOFTHANGUP_EXPLICIT);
1512 ast_softhangup(p->chan, AST_SOFTHANGUP_EXPLICIT);
1514 ret = 0; /* found an agent => return 0 */
1515 logintime = time(NULL) - p->loginstart;
1517 agent_logoff_maintenance(p, p->loginchan, logintime, NULL, "CommandLogoff");
1525 static int agent_logoff_cmd(int fd, int argc, char **argv)
1530 if (argc < 3 || argc > 4)
1531 return RESULT_SHOWUSAGE;
1532 if (argc == 4 && strcasecmp(argv[3], "soft"))
1533 return RESULT_SHOWUSAGE;
1535 agent = argv[2] + 6;
1536 ret = agent_logoff(agent, argc == 4);
1538 ast_cli(fd, "Logging out %s\n", agent);
1540 return RESULT_SUCCESS;
1544 * Sets an agent as no longer logged in in the Manager API.
1545 * It is registered on load_module() and it gets called by the manager backend.
1549 * \sa action_agents(), action_agent_callback_login(), load_module().
1551 static int action_agent_logoff(struct mansession *s, struct message *m)
1553 char *agent = astman_get_header(m, "Agent");
1554 char *soft_s = astman_get_header(m, "Soft"); /* "true" is don't hangup */
1556 int ret; /* return value of agent_logoff */
1558 if (ast_strlen_zero(agent)) {
1559 astman_send_error(s, m, "No agent specified");
1563 soft = ast_true(soft_s) ? 1 : 0;
1564 ret = agent_logoff(agent, soft);
1566 astman_send_ack(s, m, "Agent logged out");
1568 astman_send_error(s, m, "No such agent");
1573 static char *complete_agent_logoff_cmd(const char *line, const char *word, int pos, int state)
1576 struct agent_pvt *p;
1577 char name[AST_MAX_AGENT];
1578 int which = 0, len = strlen(word);
1580 AST_LIST_TRAVERSE(&agents, p, list) {
1581 snprintf(name, sizeof(name), "Agent/%s", p->agent);
1582 if (!strncasecmp(word, name, len) && ++which > state)
1583 return ast_strdup(name);
1585 } else if (pos == 3 && state == 0)
1586 return ast_strdup("soft");
1592 * Show agents in cli.
1594 static int agents_show(int fd, int argc, char **argv)
1596 struct agent_pvt *p;
1597 char username[AST_MAX_BUF];
1598 char location[AST_MAX_BUF] = "";
1599 char talkingto[AST_MAX_BUF] = "";
1600 char moh[AST_MAX_BUF];
1601 int count_agents = 0; /*!< Number of agents configured */
1602 int online_agents = 0; /*!< Number of online agents */
1603 int offline_agents = 0; /*!< Number of offline agents */
1605 return RESULT_SHOWUSAGE;
1606 AST_LIST_LOCK(&agents);
1607 AST_LIST_TRAVERSE(&agents, p, list) {
1608 ast_mutex_lock(&p->lock);
1611 ast_cli(fd, "-- Pending call to group %d\n", powerof(p->group));
1613 ast_cli(fd, "-- Pending call to agent %s\n", p->agent);
1615 if (!ast_strlen_zero(p->name))
1616 snprintf(username, sizeof(username), "(%s) ", p->name);
1620 snprintf(location, sizeof(location), "logged in on %s", p->chan->name);
1621 if (p->owner && ast_bridged_channel(p->owner))
1622 snprintf(talkingto, sizeof(talkingto), " talking to %s", ast_bridged_channel(p->owner)->name);
1624 strcpy(talkingto, " is idle");
1626 } else if (!ast_strlen_zero(p->loginchan)) {
1627 if (ast_tvdiff_ms(ast_tvnow(), p->lastdisc) > 0 || !(p->lastdisc.tv_sec))
1628 snprintf(location, sizeof(location) - 20, "available at '%s'", p->loginchan);
1630 snprintf(location, sizeof(location) - 20, "wrapping up at '%s'", p->loginchan);
1631 talkingto[0] = '\0';
1633 if (p->acknowledged)
1634 strncat(location, " (Confirmed)", sizeof(location) - strlen(location) - 1);
1636 strcpy(location, "not logged in");
1637 talkingto[0] = '\0';
1640 if (!ast_strlen_zero(p->moh))
1641 snprintf(moh, sizeof(moh), " (musiconhold is '%s')", p->moh);
1642 ast_cli(fd, "%-12.12s %s%s%s%s\n", p->agent,
1643 username, location, talkingto, moh);
1646 ast_mutex_unlock(&p->lock);
1648 AST_LIST_UNLOCK(&agents);
1649 if ( !count_agents )
1650 ast_cli(fd, "No Agents are configured in %s\n",config);
1652 ast_cli(fd, "%d agents configured [%d online , %d offline]\n",count_agents, online_agents, offline_agents);
1655 return RESULT_SUCCESS;
1659 static int agents_show_online(int fd, int argc, char **argv)
1661 struct agent_pvt *p;
1662 char username[AST_MAX_BUF];
1663 char location[AST_MAX_BUF] = "";
1664 char talkingto[AST_MAX_BUF] = "";
1665 char moh[AST_MAX_BUF];
1666 int count_agents = 0; /* Number of agents configured */
1667 int online_agents = 0; /* Number of online agents */
1668 int agent_status = 0; /* 0 means offline, 1 means online */
1670 return RESULT_SHOWUSAGE;
1671 AST_LIST_LOCK(&agents);
1672 AST_LIST_TRAVERSE(&agents, p, list) {
1673 agent_status = 0; /* reset it to offline */
1674 ast_mutex_lock(&p->lock);
1675 if (!ast_strlen_zero(p->name))
1676 snprintf(username, sizeof(username), "(%s) ", p->name);
1680 snprintf(location, sizeof(location), "logged in on %s", p->chan->name);
1681 if (p->owner && ast_bridged_channel(p->owner))
1682 snprintf(talkingto, sizeof(talkingto), " talking to %s", ast_bridged_channel(p->owner)->name);
1684 strcpy(talkingto, " is idle");
1687 } else if (!ast_strlen_zero(p->loginchan)) {
1688 snprintf(location, sizeof(location) - 20, "available at '%s'", p->loginchan);
1689 talkingto[0] = '\0';
1692 if (p->acknowledged)
1693 strncat(location, " (Confirmed)", sizeof(location) - strlen(location) - 1);
1695 if (!ast_strlen_zero(p->moh))
1696 snprintf(moh, sizeof(moh), " (musiconhold is '%s')", p->moh);
1698 ast_cli(fd, "%-12.12s %s%s%s%s\n", p->agent, username, location, talkingto, moh);
1700 ast_mutex_unlock(&p->lock);
1702 AST_LIST_UNLOCK(&agents);
1704 ast_cli(fd, "No Agents are configured in %s\n", config);
1706 ast_cli(fd, "%d agents online\n", online_agents);
1708 return RESULT_SUCCESS;
1713 static char show_agents_usage[] =
1714 "Usage: agent list\n"
1715 " Provides summary information on agents.\n";
1717 static char show_agents_online_usage[] =
1718 "Usage: agent list online\n"
1719 " Provides a list of all online agents.\n";
1721 static char agent_logoff_usage[] =
1722 "Usage: agent logoff <channel> [soft]\n"
1723 " Sets an agent as no longer logged in.\n"
1724 " If 'soft' is specified, do not hangup existing calls.\n";
1726 static struct ast_cli_entry cli_agents[] = {
1727 { { "agent", "list", NULL },
1728 agents_show, "Show status of agents",
1729 show_agents_usage },
1731 { { "agent", "list", "online" },
1732 agents_show_online, "Show all online agents",
1733 show_agents_online_usage },
1735 { { "agent", "logoff", NULL },
1736 agent_logoff_cmd, "Sets an agent offline",
1737 agent_logoff_usage, complete_agent_logoff_cmd },
1741 * \brief Log in agent application.
1745 * \param callbackmode non-zero for AgentCallbackLogin
1747 static int __login_exec(struct ast_channel *chan, void *data, int callbackmode)
1751 int max_login_tries = maxlogintries;
1752 struct agent_pvt *p;
1753 struct ast_module_user *u;
1754 int login_state = 0;
1755 char user[AST_MAX_AGENT] = "";
1756 char pass[AST_MAX_AGENT];
1757 char agent[AST_MAX_AGENT] = "";
1758 char xpass[AST_MAX_AGENT] = "";
1761 AST_DECLARE_APP_ARGS(args,
1762 AST_APP_ARG(agent_id);
1763 AST_APP_ARG(options);
1764 AST_APP_ARG(extension);
1766 const char *tmpoptions = NULL;
1767 char *context = NULL;
1768 int play_announcement = 1;
1769 char agent_goodbye[AST_MAX_FILENAME_LEN];
1770 int update_cdr = updatecdr;
1771 char *filename = "agent-loginok";
1772 char tmpchan[AST_MAX_BUF] = "";
1774 u = ast_module_user_add(chan);
1776 parse = ast_strdupa(data);
1778 AST_STANDARD_APP_ARGS(args, parse);
1780 ast_copy_string(agent_goodbye, agentgoodbye, sizeof(agent_goodbye));
1782 /* Set Channel Specific Login Overrides */
1783 if (pbx_builtin_getvar_helper(chan, "AGENTLMAXLOGINTRIES") && strlen(pbx_builtin_getvar_helper(chan, "AGENTLMAXLOGINTRIES"))) {
1784 max_login_tries = atoi(pbx_builtin_getvar_helper(chan, "AGENTMAXLOGINTRIES"));
1785 if (max_login_tries < 0)
1786 max_login_tries = 0;
1787 tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTMAXLOGINTRIES");
1788 if (option_verbose > 2)
1789 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);
1791 if (pbx_builtin_getvar_helper(chan, "AGENTUPDATECDR") && !ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENTUPDATECDR"))) {
1792 if (ast_true(pbx_builtin_getvar_helper(chan, "AGENTUPDATECDR")))
1796 tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTUPDATECDR");
1797 if (option_verbose > 2)
1798 ast_verbose(VERBOSE_PREFIX_3 "Saw variable AGENTUPDATECDR=%s, setting update_cdr to: %d on Channel '%s'.\n",tmpoptions,update_cdr,chan->name);
1800 if (pbx_builtin_getvar_helper(chan, "AGENTGOODBYE") && !ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENTGOODBYE"))) {
1801 strcpy(agent_goodbye, pbx_builtin_getvar_helper(chan, "AGENTGOODBYE"));
1802 tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTGOODBYE");
1803 if (option_verbose > 2)
1804 ast_verbose(VERBOSE_PREFIX_3 "Saw variable AGENTGOODBYE=%s, setting agent_goodbye to: %s on Channel '%s'.\n",tmpoptions,agent_goodbye,chan->name);
1806 /* End Channel Specific Login Overrides */
1808 if (callbackmode && args.extension) {
1809 parse = args.extension;
1810 args.extension = strsep(&parse, "@");
1814 if (!ast_strlen_zero(args.options)) {
1815 if (strchr(args.options, 's')) {
1816 play_announcement = 0;
1820 if (chan->_state != AST_STATE_UP)
1821 res = ast_answer(chan);
1823 if (!ast_strlen_zero(args.agent_id))
1824 ast_copy_string(user, args.agent_id, AST_MAX_AGENT);
1826 res = ast_app_getdata(chan, "agent-user", user, sizeof(user) - 1, 0);
1828 while (!res && (max_login_tries==0 || tries < max_login_tries)) {
1830 /* Check for password */
1831 AST_LIST_LOCK(&agents);
1832 AST_LIST_TRAVERSE(&agents, p, list) {
1833 if (!strcmp(p->agent, user) && !p->pending)
1834 ast_copy_string(xpass, p->password, sizeof(xpass));
1836 AST_LIST_UNLOCK(&agents);
1838 if (!ast_strlen_zero(xpass))
1839 res = ast_app_getdata(chan, "agent-pass", pass, sizeof(pass) - 1, 0);
1843 errmsg = "agent-incorrect";
1846 ast_log(LOG_NOTICE, "user: %s, pass: %s\n", user, pass);
1849 /* Check again for accuracy */
1850 AST_LIST_LOCK(&agents);
1851 AST_LIST_TRAVERSE(&agents, p, list) {
1852 ast_mutex_lock(&p->lock);
1853 if (!strcmp(p->agent, user) &&
1854 !strcmp(p->password, pass) && !p->pending) {
1855 login_state = 1; /* Successful Login */
1857 /* Ensure we can't be gotten until we're done */
1858 gettimeofday(&p->lastdisc, NULL);
1859 p->lastdisc.tv_sec++;
1861 /* Set Channel Specific Agent Overrides */
1862 if (pbx_builtin_getvar_helper(chan, "AGENTACKCALL") && strlen(pbx_builtin_getvar_helper(chan, "AGENTACKCALL"))) {
1863 if (!strcasecmp(pbx_builtin_getvar_helper(chan, "AGENTACKCALL"), "always"))
1865 else if (ast_true(pbx_builtin_getvar_helper(chan, "AGENTACKCALL")))
1869 tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTACKCALL");
1870 if (option_verbose > 2)
1871 ast_verbose(VERBOSE_PREFIX_3 "Saw variable AGENTACKCALL=%s, setting ackcall to: %d for Agent '%s'.\n",tmpoptions,p->ackcall,p->agent);
1873 if (pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF") && strlen(pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF"))) {
1874 p->autologoff = atoi(pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF"));
1875 if (p->autologoff < 0)
1877 tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF");
1878 if (option_verbose > 2)
1879 ast_verbose(VERBOSE_PREFIX_3 "Saw variable AGENTAUTOLOGOFF=%s, setting autologff to: %d for Agent '%s'.\n",tmpoptions,p->autologoff,p->agent);
1881 if (pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME") && strlen(pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME"))) {
1882 p->wrapuptime = atoi(pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME"));
1883 if (p->wrapuptime < 0)
1885 tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME");
1886 if (option_verbose > 2)
1887 ast_verbose(VERBOSE_PREFIX_3 "Saw variable AGENTWRAPUPTIME=%s, setting wrapuptime to: %d for Agent '%s'.\n",tmpoptions,p->wrapuptime,p->agent);
1889 /* End Channel Specific Agent Overrides */
1891 char last_loginchan[80] = "";
1893 snprintf(agent, sizeof(agent), "Agent/%s", p->agent);
1897 /* Retrieve login chan */
1899 if (!ast_strlen_zero(args.extension)) {
1900 ast_copy_string(tmpchan, args.extension, sizeof(tmpchan));
1903 res = ast_app_getdata(chan, "agent-newlocation", tmpchan+pos, sizeof(tmpchan) - 2, 0);
1904 if (ast_strlen_zero(tmpchan) )
1906 if(ast_exists_extension(chan, S_OR(context,"default"), tmpchan,1, NULL) ) {
1907 if(!allow_multiple_login(tmpchan,context) ) {
1908 args.extension = NULL;
1913 if (args.extension) {
1914 ast_log(LOG_WARNING, "Extension '%s' is not valid for automatic login of agent '%s'\n", args.extension, p->agent);
1915 args.extension = NULL;
1918 ast_log(LOG_WARNING, "Extension '%s@%s' is not valid for automatic login of agent '%s'\n", tmpchan, S_OR(context, "default"), p->agent);
1919 res = ast_streamfile(chan, "invalid", chan->language);
1921 res = ast_waitstream(chan, AST_DIGIT_ANY);
1932 args.extension = tmpchan;
1934 set_agentbycallerid(p->logincallerid, NULL);
1935 if (!ast_strlen_zero(context) && !ast_strlen_zero(tmpchan))
1936 snprintf(p->loginchan, sizeof(p->loginchan), "%s@%s", tmpchan, context);
1938 ast_copy_string(last_loginchan, p->loginchan, sizeof(last_loginchan));
1939 ast_copy_string(p->loginchan, tmpchan, sizeof(p->loginchan));
1941 p->acknowledged = 0;
1942 if (ast_strlen_zero(p->loginchan)) {
1944 filename = "agent-loggedoff";
1946 if (chan->cid.cid_num) {
1947 ast_copy_string(p->logincallerid, chan->cid.cid_num, sizeof(p->logincallerid));
1948 set_agentbycallerid(p->logincallerid, p->agent);
1950 p->logincallerid[0] = '\0';
1953 if(update_cdr && chan->cdr)
1954 snprintf(chan->cdr->channel, sizeof(chan->cdr->channel), "Agent/%s", p->agent);
1958 p->loginchan[0] = '\0';
1959 p->logincallerid[0] = '\0';
1960 p->acknowledged = 0;
1962 ast_mutex_unlock(&p->lock);
1963 AST_LIST_UNLOCK(&agents);
1964 if( !res && play_announcement==1 )
1965 res = ast_streamfile(chan, filename, chan->language);
1967 ast_waitstream(chan, "");
1968 AST_LIST_LOCK(&agents);
1969 ast_mutex_lock(&p->lock);
1971 res = ast_set_read_format(chan, ast_best_codec(chan->nativeformats));
1973 ast_log(LOG_WARNING, "Unable to set read format to %d\n", ast_best_codec(chan->nativeformats));
1976 res = ast_set_write_format(chan, ast_best_codec(chan->nativeformats));
1978 ast_log(LOG_WARNING, "Unable to set write format to %d\n", ast_best_codec(chan->nativeformats));
1980 /* Check once more just in case */
1983 if (callbackmode && !res) {
1984 /* Just say goodbye and be done with it */
1985 if (!ast_strlen_zero(p->loginchan)) {
1986 if (p->loginstart == 0)
1987 time(&p->loginstart);
1988 manager_event(EVENT_FLAG_AGENT, "Agentcallbacklogin",
1992 p->agent, p->loginchan, chan->uniqueid);
1993 ast_queue_log("NONE", chan->uniqueid, agent, "AGENTCALLBACKLOGIN", "%s", p->loginchan);
1994 if (option_verbose > 1)
1995 ast_verbose(VERBOSE_PREFIX_2 "Callback Agent '%s' logged in on %s\n", p->agent, p->loginchan);
1996 ast_device_state_changed("Agent/%s", p->agent);
1997 if (persistent_agents)
2000 logintime = time(NULL) - p->loginstart;
2003 agent_logoff_maintenance(p, last_loginchan, logintime, chan->uniqueid, NULL);
2004 if (option_verbose > 1)
2005 ast_verbose(VERBOSE_PREFIX_2 "Callback Agent '%s' logged out\n", p->agent);
2007 AST_LIST_UNLOCK(&agents);
2009 res = ast_safe_sleep(chan, 500);
2010 ast_mutex_unlock(&p->lock);
2012 ast_indicate_data(chan, AST_CONTROL_HOLD,
2014 !ast_strlen_zero(p->moh) ? strlen(p->moh) + 1 : 0);
2015 if (p->loginstart == 0)
2016 time(&p->loginstart);
2017 manager_event(EVENT_FLAG_AGENT, "Agentlogin",
2021 p->agent, chan->name, chan->uniqueid);
2022 if (update_cdr && chan->cdr)
2023 snprintf(chan->cdr->channel, sizeof(chan->cdr->channel), "Agent/%s", p->agent);
2024 ast_queue_log("NONE", chan->uniqueid, agent, "AGENTLOGIN", "%s", chan->name);
2025 if (option_verbose > 1)
2026 ast_verbose(VERBOSE_PREFIX_2 "Agent '%s' logged in (format %s/%s)\n", p->agent,
2027 ast_getformatname(chan->readformat), ast_getformatname(chan->writeformat));
2028 /* Login this channel and wait for it to go away */
2033 check_availability(p, 0);
2034 ast_mutex_unlock(&p->lock);
2035 AST_LIST_UNLOCK(&agents);
2036 ast_device_state_changed("Agent/%s", p->agent);
2038 ast_mutex_lock(&p->lock);
2039 if (p->chan != chan)
2041 ast_mutex_unlock(&p->lock);
2042 /* Yield here so other interested threads can kick in. */
2047 AST_LIST_LOCK(&agents);
2048 ast_mutex_lock(&p->lock);
2049 if (p->lastdisc.tv_sec) {
2050 if (ast_tvdiff_ms(ast_tvnow(), p->lastdisc) > p->wrapuptime) {
2052 ast_log(LOG_DEBUG, "Wrapup time for %s expired!\n", p->agent);
2053 p->lastdisc = ast_tv(0, 0);
2057 check_availability(p, 0);
2060 ast_mutex_unlock(&p->lock);
2061 AST_LIST_UNLOCK(&agents);
2062 /* Synchronize channel ownership between call to agent and itself. */
2063 ast_mutex_lock( &p->app_lock );
2064 ast_mutex_lock(&p->lock);
2065 p->owning_app = pthread_self();
2066 ast_mutex_unlock(&p->lock);
2068 res = agent_ack_sleep(p);
2070 res = ast_safe_sleep_conditional( chan, 1000, agent_cont_sleep, p );
2071 ast_mutex_unlock( &p->app_lock );
2072 if ((p->ackcall > 1) && (res == 1)) {
2073 AST_LIST_LOCK(&agents);
2074 ast_mutex_lock(&p->lock);
2075 check_availability(p, 0);
2076 ast_mutex_unlock(&p->lock);
2077 AST_LIST_UNLOCK(&agents);
2082 ast_mutex_lock(&p->lock);
2083 if (res && p->owner)
2084 ast_log(LOG_WARNING, "Huh? We broke out when there was still an owner?\n");
2085 /* Log us off if appropriate */
2086 if (p->chan == chan)
2088 p->acknowledged = 0;
2089 logintime = time(NULL) - p->loginstart;
2091 ast_mutex_unlock(&p->lock);
2092 manager_event(EVENT_FLAG_AGENT, "Agentlogoff",
2094 "Logintime: %ld\r\n"
2096 p->agent, logintime, chan->uniqueid);
2097 ast_queue_log("NONE", chan->uniqueid, agent, "AGENTLOGOFF", "%s|%ld", chan->name, logintime);
2098 if (option_verbose > 1)
2099 ast_verbose(VERBOSE_PREFIX_2 "Agent '%s' logged out\n", p->agent);
2100 /* If there is no owner, go ahead and kill it now */
2101 ast_device_state_changed("Agent/%s", p->agent);
2102 if (p->dead && !p->owner) {
2103 ast_mutex_destroy(&p->lock);
2104 ast_mutex_destroy(&p->app_lock);
2109 ast_mutex_unlock(&p->lock);
2114 ast_mutex_unlock(&p->lock);
2115 errmsg = "agent-alreadyon";
2120 ast_mutex_unlock(&p->lock);
2123 AST_LIST_UNLOCK(&agents);
2125 if (!res && (max_login_tries==0 || tries < max_login_tries))
2126 res = ast_app_getdata(chan, errmsg, user, sizeof(user) - 1, 0);
2130 res = ast_safe_sleep(chan, 500);
2132 /* AgentLogin() exit */
2133 if (!callbackmode) {
2134 ast_module_user_remove(u);
2136 } else { /* AgentCallbackLogin() exit*/
2138 if (login_state > 0) {
2139 pbx_builtin_setvar_helper(chan, "AGENTNUMBER", user);
2140 if (login_state==1) {
2141 pbx_builtin_setvar_helper(chan, "AGENTSTATUS", "on");
2142 pbx_builtin_setvar_helper(chan, "AGENTEXTEN", args.extension);
2144 pbx_builtin_setvar_helper(chan, "AGENTSTATUS", "off");
2146 pbx_builtin_setvar_helper(chan, "AGENTSTATUS", "fail");
2148 if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 1, chan->cid.cid_num)) {
2149 ast_module_user_remove(u);
2152 /* Do we need to play agent-goodbye now that we will be hanging up? */
2153 if (play_announcement) {
2155 res = ast_safe_sleep(chan, 1000);
2156 res = ast_streamfile(chan, agent_goodbye, chan->language);
2158 res = ast_waitstream(chan, "");
2160 res = ast_safe_sleep(chan, 1000);
2164 ast_module_user_remove(u);
2166 /* We should never get here if next priority exists when in callbackmode */
2171 * Called by the AgentLogin application (from the dial plan).
2176 * \sa callback_login_exec(), agentmonitoroutgoing_exec(), load_module().
2178 static int login_exec(struct ast_channel *chan, void *data)
2180 return __login_exec(chan, data, 0);
2184 * \brief Called by the AgentMonitorOutgoing application (from the dial plan).
2189 * \sa login_exec(), callback_login_exec(), load_module().
2191 static int agentmonitoroutgoing_exec(struct ast_channel *chan, void *data)
2193 int exitifnoagentid = 0;
2195 int changeoutgoing = 0;
2197 char agent[AST_MAX_AGENT];
2200 if (strchr(data, 'd'))
2201 exitifnoagentid = 1;
2202 if (strchr(data, 'n'))
2204 if (strchr(data, 'c'))
2207 if (chan->cid.cid_num) {
2209 char agentvar[AST_MAX_BUF];
2210 snprintf(agentvar, sizeof(agentvar), "%s_%s", GETAGENTBYCALLERID, chan->cid.cid_num);
2211 if ((tmp = pbx_builtin_getvar_helper(NULL, agentvar))) {
2212 struct agent_pvt *p;
2213 ast_copy_string(agent, tmp, sizeof(agent));
2214 AST_LIST_LOCK(&agents);
2215 AST_LIST_TRAVERSE(&agents, p, list) {
2216 if (!strcasecmp(p->agent, tmp)) {
2217 if (changeoutgoing) snprintf(chan->cdr->channel, sizeof(chan->cdr->channel), "Agent/%s", p->agent);
2218 __agent_start_monitoring(chan, p, 1);
2222 AST_LIST_UNLOCK(&agents);
2227 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);
2232 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");
2234 /* check if there is n + 101 priority */
2235 /*! \todo XXX Needs to check option priorityjump etc etc */
2237 if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 101, chan->cid.cid_num)) {
2238 chan->priority+=100;
2239 if (option_verbose > 2)
2240 ast_verbose(VERBOSE_PREFIX_3 "Going to %d priority because there is no callerid or the agentid cannot be found.\n",chan->priority);
2241 } else if (exitifnoagentid)
2248 * \brief Dump AgentCallbackLogin agents to the ASTdb database for persistence
2250 static void dump_agents(void)
2252 struct agent_pvt *cur_agent = NULL;
2255 AST_LIST_TRAVERSE(&agents, cur_agent, list) {
2256 if (cur_agent->chan)
2259 if (!ast_strlen_zero(cur_agent->loginchan)) {
2260 snprintf(buf, sizeof(buf), "%s;%s", cur_agent->loginchan, cur_agent->logincallerid);
2261 if (ast_db_put(pa_family, cur_agent->agent, buf))
2262 ast_log(LOG_WARNING, "failed to create persistent entry in ASTdb for %s!\n", buf);
2263 else if (option_debug)
2264 ast_log(LOG_DEBUG, "Saved Agent: %s on %s\n", cur_agent->agent, cur_agent->loginchan);
2266 /* Delete - no agent or there is an error */
2267 ast_db_del(pa_family, cur_agent->agent);
2273 * \brief Reload the persistent agents from astdb.
2275 static void reload_agents(void)
2278 struct ast_db_entry *db_tree;
2279 struct ast_db_entry *entry;
2280 struct agent_pvt *cur_agent;
2281 char agent_data[256];
2284 char *agent_callerid;
2286 db_tree = ast_db_gettree(pa_family, NULL);
2288 AST_LIST_LOCK(&agents);
2289 for (entry = db_tree; entry; entry = entry->next) {
2290 agent_num = entry->key + strlen(pa_family) + 2;
2291 AST_LIST_TRAVERSE(&agents, cur_agent, list) {
2292 ast_mutex_lock(&cur_agent->lock);
2293 if (strcmp(agent_num, cur_agent->agent) == 0)
2295 ast_mutex_unlock(&cur_agent->lock);
2298 ast_db_del(pa_family, agent_num);
2301 ast_mutex_unlock(&cur_agent->lock);
2302 if (!ast_db_get(pa_family, agent_num, agent_data, sizeof(agent_data)-1)) {
2304 ast_log(LOG_DEBUG, "Reload Agent from AstDB: %s on %s\n", cur_agent->agent, agent_data);
2306 agent_chan = strsep(&parse, ";");
2307 agent_callerid = strsep(&parse, ";");
2308 ast_copy_string(cur_agent->loginchan, agent_chan, sizeof(cur_agent->loginchan));
2309 if (agent_callerid) {
2310 ast_copy_string(cur_agent->logincallerid, agent_callerid, sizeof(cur_agent->logincallerid));
2311 set_agentbycallerid(cur_agent->logincallerid, cur_agent->agent);
2313 cur_agent->logincallerid[0] = '\0';
2314 if (cur_agent->loginstart == 0)
2315 time(&cur_agent->loginstart);
2316 ast_device_state_changed("Agent/%s", cur_agent->agent);
2319 AST_LIST_UNLOCK(&agents);
2321 ast_log(LOG_NOTICE, "Agents successfully reloaded from database.\n");
2322 ast_db_freetree(db_tree);
2326 /*! \brief Part of PBX channel interface */
2327 static int agent_devicestate(void *data)
2329 struct agent_pvt *p;
2331 ast_group_t groupmatch;
2334 int res = AST_DEVICE_INVALID;
2337 if ((s[0] == '@') && (sscanf(s + 1, "%d", &groupoff) == 1))
2338 groupmatch = (1 << groupoff);
2339 else if ((s[0] == ':') && (sscanf(s + 1, "%d", &groupoff) == 1)) {
2340 groupmatch = (1 << groupoff);
2345 /* Check actual logged in agents first */
2346 AST_LIST_LOCK(&agents);
2347 AST_LIST_TRAVERSE(&agents, p, list) {
2348 ast_mutex_lock(&p->lock);
2349 if (!p->pending && ((groupmatch && (p->group & groupmatch)) || !strcmp(data, p->agent))) {
2351 if (res != AST_DEVICE_INUSE)
2352 res = AST_DEVICE_BUSY;
2354 if (res == AST_DEVICE_BUSY)
2355 res = AST_DEVICE_INUSE;
2356 if (p->chan || !ast_strlen_zero(p->loginchan)) {
2357 if (res == AST_DEVICE_INVALID)
2358 res = AST_DEVICE_UNKNOWN;
2359 } else if (res == AST_DEVICE_INVALID)
2360 res = AST_DEVICE_UNAVAILABLE;
2362 if (!strcmp(data, p->agent)) {
2363 ast_mutex_unlock(&p->lock);
2367 ast_mutex_unlock(&p->lock);
2369 AST_LIST_UNLOCK(&agents);
2373 static struct agent_pvt *find_agent(char *agentid)
2375 struct agent_pvt *cur;
2377 AST_LIST_TRAVERSE(&agents, cur, list) {
2378 if (!strcmp(cur->agent, agentid))
2385 static int function_agent(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
2388 AST_DECLARE_APP_ARGS(args,
2389 AST_APP_ARG(agentid);
2393 struct agent_pvt *agent;
2397 if (ast_strlen_zero(data)) {
2398 ast_log(LOG_WARNING, "The AGENT function requires an argument - agentid!\n");
2402 parse = ast_strdupa(data);
2404 AST_NONSTANDARD_APP_ARGS(args, parse, ':');
2406 args.item = "status";
2408 if (!(agent = find_agent(args.agentid))) {
2409 ast_log(LOG_WARNING, "Agent '%s' not found!\n", args.agentid);
2413 if (!strcasecmp(args.item, "status")) {
2414 char *status = "LOGGEDOUT";
2415 if (agent->chan || !ast_strlen_zero(agent->loginchan))
2416 status = "LOGGEDIN";
2417 ast_copy_string(buf, status, len);
2418 } else if (!strcasecmp(args.item, "password"))
2419 ast_copy_string(buf, agent->password, len);
2420 else if (!strcasecmp(args.item, "name"))
2421 ast_copy_string(buf, agent->name, len);
2422 else if (!strcasecmp(args.item, "mohclass"))
2423 ast_copy_string(buf, agent->moh, len);
2424 else if (!strcasecmp(args.item, "channel")) {
2426 ast_copy_string(buf, agent->chan->name, len);
2427 tmp = strrchr(buf, '-');
2431 } else if (!strcasecmp(args.item, "exten"))
2432 ast_copy_string(buf, agent->loginchan, len);
2437 struct ast_custom_function agent_function = {
2439 .synopsis = "Gets information about an Agent",
2440 .syntax = "AGENT(<agentid>[:item])",
2441 .read = function_agent,
2442 .desc = "The valid items to retrieve are:\n"
2443 "- status (default) The status of the agent\n"
2444 " LOGGEDIN | LOGGEDOUT\n"
2445 "- password The password of the agent\n"
2446 "- name The name of the agent\n"
2447 "- mohclass MusicOnHold class\n"
2448 "- exten The callback extension for the Agent (AgentCallbackLogin)\n"
2449 "- channel The name of the active channel for the Agent (AgentLogin)\n"
2454 * \brief Initialize the Agents module.
2455 * This function is being called by Asterisk when loading the module.
2456 * Among other things it registers applications, cli commands and reads the cofiguration file.
2458 * \returns int Always 0.
2460 static int load_module(void)
2462 /* Make sure we can register our agent channel type */
2463 if (ast_channel_register(&agent_tech)) {
2464 ast_log(LOG_ERROR, "Unable to register channel class 'Agent'\n");
2467 /* Read in the config */
2468 if (!read_agent_config())
2469 return AST_MODULE_LOAD_DECLINE;
2470 if (persistent_agents)
2472 /* Dialplan applications */
2473 ast_register_application(app, login_exec, synopsis, descrip);
2474 ast_register_application(app3, agentmonitoroutgoing_exec, synopsis3, descrip3);
2476 /* Manager commands */
2477 ast_manager_register2("Agents", EVENT_FLAG_AGENT, action_agents, "Lists agents and their status", mandescr_agents);
2478 ast_manager_register2("AgentLogoff", EVENT_FLAG_AGENT, action_agent_logoff, "Sets an agent as no longer logged in", mandescr_agent_logoff);
2481 ast_cli_register_multiple(cli_agents, sizeof(cli_agents) / sizeof(struct ast_cli_entry));
2483 /* Dialplan Functions */
2484 ast_custom_function_register(&agent_function);
2489 static int reload(void)
2491 read_agent_config();
2492 if (persistent_agents)
2497 static int unload_module(void)
2499 struct agent_pvt *p;
2500 /* First, take us out of the channel loop */
2501 ast_channel_unregister(&agent_tech);
2502 /* Unregister dialplan functions */
2503 ast_custom_function_unregister(&agent_function);
2504 /* Unregister CLI commands */
2505 ast_cli_unregister_multiple(cli_agents, sizeof(cli_agents) / sizeof(struct ast_cli_entry));
2506 /* Unregister dialplan applications */
2507 ast_unregister_application(app);
2508 ast_unregister_application(app3);
2509 /* Unregister manager command */
2510 ast_manager_unregister("Agents");
2511 ast_manager_unregister("AgentLogoff");
2512 /* Unregister channel */
2513 AST_LIST_LOCK(&agents);
2514 /* Hangup all interfaces if they have an owner */
2515 while ((p = AST_LIST_REMOVE_HEAD(&agents, list))) {
2517 ast_softhangup(p->owner, AST_SOFTHANGUP_APPUNLOAD);
2520 AST_LIST_UNLOCK(&agents);
2521 AST_LIST_HEAD_DESTROY(&agents);
2525 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Agent Proxy Channel",
2526 .load = load_module,
2527 .unload = unload_module,