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
34 <depend>chan_local</depend>
39 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
41 #include <sys/socket.h>
44 #include <netinet/in.h>
45 #include <arpa/inet.h>
46 #include <sys/signal.h>
48 #include "asterisk/lock.h"
49 #include "asterisk/channel.h"
50 #include "asterisk/config.h"
51 #include "asterisk/module.h"
52 #include "asterisk/pbx.h"
53 #include "asterisk/sched.h"
54 #include "asterisk/io.h"
55 #include "asterisk/rtp.h"
56 #include "asterisk/acl.h"
57 #include "asterisk/callerid.h"
58 #include "asterisk/file.h"
59 #include "asterisk/cli.h"
60 #include "asterisk/app.h"
61 #include "asterisk/musiconhold.h"
62 #include "asterisk/manager.h"
63 #include "asterisk/features.h"
64 #include "asterisk/utils.h"
65 #include "asterisk/causes.h"
66 #include "asterisk/astdb.h"
67 #include "asterisk/devicestate.h"
68 #include "asterisk/monitor.h"
69 #include "asterisk/stringfields.h"
71 static const char tdesc[] = "Call Agent Proxy Channel";
72 static const char config[] = "agents.conf";
74 static const char app[] = "AgentLogin";
75 static const char app3[] = "AgentMonitorOutgoing";
77 static const char synopsis[] = "Call agent login";
78 static const char synopsis3[] = "Record agent's outgoing call";
80 static const char descrip[] =
81 " AgentLogin([AgentNo][,options]):\n"
82 "Asks the agent to login to the system. Always returns -1. While\n"
83 "logged in, the agent can receive calls and will hear a 'beep'\n"
84 "when a new call comes in. The agent can dump the call by pressing\n"
86 "The option string may contain zero or more of the following characters:\n"
87 " 's' -- silent login - do not announce the login ok segment after agent logged on/off\n";
89 static const char descrip3[] =
90 " AgentMonitorOutgoing([options]):\n"
91 "Tries to figure out the id of the agent who is placing outgoing call based on\n"
92 "comparison of the callerid of the current interface and the global variable \n"
93 "placed by the AgentCallbackLogin application. That's why it should be used only\n"
94 "with the AgentCallbackLogin app. Uses the monitoring functions in chan_agent \n"
95 "instead of Monitor application. That has to be configured in the agents.conf file.\n"
97 "Normally the app returns 0 unless the options are passed.\n"
99 " 'd' - make the app return -1 if there is an error condition\n"
100 " 'c' - change the CDR so that the source of the call is 'Agent/agent_id'\n"
101 " 'n' - don't generate the warnings when there is no callerid or the\n"
102 " agentid is not known.\n"
103 " It's handy if you want to have one context for agent and non-agent calls.\n";
105 static const char mandescr_agents[] =
106 "Description: Will list info about all possible agents.\n"
109 static const char mandescr_agent_logoff[] =
110 "Description: Sets an agent as no longer logged in.\n"
111 "Variables: (Names marked with * are required)\n"
112 " *Agent: Agent ID of the agent to log off\n"
113 " Soft: Set to 'true' to not hangup existing calls\n";
115 static char moh[80] = "default";
117 #define AST_MAX_AGENT 80 /*!< Agent ID or Password max length */
118 #define AST_MAX_BUF 256
119 #define AST_MAX_FILENAME_LEN 256
121 static const char pa_family[] = "Agents"; /*!< Persistent Agents astdb family */
122 #define PA_MAX_LEN 2048 /*!< The maximum length of each persistent member agent database entry */
124 static int persistent_agents = 0; /*!< queues.conf [general] option */
125 static void dump_agents(void);
127 #define DEFAULT_ACCEPTDTMF '#'
128 #define DEFAULT_ENDDTMF '*'
130 static ast_group_t group;
131 static int autologoff;
132 static int wrapuptime;
135 static int multiplelogin = 1;
136 static int autologoffunavail = 0;
137 static char acceptdtmf = DEFAULT_ACCEPTDTMF;
138 static char enddtmf = DEFAULT_ENDDTMF;
140 static int maxlogintries = 3;
141 static char agentgoodbye[AST_MAX_FILENAME_LEN] = "vm-goodbye";
143 static int recordagentcalls = 0;
144 static char recordformat[AST_MAX_BUF] = "";
145 static char recordformatext[AST_MAX_BUF] = "";
146 static char urlprefix[AST_MAX_BUF] = "";
147 static char savecallsin[AST_MAX_BUF] = "";
148 static int updatecdr = 0;
149 static char beep[AST_MAX_BUF] = "beep";
151 #define GETAGENTBYCALLERID "AGENTBYCALLERID"
153 /*! \brief Structure representing an agent. */
155 ast_mutex_t lock; /*!< Channel private lock */
156 int dead; /*!< Poised for destruction? */
157 int pending; /*!< Not a real agent -- just pending a match */
158 int abouttograb; /*!< About to grab */
159 int autologoff; /*!< Auto timeout time */
160 int ackcall; /*!< ackcall */
161 int deferlogoff; /*!< Defer logoff to hangup */
164 time_t loginstart; /*!< When agent first logged in (0 when logged off) */
165 time_t start; /*!< When call started */
166 struct timeval lastdisc; /*!< When last disconnected */
167 int wrapuptime; /*!< Wrapup time in ms */
168 ast_group_t group; /*!< Group memberships */
169 int acknowledged; /*!< Acknowledged */
170 char moh[80]; /*!< Which music on hold */
171 char agent[AST_MAX_AGENT]; /*!< Agent ID */
172 char password[AST_MAX_AGENT]; /*!< Password for Agent login */
173 char name[AST_MAX_AGENT];
174 ast_mutex_t app_lock; /**< Synchronization between owning applications */
175 volatile pthread_t owning_app; /**< Owning application thread id */
176 volatile int app_sleep_cond; /**< Sleep condition for the login app */
177 struct ast_channel *owner; /**< Agent */
178 char loginchan[80]; /**< channel they logged in from */
179 char logincallerid[80]; /**< Caller ID they had when they logged in */
180 struct ast_channel *chan; /**< Channel we use */
181 AST_LIST_ENTRY(agent_pvt) list; /**< Next Agent in the linked list. */
184 static AST_LIST_HEAD_STATIC(agents, agent_pvt); /*!< Holds the list of agents (loaded form agents.conf). */
186 #define CHECK_FORMATS(ast, p) do { \
188 if (ast->nativeformats != p->chan->nativeformats) { \
189 ast_debug(1, "Native formats changing from %d to %d\n", ast->nativeformats, p->chan->nativeformats); \
190 /* Native formats changed, reset things */ \
191 ast->nativeformats = p->chan->nativeformats; \
192 ast_debug(1, "Resetting read to %d and write to %d\n", ast->readformat, ast->writeformat);\
193 ast_set_read_format(ast, ast->readformat); \
194 ast_set_write_format(ast, ast->writeformat); \
196 if (p->chan->readformat != ast->rawreadformat && !p->chan->generator) \
197 ast_set_read_format(p->chan, ast->rawreadformat); \
198 if (p->chan->writeformat != ast->rawwriteformat && !p->chan->generator) \
199 ast_set_write_format(p->chan, ast->rawwriteformat); \
203 /*! \brief Cleanup moves all the relevant FD's from the 2nd to the first, but retains things
204 properly for a timingfd XXX This might need more work if agents were logged in as agents or other
205 totally impractical combinations XXX */
207 #define CLEANUP(ast, p) do { \
210 for (x=0;x<AST_MAX_FDS;x++) {\
211 if (x != AST_TIMING_FD) \
212 ast_channel_set_fd(ast, x, p->chan->fds[x]); \
214 ast_channel_set_fd(ast, AST_AGENT_FD, p->chan->fds[AST_TIMING_FD]); \
218 /*--- Forward declarations */
219 static struct ast_channel *agent_request(const char *type, int format, void *data, int *cause);
220 static int agent_devicestate(void *data);
221 static void agent_logoff_maintenance(struct agent_pvt *p, char *loginchan, long logintime, const char *uniqueid, char *logcommand);
222 static int agent_digit_begin(struct ast_channel *ast, char digit);
223 static int agent_digit_end(struct ast_channel *ast, char digit, unsigned int duration);
224 static int agent_call(struct ast_channel *ast, char *dest, int timeout);
225 static int agent_hangup(struct ast_channel *ast);
226 static int agent_answer(struct ast_channel *ast);
227 static struct ast_frame *agent_read(struct ast_channel *ast);
228 static int agent_write(struct ast_channel *ast, struct ast_frame *f);
229 static int agent_sendhtml(struct ast_channel *ast, int subclass, const char *data, int datalen);
230 static int agent_sendtext(struct ast_channel *ast, const char *text);
231 static int agent_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen);
232 static int agent_fixup(struct ast_channel *oldchan, struct ast_channel *newchan);
233 static struct ast_channel *agent_bridgedchannel(struct ast_channel *chan, struct ast_channel *bridge);
234 static void set_agentbycallerid(const char *callerid, const char *agent);
235 static char *complete_agent_logoff_cmd(const char *line, const char *word, int pos, int state);
236 static struct ast_channel* agent_get_base_channel(struct ast_channel *chan);
237 static int agent_set_base_channel(struct ast_channel *chan, struct ast_channel *base);
239 /*! \brief Channel interface description for PBX integration */
240 static const struct ast_channel_tech agent_tech = {
242 .description = tdesc,
244 .requester = agent_request,
245 .devicestate = agent_devicestate,
246 .send_digit_begin = agent_digit_begin,
247 .send_digit_end = agent_digit_end,
249 .hangup = agent_hangup,
250 .answer = agent_answer,
252 .write = agent_write,
253 .write_video = agent_write,
254 .send_html = agent_sendhtml,
255 .send_text = agent_sendtext,
256 .exception = agent_read,
257 .indicate = agent_indicate,
258 .fixup = agent_fixup,
259 .bridged_channel = agent_bridgedchannel,
260 .get_base_channel = agent_get_base_channel,
261 .set_base_channel = agent_set_base_channel,
265 * Adds an agent to the global list of agents.
267 * \param agent A string with the username, password and real name of an agent. As defined in agents.conf. Example: "13,169,John Smith"
268 * \param pending If it is pending or not.
269 * @return The just created agent.
270 * \sa agent_pvt, agents.
272 static struct agent_pvt *add_agent(const char *agent, int pending)
275 AST_DECLARE_APP_ARGS(args,
277 AST_APP_ARG(password);
280 char *password = NULL;
285 parse = ast_strdupa(agent);
287 /* Extract username (agt), password and name from agent (args). */
288 AST_STANDARD_APP_ARGS(args, parse);
291 ast_log(LOG_WARNING, "A blank agent line!\n");
295 if(ast_strlen_zero(args.agt) ) {
296 ast_log(LOG_WARNING, "An agent line with no agentid!\n");
301 if(!ast_strlen_zero(args.password)) {
302 password = args.password;
303 while (*password && *password < 33) password++;
305 if(!ast_strlen_zero(args.name)) {
307 while (*name && *name < 33) name++;
310 /* Are we searching for the agent here ? To see if it exists already ? */
311 AST_LIST_TRAVERSE(&agents, p, list) {
312 if (!pending && !strcmp(p->agent, agt))
317 if (!(p = ast_calloc(1, sizeof(*p))))
319 ast_copy_string(p->agent, agt, sizeof(p->agent));
320 ast_mutex_init(&p->lock);
321 ast_mutex_init(&p->app_lock);
322 p->owning_app = (pthread_t) -1;
323 p->app_sleep_cond = 1;
325 p->pending = pending;
326 AST_LIST_INSERT_TAIL(&agents, p, list);
329 ast_copy_string(p->password, password ? password : "", sizeof(p->password));
330 ast_copy_string(p->name, name ? name : "", sizeof(p->name));
331 ast_copy_string(p->moh, moh, sizeof(p->moh));
332 p->ackcall = ackcall;
333 p->autologoff = autologoff;
334 p->acceptdtmf = acceptdtmf;
335 p->enddtmf = enddtmf;
337 /* If someone reduces the wrapuptime and reloads, we want it
338 * to change the wrapuptime immediately on all calls */
339 if (p->wrapuptime > wrapuptime) {
340 struct timeval now = ast_tvnow();
341 /* XXX check what is this exactly */
343 /* We won't be pedantic and check the tv_usec val */
344 if (p->lastdisc.tv_sec > (now.tv_sec + wrapuptime/1000)) {
345 p->lastdisc.tv_sec = now.tv_sec + wrapuptime/1000;
346 p->lastdisc.tv_usec = now.tv_usec;
349 p->wrapuptime = wrapuptime;
359 * Deletes an agent after doing some clean up.
360 * Further documentation: How safe is this function ? What state should the agent be to be cleaned.
361 * \param p Agent to be deleted.
364 static int agent_cleanup(struct agent_pvt *p)
366 struct ast_channel *chan = p->owner;
368 chan->tech_pvt = NULL;
369 p->app_sleep_cond = 1;
370 /* Release ownership of the agent to other threads (presumably running the login app). */
371 ast_mutex_unlock(&p->app_lock);
373 ast_channel_free(chan);
375 ast_mutex_destroy(&p->lock);
376 ast_mutex_destroy(&p->app_lock);
382 static int check_availability(struct agent_pvt *newlyavailable, int needlock);
384 static int agent_answer(struct ast_channel *ast)
386 ast_log(LOG_WARNING, "Huh? Agent is being asked to answer?\n");
390 static int __agent_start_monitoring(struct ast_channel *ast, struct agent_pvt *p, int needlock)
392 char tmp[AST_MAX_BUF],tmp2[AST_MAX_BUF], *pointer;
393 char filename[AST_MAX_BUF];
398 snprintf(filename, sizeof(filename), "agent-%s-%s",p->agent, ast->uniqueid);
399 /* substitute . for - */
400 if ((pointer = strchr(filename, '.')))
402 snprintf(tmp, sizeof(tmp), "%s%s", savecallsin, filename);
403 ast_monitor_start(ast, recordformat, tmp, needlock, X_REC_IN | X_REC_OUT);
404 ast_monitor_setjoinfiles(ast, 1);
405 snprintf(tmp2, sizeof(tmp2), "%s%s.%s", urlprefix, filename, recordformatext);
407 ast_verbose("name is %s, link is %s\n",tmp, tmp2);
410 ast->cdr = ast_cdr_alloc();
411 ast_cdr_setuserfield(ast, tmp2);
414 ast_log(LOG_ERROR, "Recording already started on that call.\n");
418 static int agent_start_monitoring(struct ast_channel *ast, int needlock)
420 return __agent_start_monitoring(ast, ast->tech_pvt, needlock);
423 static struct ast_frame *agent_read(struct ast_channel *ast)
425 struct agent_pvt *p = ast->tech_pvt;
426 struct ast_frame *f = NULL;
427 static struct ast_frame answer_frame = { AST_FRAME_CONTROL, AST_CONTROL_ANSWER };
429 ast_mutex_lock(&p->lock);
430 CHECK_FORMATS(ast, p);
432 ast_copy_flags(p->chan, ast, AST_FLAG_EXCEPTION);
433 p->chan->fdno = (ast->fdno == AST_AGENT_FD) ? AST_TIMING_FD : ast->fdno;
434 f = ast_read(p->chan);
438 /* If there's a channel, hang it up (if it's on a callback) make it NULL */
440 p->chan->_bridge = NULL;
441 /* Note that we don't hangup if it's not a callback because Asterisk will do it
442 for us when the PBX instance that called login finishes */
443 if (!ast_strlen_zero(p->loginchan)) {
445 ast_debug(1, "Bridge on '%s' being cleared (2)\n", p->chan->name);
446 if (p->owner->_state != AST_STATE_UP) {
447 int howlong = time(NULL) - p->start;
448 if (p->autologoff && howlong > p->autologoff) {
449 long logintime = time(NULL) - p->loginstart;
451 ast_log(LOG_NOTICE, "Agent '%s' didn't answer/confirm within %d seconds (waited %d)\n", p->name, p->autologoff, howlong);
452 agent_logoff_maintenance(p, p->loginchan, logintime, ast->uniqueid, "Autologoff");
453 if (persistent_agents)
457 status = pbx_builtin_getvar_helper(p->chan, "CHANLOCALSTATUS");
458 if (autologoffunavail && status && !strcasecmp(status, "CHANUNAVAIL")) {
459 long logintime = time(NULL) - p->loginstart;
461 ast_log(LOG_NOTICE, "Agent read: '%s' is not available now, auto logoff\n", p->name);
462 agent_logoff_maintenance(p, p->loginchan, logintime, ast->uniqueid, "Chanunavail");
465 if (p->wrapuptime && p->acknowledged)
466 p->lastdisc = ast_tvadd(ast_tvnow(), ast_samp2tv(p->wrapuptime, 1000));
472 /* if acknowledgement is not required, and the channel is up, we may have missed
473 an AST_CONTROL_ANSWER (if there was one), so mark the call acknowledged anyway */
474 if (!p->ackcall && !p->acknowledged && p->chan && (p->chan->_state == AST_STATE_UP))
476 switch (f->frametype) {
477 case AST_FRAME_CONTROL:
478 if (f->subclass == AST_CONTROL_ANSWER) {
480 ast_verb(3, "%s answered, waiting for '%c' to acknowledge\n", p->chan->name, p->acceptdtmf);
481 /* Don't pass answer along */
486 /* Use the builtin answer frame for the
487 recording start check below. */
493 case AST_FRAME_DTMF_BEGIN:
494 /*ignore DTMF begin's as it can cause issues with queue announce files*/
495 if((!p->acknowledged && f->subclass == p->acceptdtmf) || (f->subclass == p->enddtmf && endcall)){
500 case AST_FRAME_DTMF_END:
501 if (!p->acknowledged && (f->subclass == p->acceptdtmf)) {
502 ast_verb(3, "%s acknowledged\n", p->chan->name);
506 } else if (f->subclass == p->enddtmf && endcall) {
507 /* terminates call */
512 case AST_FRAME_VOICE:
513 case AST_FRAME_VIDEO:
514 /* don't pass voice or video until the call is acknowledged */
515 if (!p->acknowledged) {
520 /* pass everything else on through */
526 if (p->chan && !p->chan->_bridge) {
527 if (strcasecmp(p->chan->tech->type, "Local")) {
528 p->chan->_bridge = ast;
530 ast_debug(1, "Bridge on '%s' being set to '%s' (3)\n", p->chan->name, p->chan->_bridge->name);
533 ast_mutex_unlock(&p->lock);
534 if (recordagentcalls && f == &answer_frame)
535 agent_start_monitoring(ast,0);
539 static int agent_sendhtml(struct ast_channel *ast, int subclass, const char *data, int datalen)
541 struct agent_pvt *p = ast->tech_pvt;
543 ast_mutex_lock(&p->lock);
545 res = ast_channel_sendhtml(p->chan, subclass, data, datalen);
546 ast_mutex_unlock(&p->lock);
550 static int agent_sendtext(struct ast_channel *ast, const char *text)
552 struct agent_pvt *p = ast->tech_pvt;
554 ast_mutex_lock(&p->lock);
556 res = ast_sendtext(p->chan, text);
557 ast_mutex_unlock(&p->lock);
561 static int agent_write(struct ast_channel *ast, struct ast_frame *f)
563 struct agent_pvt *p = ast->tech_pvt;
565 CHECK_FORMATS(ast, p);
566 ast_mutex_lock(&p->lock);
570 if ((f->frametype != AST_FRAME_VOICE) ||
571 (f->frametype != AST_FRAME_VIDEO) ||
572 (f->subclass == p->chan->writeformat)) {
573 res = ast_write(p->chan, f);
575 ast_debug(1, "Dropping one incompatible %s frame on '%s' to '%s'\n",
576 f->frametype == AST_FRAME_VOICE ? "audio" : "video",
577 ast->name, p->chan->name);
582 ast_mutex_unlock(&p->lock);
586 static int agent_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
588 struct agent_pvt *p = newchan->tech_pvt;
589 ast_mutex_lock(&p->lock);
590 if (p->owner != oldchan) {
591 ast_log(LOG_WARNING, "old channel wasn't %p but was %p\n", oldchan, p->owner);
592 ast_mutex_unlock(&p->lock);
596 ast_mutex_unlock(&p->lock);
600 static int agent_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen)
602 struct agent_pvt *p = ast->tech_pvt;
604 ast_mutex_lock(&p->lock);
606 res = p->chan->tech->indicate ? p->chan->tech->indicate(p->chan, condition, data, datalen) : -1;
609 ast_mutex_unlock(&p->lock);
613 static int agent_digit_begin(struct ast_channel *ast, char digit)
615 struct agent_pvt *p = ast->tech_pvt;
616 ast_mutex_lock(&p->lock);
618 ast_senddigit_begin(p->chan, digit);
620 ast_mutex_unlock(&p->lock);
624 static int agent_digit_end(struct ast_channel *ast, char digit, unsigned int duration)
626 struct agent_pvt *p = ast->tech_pvt;
627 ast_mutex_lock(&p->lock);
629 ast_senddigit_end(p->chan, digit, duration);
631 ast_mutex_unlock(&p->lock);
635 static int agent_call(struct ast_channel *ast, char *dest, int timeout)
637 struct agent_pvt *p = ast->tech_pvt;
640 ast_mutex_lock(&p->lock);
644 ast_debug(1, "Pretending to dial on pending agent\n");
645 newstate = AST_STATE_DIALING;
648 ast_log(LOG_NOTICE, "Whoa, they hung up between alloc and call... what are the odds of that?\n");
651 ast_mutex_unlock(&p->lock);
653 ast_setstate(ast, newstate);
655 } else if (!ast_strlen_zero(p->loginchan)) {
657 /* Call on this agent */
658 ast_verb(3, "outgoing agentcall, to agent '%s', on '%s'\n", p->agent, p->chan->name);
659 ast_set_callerid(p->chan,
660 ast->cid.cid_num, ast->cid.cid_name, NULL);
661 ast_channel_inherit_variables(ast, p->chan);
662 res = ast_call(p->chan, p->loginchan, 0);
664 ast_mutex_unlock(&p->lock);
667 ast_verb(3, "agent_call, call to agent '%s' call on '%s'\n", p->agent, p->chan->name);
668 ast_debug(3, "Playing beep, lang '%s'\n", p->chan->language);
669 res = ast_streamfile(p->chan, beep, p->chan->language);
670 ast_debug(3, "Played beep, result '%d'\n", res);
672 res = ast_waitstream(p->chan, "");
673 ast_debug(3, "Waited for stream, result '%d'\n", res);
676 res = ast_set_read_format(p->chan, ast_best_codec(p->chan->nativeformats));
677 ast_debug(3, "Set read format, result '%d'\n", res);
679 ast_log(LOG_WARNING, "Unable to set read format to %s\n", ast_getformatname(ast_best_codec(p->chan->nativeformats)));
686 res = ast_set_write_format(p->chan, ast_best_codec(p->chan->nativeformats));
687 ast_debug(3, "Set write format, result '%d'\n", res);
689 ast_log(LOG_WARNING, "Unable to set write format to %s\n", ast_getformatname(ast_best_codec(p->chan->nativeformats)));
692 /* Call is immediately up, or might need ack */
694 newstate = AST_STATE_RINGING;
696 newstate = AST_STATE_UP;
697 if (recordagentcalls)
698 agent_start_monitoring(ast, 0);
704 ast_mutex_unlock(&p->lock);
706 ast_setstate(ast, newstate);
710 /*! \brief store/clear the global variable that stores agentid based on the callerid */
711 static void set_agentbycallerid(const char *callerid, const char *agent)
713 char buf[AST_MAX_BUF];
715 /* if there is no Caller ID, nothing to do */
716 if (ast_strlen_zero(callerid))
719 snprintf(buf, sizeof(buf), "%s_%s", GETAGENTBYCALLERID, callerid);
720 pbx_builtin_setvar_helper(NULL, buf, agent);
723 /*! \brief return the channel or base channel if one exists. This function assumes the channel it is called on is already locked */
724 struct ast_channel* agent_get_base_channel(struct ast_channel *chan)
726 struct agent_pvt *p = NULL;
727 struct ast_channel *base = chan;
729 /* chan is locked by the calling function */
730 if (!chan || !chan->tech_pvt) {
731 ast_log(LOG_ERROR, "whoa, you need a channel (0x%ld) with a tech_pvt (0x%ld) to get a base channel.\n", (long)chan, (chan)?(long)chan->tech_pvt:(long)NULL);
740 int agent_set_base_channel(struct ast_channel *chan, struct ast_channel *base)
742 struct agent_pvt *p = NULL;
744 if (!chan || !base) {
745 ast_log(LOG_ERROR, "whoa, you need a channel (0x%ld) and a base channel (0x%ld) for setting.\n", (long)chan, (long)base);
750 ast_log(LOG_ERROR, "whoa, channel %s is missing his tech_pvt structure!!.\n", chan->name);
757 static int agent_hangup(struct ast_channel *ast)
759 struct agent_pvt *p = ast->tech_pvt;
762 ast_mutex_lock(&p->lock);
764 ast->tech_pvt = NULL;
765 p->app_sleep_cond = 1;
768 /* if they really are hung up then set start to 0 so the test
769 * later if we're called on an already downed channel
770 * doesn't cause an agent to be logged out like when
771 * agent_request() is followed immediately by agent_hangup()
772 * as in apps/app_chanisavail.c:chanavail_exec()
775 ast_debug(1, "Hangup called for state %s\n", ast_state2str(ast->_state));
776 if (p->start && (ast->_state != AST_STATE_UP)) {
777 howlong = time(NULL) - p->start;
779 } else if (ast->_state == AST_STATE_RESERVED)
784 p->chan->_bridge = NULL;
785 /* If they're dead, go ahead and hang up on the agent now */
786 if (!ast_strlen_zero(p->loginchan)) {
787 /* Store last disconnect time */
789 p->lastdisc = ast_tvadd(ast_tvnow(), ast_samp2tv(p->wrapuptime, 1000));
791 p->lastdisc = ast_tv(0,0);
793 status = pbx_builtin_getvar_helper(p->chan, "CHANLOCALSTATUS");
794 if (autologoffunavail && status && !strcasecmp(status, "CHANUNAVAIL")) {
795 long logintime = time(NULL) - p->loginstart;
797 ast_log(LOG_NOTICE, "Agent hangup: '%s' is not available now, auto logoff\n", p->name);
798 agent_logoff_maintenance(p, p->loginchan, logintime, ast->uniqueid, "Chanunavail");
800 /* Recognize the hangup and pass it along immediately */
804 ast_debug(1, "Hungup, howlong is %d, autologoff is %d\n", howlong, p->autologoff);
805 if ((p->deferlogoff) || (howlong && p->autologoff && (howlong > p->autologoff))) {
806 long logintime = time(NULL) - p->loginstart;
809 ast_log(LOG_NOTICE, "Agent '%s' didn't answer/confirm within %d seconds (waited %d)\n", p->name, p->autologoff, howlong);
811 agent_logoff_maintenance(p, p->loginchan, logintime, ast->uniqueid, "Autologoff");
812 if (persistent_agents)
815 } else if (p->dead) {
816 ast_channel_lock(p->chan);
817 ast_softhangup(p->chan, AST_SOFTHANGUP_EXPLICIT);
818 ast_channel_unlock(p->chan);
819 } else if (p->loginstart) {
820 ast_channel_lock(p->chan);
821 ast_indicate_data(p->chan, AST_CONTROL_HOLD,
823 !ast_strlen_zero(p->moh) ? strlen(p->moh) + 1 : 0);
824 ast_channel_unlock(p->chan);
827 ast_mutex_unlock(&p->lock);
829 /* Only register a device state change if the agent is still logged in */
830 if (!p->loginstart) {
831 p->loginchan[0] = '\0';
832 p->logincallerid[0] = '\0';
833 if (persistent_agents)
836 ast_device_state_changed("Agent/%s", p->agent);
840 AST_LIST_LOCK(&agents);
841 AST_LIST_REMOVE(&agents, p, list);
842 AST_LIST_UNLOCK(&agents);
844 if (p->abouttograb) {
845 /* Let the "about to grab" thread know this isn't valid anymore, and let it
848 } else if (p->dead) {
849 ast_mutex_destroy(&p->lock);
850 ast_mutex_destroy(&p->app_lock);
854 /* Not dead -- check availability now */
855 ast_mutex_lock(&p->lock);
856 /* Store last disconnect time */
857 p->lastdisc = ast_tvadd(ast_tvnow(), ast_samp2tv(p->wrapuptime, 1000));
858 ast_mutex_unlock(&p->lock);
860 /* Release ownership of the agent to other threads (presumably running the login app). */
861 if (ast_strlen_zero(p->loginchan))
862 ast_mutex_unlock(&p->app_lock);
867 static int agent_cont_sleep( void *data )
872 p = (struct agent_pvt *)data;
874 ast_mutex_lock(&p->lock);
875 res = p->app_sleep_cond;
876 if (p->lastdisc.tv_sec) {
877 if (ast_tvdiff_ms(ast_tvnow(), p->lastdisc) > 0)
880 ast_mutex_unlock(&p->lock);
883 ast_debug(5, "agent_cont_sleep() returning %d\n", res );
888 static int agent_ack_sleep(void *data)
895 /* Wait a second and look for something */
897 p = (struct agent_pvt *) data;
902 to = ast_waitfor(p->chan, to);
907 f = ast_read(p->chan);
910 if (f->frametype == AST_FRAME_DTMF)
915 ast_mutex_lock(&p->lock);
916 if (!p->app_sleep_cond) {
917 ast_mutex_unlock(&p->lock);
919 } else if (res == p->acceptdtmf) {
920 ast_mutex_unlock(&p->lock);
923 ast_mutex_unlock(&p->lock);
929 static struct ast_channel *agent_bridgedchannel(struct ast_channel *chan, struct ast_channel *bridge)
931 struct agent_pvt *p = bridge->tech_pvt;
932 struct ast_channel *ret = NULL;
936 ret = bridge->_bridge;
937 else if (chan == bridge->_bridge)
941 ast_debug(1, "Asked for bridged channel on '%s'/'%s', returning '%s'\n", chan->name, bridge->name, ret ? ret->name : "<none>");
945 /*! \brief Create new agent channel */
946 static struct ast_channel *agent_new(struct agent_pvt *p, int state)
948 struct ast_channel *tmp;
951 ast_log(LOG_WARNING, "No channel? :(\n");
956 tmp = ast_channel_alloc(0, state, 0, 0, "", p->chan ? p->chan->exten:"", p->chan ? p->chan->context:"", 0, "Agent/P%s-%d", p->agent, ast_random() & 0xffff);
958 tmp = ast_channel_alloc(0, state, 0, 0, "", p->chan ? p->chan->exten:"", p->chan ? p->chan->context:"", 0, "Agent/%s", p->agent);
960 ast_log(LOG_WARNING, "Unable to allocate agent channel structure\n");
964 tmp->tech = &agent_tech;
966 tmp->nativeformats = p->chan->nativeformats;
967 tmp->writeformat = p->chan->writeformat;
968 tmp->rawwriteformat = p->chan->writeformat;
969 tmp->readformat = p->chan->readformat;
970 tmp->rawreadformat = p->chan->readformat;
971 ast_string_field_set(tmp, language, p->chan->language);
972 ast_copy_string(tmp->context, p->chan->context, sizeof(tmp->context));
973 ast_copy_string(tmp->exten, p->chan->exten, sizeof(tmp->exten));
974 /* XXX Is this really all we copy form the originating channel?? */
976 tmp->nativeformats = AST_FORMAT_SLINEAR;
977 tmp->writeformat = AST_FORMAT_SLINEAR;
978 tmp->rawwriteformat = AST_FORMAT_SLINEAR;
979 tmp->readformat = AST_FORMAT_SLINEAR;
980 tmp->rawreadformat = AST_FORMAT_SLINEAR;
982 /* Safe, agentlock already held */
986 /* Wake up and wait for other applications (by definition the login app)
987 * to release this channel). Takes ownership of the agent channel
988 * to this thread only.
989 * For signalling the other thread, ast_queue_frame is used until we
990 * can safely use signals for this purpose. The pselect() needs to be
991 * implemented in the kernel for this.
993 p->app_sleep_cond = 0;
994 if(ast_strlen_zero(p->loginchan) && ast_mutex_trylock(&p->app_lock)) {
996 ast_queue_frame(p->chan, &ast_null_frame);
997 ast_mutex_unlock(&p->lock); /* For other thread to read the condition. */
998 ast_mutex_lock(&p->app_lock);
999 ast_mutex_lock(&p->lock);
1001 ast_log(LOG_WARNING, "Agent disconnected while we were connecting the call\n");
1003 tmp->tech_pvt = NULL;
1004 p->app_sleep_cond = 1;
1005 ast_channel_free( tmp );
1006 ast_mutex_unlock(&p->lock); /* For other thread to read the condition. */
1007 ast_mutex_unlock(&p->app_lock);
1010 } else if (!ast_strlen_zero(p->loginchan)) {
1012 ast_queue_frame(p->chan, &ast_null_frame);
1014 ast_log(LOG_WARNING, "Agent disconnected while we were connecting the call\n");
1016 tmp->tech_pvt = NULL;
1017 p->app_sleep_cond = 1;
1018 ast_channel_free( tmp );
1019 ast_mutex_unlock(&p->lock); /* For other thread to read the condition. */
1024 ast_indicate(p->chan, AST_CONTROL_UNHOLD);
1025 p->owning_app = pthread_self();
1026 /* After the above step, there should not be any blockers. */
1028 if (ast_test_flag(p->chan, AST_FLAG_BLOCKING)) {
1029 ast_log( LOG_ERROR, "A blocker exists after agent channel ownership acquired\n" );
1030 ast_assert(ast_test_flag(p->chan, AST_FLAG_BLOCKING) == 0);
1038 * Read configuration data. The file named agents.conf.
1040 * \returns Always 0, or so it seems.
1042 static int read_agent_config(int reload)
1044 struct ast_config *cfg;
1045 struct ast_config *ucfg;
1046 struct ast_variable *v;
1047 struct agent_pvt *p;
1048 const char *general_val;
1049 const char *catname;
1050 const char *hasagent;
1052 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
1059 cfg = ast_config_load(config, config_flags);
1061 ast_log(LOG_NOTICE, "No agent configuration found -- agent support disabled\n");
1063 } else if (cfg == CONFIG_STATUS_FILEUNCHANGED)
1065 AST_LIST_LOCK(&agents);
1066 AST_LIST_TRAVERSE(&agents, p, list) {
1069 strcpy(moh, "default");
1070 /* set the default recording values */
1071 recordagentcalls = 0;
1072 strcpy(recordformat, "wav");
1073 strcpy(recordformatext, "wav");
1074 urlprefix[0] = '\0';
1075 savecallsin[0] = '\0';
1077 /* Read in [general] section for persistence */
1078 if ((general_val = ast_variable_retrieve(cfg, "general", "persistentagents")))
1079 persistent_agents = ast_true(general_val);
1080 multiplelogin = ast_true(ast_variable_retrieve(cfg, "general", "multiplelogin"));
1082 /* Read in the [agents] section */
1083 v = ast_variable_browse(cfg, "agents");
1085 /* Create the interface list */
1086 if (!strcasecmp(v->name, "agent")) {
1087 add_agent(v->value, 0);
1088 } else if (!strcasecmp(v->name, "group")) {
1089 group = ast_get_group(v->value);
1090 } else if (!strcasecmp(v->name, "autologoff")) {
1091 autologoff = atoi(v->value);
1094 } else if (!strcasecmp(v->name, "ackcall")) {
1095 if (!strcasecmp(v->value, "always"))
1097 else if (ast_true(v->value))
1101 } else if (!strcasecmp(v->name, "endcall")) {
1102 endcall = ast_true(v->value);
1103 } else if (!strcasecmp(v->name, "acceptdtmf")) {
1104 acceptdtmf = *(v->value);
1105 ast_log(LOG_NOTICE, "Set acceptdtmf to %c\n", acceptdtmf);
1106 } else if (!strcasecmp(v->name, "enddtmf")) {
1107 enddtmf = *(v->value);
1108 } else if (!strcasecmp(v->name, "wrapuptime")) {
1109 wrapuptime = atoi(v->value);
1112 } else if (!strcasecmp(v->name, "maxlogintries") && !ast_strlen_zero(v->value)) {
1113 maxlogintries = atoi(v->value);
1114 if (maxlogintries < 0)
1116 } else if (!strcasecmp(v->name, "goodbye") && !ast_strlen_zero(v->value)) {
1117 strcpy(agentgoodbye,v->value);
1118 } else if (!strcasecmp(v->name, "musiconhold")) {
1119 ast_copy_string(moh, v->value, sizeof(moh));
1120 } else if (!strcasecmp(v->name, "updatecdr")) {
1121 if (ast_true(v->value))
1125 } else if (!strcasecmp(v->name, "autologoffunavail")) {
1126 if (ast_true(v->value))
1127 autologoffunavail = 1;
1129 autologoffunavail = 0;
1130 } else if (!strcasecmp(v->name, "recordagentcalls")) {
1131 recordagentcalls = ast_true(v->value);
1132 } else if (!strcasecmp(v->name, "recordformat")) {
1133 ast_copy_string(recordformat, v->value, sizeof(recordformat));
1134 if (!strcasecmp(v->value, "wav49"))
1135 strcpy(recordformatext, "WAV");
1137 ast_copy_string(recordformatext, v->value, sizeof(recordformatext));
1138 } else if (!strcasecmp(v->name, "urlprefix")) {
1139 ast_copy_string(urlprefix, v->value, sizeof(urlprefix));
1140 if (urlprefix[strlen(urlprefix) - 1] != '/')
1141 strncat(urlprefix, "/", sizeof(urlprefix) - strlen(urlprefix) - 1);
1142 } else if (!strcasecmp(v->name, "savecallsin")) {
1143 if (v->value[0] == '/')
1144 ast_copy_string(savecallsin, v->value, sizeof(savecallsin));
1146 snprintf(savecallsin, sizeof(savecallsin) - 2, "/%s", v->value);
1147 if (savecallsin[strlen(savecallsin) - 1] != '/')
1148 strncat(savecallsin, "/", sizeof(savecallsin) - strlen(savecallsin) - 1);
1149 } else if (!strcasecmp(v->name, "custom_beep")) {
1150 ast_copy_string(beep, v->value, sizeof(beep));
1154 if ((ucfg = ast_config_load("users.conf", config_flags)) && ucfg != CONFIG_STATUS_FILEUNCHANGED) {
1155 genhasagent = ast_true(ast_variable_retrieve(ucfg, "general", "hasagent"));
1156 catname = ast_category_browse(ucfg, NULL);
1158 if (strcasecmp(catname, "general")) {
1159 hasagent = ast_variable_retrieve(ucfg, catname, "hasagent");
1160 if (ast_true(hasagent) || (!hasagent && genhasagent)) {
1162 const char *fullname = ast_variable_retrieve(ucfg, catname, "fullname");
1163 const char *secret = ast_variable_retrieve(ucfg, catname, "secret");
1168 snprintf(tmp, sizeof(tmp), "%s,%s,%s", catname, secret,fullname);
1172 catname = ast_category_browse(ucfg, catname);
1174 ast_config_destroy(ucfg);
1176 AST_LIST_TRAVERSE_SAFE_BEGIN(&agents, p, list) {
1178 AST_LIST_REMOVE_CURRENT(list);
1179 /* Destroy if appropriate */
1182 ast_mutex_destroy(&p->lock);
1183 ast_mutex_destroy(&p->app_lock);
1186 /* Cause them to hang up */
1187 ast_softhangup(p->chan, AST_SOFTHANGUP_EXPLICIT);
1192 AST_LIST_TRAVERSE_SAFE_END;
1193 AST_LIST_UNLOCK(&agents);
1194 ast_config_destroy(cfg);
1198 static int check_availability(struct agent_pvt *newlyavailable, int needlock)
1200 struct ast_channel *chan=NULL, *parent=NULL;
1201 struct agent_pvt *p;
1204 ast_debug(1, "Checking availability of '%s'\n", newlyavailable->agent);
1206 AST_LIST_LOCK(&agents);
1207 AST_LIST_TRAVERSE(&agents, p, list) {
1208 if (p == newlyavailable) {
1211 ast_mutex_lock(&p->lock);
1212 if (!p->abouttograb && p->pending && ((p->group && (newlyavailable->group & p->group)) || !strcmp(p->agent, newlyavailable->agent))) {
1213 ast_debug(1, "Call '%s' looks like a winner for agent '%s'\n", p->owner->name, newlyavailable->agent);
1214 /* We found a pending call, time to merge */
1215 chan = agent_new(newlyavailable, AST_STATE_DOWN);
1218 ast_mutex_unlock(&p->lock);
1221 ast_mutex_unlock(&p->lock);
1224 AST_LIST_UNLOCK(&agents);
1225 if (parent && chan) {
1226 if (newlyavailable->ackcall > 1) {
1227 /* Don't do beep here */
1230 ast_debug(3, "Playing beep, lang '%s'\n", newlyavailable->chan->language);
1231 res = ast_streamfile(newlyavailable->chan, beep, newlyavailable->chan->language);
1232 ast_debug(3, "Played beep, result '%d'\n", res);
1234 res = ast_waitstream(newlyavailable->chan, "");
1235 ast_debug(1, "Waited for stream, result '%d'\n", res);
1239 /* Note -- parent may have disappeared */
1240 if (p->abouttograb) {
1241 newlyavailable->acknowledged = 1;
1242 /* Safe -- agent lock already held */
1243 ast_setstate(parent, AST_STATE_UP);
1244 ast_setstate(chan, AST_STATE_UP);
1245 ast_copy_string(parent->context, chan->context, sizeof(parent->context));
1246 /* Go ahead and mark the channel as a zombie so that masquerade will
1247 destroy it for us, and we need not call ast_hangup */
1248 ast_set_flag(chan, AST_FLAG_ZOMBIE);
1249 ast_channel_masquerade(parent, chan);
1252 ast_debug(1, "Sneaky, parent disappeared in the mean time...\n");
1253 agent_cleanup(newlyavailable);
1256 ast_debug(1, "Ugh... Agent hung up at exactly the wrong time\n");
1257 agent_cleanup(newlyavailable);
1263 static int check_beep(struct agent_pvt *newlyavailable, int needlock)
1265 struct agent_pvt *p;
1268 ast_debug(1, "Checking beep availability of '%s'\n", newlyavailable->agent);
1270 AST_LIST_LOCK(&agents);
1271 AST_LIST_TRAVERSE(&agents, p, list) {
1272 if (p == newlyavailable) {
1275 ast_mutex_lock(&p->lock);
1276 if (!p->abouttograb && p->pending && ((p->group && (newlyavailable->group & p->group)) || !strcmp(p->agent, newlyavailable->agent))) {
1277 ast_debug(1, "Call '%s' looks like a would-be winner for agent '%s'\n", p->owner->name, newlyavailable->agent);
1278 ast_mutex_unlock(&p->lock);
1281 ast_mutex_unlock(&p->lock);
1284 AST_LIST_UNLOCK(&agents);
1286 ast_mutex_unlock(&newlyavailable->lock);
1287 ast_debug(3, "Playing beep, lang '%s'\n", newlyavailable->chan->language);
1288 res = ast_streamfile(newlyavailable->chan, beep, newlyavailable->chan->language);
1289 ast_debug(1, "Played beep, result '%d'\n", res);
1291 res = ast_waitstream(newlyavailable->chan, "");
1292 ast_debug(1, "Waited for stream, result '%d'\n", res);
1294 ast_mutex_lock(&newlyavailable->lock);
1299 /*! \brief Part of the Asterisk PBX interface */
1300 static struct ast_channel *agent_request(const char *type, int format, void *data, int *cause)
1302 struct agent_pvt *p;
1303 struct ast_channel *chan = NULL;
1305 ast_group_t groupmatch;
1312 if ((s[0] == '@') && (sscanf(s + 1, "%d", &groupoff) == 1)) {
1313 groupmatch = (1 << groupoff);
1314 } else if ((s[0] == ':') && (sscanf(s + 1, "%d", &groupoff) == 1)) {
1315 groupmatch = (1 << groupoff);
1320 /* Check actual logged in agents first */
1321 AST_LIST_LOCK(&agents);
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 ast_strlen_zero(p->loginchan)) {
1329 if (!p->lastdisc.tv_sec || (tv.tv_sec >= p->lastdisc.tv_sec)) {
1330 p->lastdisc = ast_tv(0, 0);
1331 /* Agent must be registered, but not have any active call, and not be in a waiting state */
1332 if (!p->owner && p->chan) {
1334 chan = agent_new(p, AST_STATE_DOWN);
1337 ast_mutex_unlock(&p->lock);
1342 ast_mutex_unlock(&p->lock);
1345 AST_LIST_TRAVERSE(&agents, p, list) {
1346 ast_mutex_lock(&p->lock);
1347 if (!p->pending && ((groupmatch && (p->group & groupmatch)) || !strcmp(data, p->agent))) {
1348 if (p->chan || !ast_strlen_zero(p->loginchan))
1352 ast_log(LOG_NOTICE, "Time now: %ld, Time of lastdisc: %ld\n", tv.tv_sec, p->lastdisc.tv_sec);
1354 if (!p->lastdisc.tv_sec || (tv.tv_sec >= p->lastdisc.tv_sec)) {
1355 p->lastdisc = ast_tv(0, 0);
1356 /* Agent must be registered, but not have any active call, and not be in a waiting state */
1357 if (!p->owner && p->chan) {
1358 /* Could still get a fixed agent */
1359 chan = agent_new(p, AST_STATE_DOWN);
1360 } else if (!p->owner && !ast_strlen_zero(p->loginchan)) {
1361 /* Adjustable agent */
1362 p->chan = ast_request("Local", format, p->loginchan, cause);
1364 chan = agent_new(p, AST_STATE_DOWN);
1367 ast_mutex_unlock(&p->lock);
1372 ast_mutex_unlock(&p->lock);
1376 if (!chan && waitforagent) {
1377 /* No agent available -- but we're requesting to wait for one.
1378 Allocate a place holder */
1380 ast_debug(1, "Creating place holder for '%s'\n", s);
1381 p = add_agent(data, 1);
1382 p->group = groupmatch;
1383 chan = agent_new(p, AST_STATE_DOWN);
1385 ast_log(LOG_WARNING, "Weird... Fix this to drop the unused pending agent\n");
1387 ast_debug(1, "Not creating place holder for '%s' since nobody logged in\n", s);
1390 *cause = hasagent ? AST_CAUSE_BUSY : AST_CAUSE_UNREGISTERED;
1391 AST_LIST_UNLOCK(&agents);
1395 static force_inline int powerof(unsigned int d)
1406 * Lists agents and their status to the Manager API.
1407 * It is registered on load_module() and it gets called by the manager backend.
1411 * \sa action_agent_logoff(), load_module().
1413 static int action_agents(struct mansession *s, const struct message *m)
1415 const char *id = astman_get_header(m,"ActionID");
1416 char idText[256] = "";
1418 struct agent_pvt *p;
1419 char *username = NULL;
1420 char *loginChan = NULL;
1421 char *talkingto = NULL;
1422 char *talkingtoChan = NULL;
1423 char *status = NULL;
1425 if (!ast_strlen_zero(id))
1426 snprintf(idText, sizeof(idText) ,"ActionID: %s\r\n", id);
1427 astman_send_ack(s, m, "Agents will follow");
1428 AST_LIST_LOCK(&agents);
1429 AST_LIST_TRAVERSE(&agents, p, list) {
1430 ast_mutex_lock(&p->lock);
1433 AGENT_LOGGEDOFF - Agent isn't logged in
1434 AGENT_IDLE - Agent is logged in, and waiting for call
1435 AGENT_ONCALL - Agent is logged in, and on a call
1436 AGENT_UNKNOWN - Don't know anything about agent. Shouldn't ever get this. */
1438 username = S_OR(p->name, "None");
1440 /* Set a default status. It 'should' get changed. */
1441 status = "AGENT_UNKNOWN";
1443 if (!ast_strlen_zero(p->loginchan) && !p->chan) {
1444 loginChan = p->loginchan;
1446 talkingtoChan = "n/a";
1447 status = "AGENT_IDLE";
1448 if (p->acknowledged) {
1449 snprintf(chanbuf, sizeof(chanbuf), " %s (Confirmed)", p->loginchan);
1450 loginChan = chanbuf;
1452 } else if (p->chan) {
1453 loginChan = ast_strdupa(p->chan->name);
1454 if (p->owner && p->owner->_bridge) {
1455 talkingto = p->chan->cid.cid_num;
1456 if (ast_bridged_channel(p->owner))
1457 talkingtoChan = ast_strdupa(ast_bridged_channel(p->owner)->name);
1459 talkingtoChan = "n/a";
1460 status = "AGENT_ONCALL";
1463 talkingtoChan = "n/a";
1464 status = "AGENT_IDLE";
1469 talkingtoChan = "n/a";
1470 status = "AGENT_LOGGEDOFF";
1473 astman_append(s, "Event: Agents\r\n"
1477 "LoggedInChan: %s\r\n"
1478 "LoggedInTime: %d\r\n"
1480 "TalkingToChan: %s\r\n"
1483 p->agent, username, status, loginChan, (int)p->loginstart, talkingto, talkingtoChan, idText);
1484 ast_mutex_unlock(&p->lock);
1486 AST_LIST_UNLOCK(&agents);
1487 astman_append(s, "Event: AgentsComplete\r\n"
1493 static void agent_logoff_maintenance(struct agent_pvt *p, char *loginchan, long logintime, const char *uniqueid, char *logcommand)
1496 char agent[AST_MAX_AGENT];
1498 if (!ast_strlen_zero(logcommand))
1501 tmp = ast_strdupa("");
1503 snprintf(agent, sizeof(agent), "Agent/%s", p->agent);
1505 if (!ast_strlen_zero(uniqueid)) {
1506 manager_event(EVENT_FLAG_AGENT, "Agentcallbacklogoff",
1510 "Logintime: %ld\r\n"
1512 p->agent, tmp, loginchan, logintime, uniqueid);
1514 manager_event(EVENT_FLAG_AGENT, "Agentcallbacklogoff",
1518 "Logintime: %ld\r\n",
1519 p->agent, tmp, loginchan, logintime);
1522 ast_queue_log("NONE", ast_strlen_zero(uniqueid) ? "NONE" : uniqueid, agent, "AGENTCALLBACKLOGOFF", "%s|%ld|%s", loginchan, logintime, tmp);
1523 set_agentbycallerid(p->logincallerid, NULL);
1524 p->loginchan[0] ='\0';
1525 p->logincallerid[0] = '\0';
1526 ast_device_state_changed("Agent/%s", p->agent);
1527 if (persistent_agents)
1532 static int agent_logoff(const char *agent, int soft)
1534 struct agent_pvt *p;
1536 int ret = -1; /* Return -1 if no agent if found */
1538 AST_LIST_LOCK(&agents);
1539 AST_LIST_TRAVERSE(&agents, p, list) {
1540 if (!strcasecmp(p->agent, agent)) {
1542 if (p->owner || p->chan) {
1544 ast_mutex_lock(&p->lock);
1546 while (p->owner && ast_channel_trylock(p->owner)) {
1547 DEADLOCK_AVOIDANCE(&p->lock);
1550 ast_softhangup(p->owner, AST_SOFTHANGUP_EXPLICIT);
1551 ast_channel_unlock(p->owner);
1554 while (p->chan && ast_channel_trylock(p->chan)) {
1555 DEADLOCK_AVOIDANCE(&p->lock);
1558 ast_softhangup(p->chan, AST_SOFTHANGUP_EXPLICIT);
1559 ast_channel_unlock(p->chan);
1562 ast_mutex_unlock(&p->lock);
1566 logintime = time(NULL) - p->loginstart;
1568 agent_logoff_maintenance(p, p->loginchan, logintime, NULL, "CommandLogoff");
1573 AST_LIST_UNLOCK(&agents);
1578 static char *agent_logoff_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1585 e->command = "agent logoff";
1587 "Usage: agent logoff <channel> [soft]\n"
1588 " Sets an agent as no longer logged in.\n"
1589 " If 'soft' is specified, do not hangup existing calls.\n";
1592 return complete_agent_logoff_cmd(a->line, a->word, a->pos, a->n);
1595 if (a->argc < 3 || a->argc > 4)
1596 return CLI_SHOWUSAGE;
1597 if (a->argc == 4 && strcasecmp(a->argv[3], "soft"))
1598 return CLI_SHOWUSAGE;
1600 agent = a->argv[2] + 6;
1601 ret = agent_logoff(agent, a->argc == 4);
1603 ast_cli(a->fd, "Logging out %s\n", agent);
1609 * Sets an agent as no longer logged in in the Manager API.
1610 * It is registered on load_module() and it gets called by the manager backend.
1614 * \sa action_agents(), load_module().
1616 static int action_agent_logoff(struct mansession *s, const struct message *m)
1618 const char *agent = astman_get_header(m, "Agent");
1619 const char *soft_s = astman_get_header(m, "Soft"); /* "true" is don't hangup */
1621 int ret; /* return value of agent_logoff */
1623 if (ast_strlen_zero(agent)) {
1624 astman_send_error(s, m, "No agent specified");
1628 soft = ast_true(soft_s) ? 1 : 0;
1629 ret = agent_logoff(agent, soft);
1631 astman_send_ack(s, m, "Agent logged out");
1633 astman_send_error(s, m, "No such agent");
1638 static char *complete_agent_logoff_cmd(const char *line, const char *word, int pos, int state)
1643 struct agent_pvt *p;
1644 char name[AST_MAX_AGENT];
1645 int which = 0, len = strlen(word);
1647 AST_LIST_LOCK(&agents);
1648 AST_LIST_TRAVERSE(&agents, p, list) {
1649 snprintf(name, sizeof(name), "Agent/%s", p->agent);
1650 if (!strncasecmp(word, name, len) && p->loginstart && ++which > state) {
1651 ret = ast_strdup(name);
1655 AST_LIST_UNLOCK(&agents);
1656 } else if (pos == 3 && state == 0)
1657 return ast_strdup("soft");
1663 * Show agents in cli.
1665 static char *agents_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1667 struct agent_pvt *p;
1668 char username[AST_MAX_BUF];
1669 char location[AST_MAX_BUF] = "";
1670 char talkingto[AST_MAX_BUF] = "";
1671 char moh[AST_MAX_BUF];
1672 int count_agents = 0; /*!< Number of agents configured */
1673 int online_agents = 0; /*!< Number of online agents */
1674 int offline_agents = 0; /*!< Number of offline agents */
1678 e->command = "agent show";
1680 "Usage: agent show\n"
1681 " Provides summary information on agents.\n";
1688 return CLI_SHOWUSAGE;
1690 AST_LIST_LOCK(&agents);
1691 AST_LIST_TRAVERSE(&agents, p, list) {
1692 ast_mutex_lock(&p->lock);
1695 ast_cli(a->fd, "-- Pending call to group %d\n", powerof(p->group));
1697 ast_cli(a->fd, "-- Pending call to agent %s\n", p->agent);
1699 if (!ast_strlen_zero(p->name))
1700 snprintf(username, sizeof(username), "(%s) ", p->name);
1704 snprintf(location, sizeof(location), "logged in on %s", p->chan->name);
1705 if (p->owner && ast_bridged_channel(p->owner))
1706 snprintf(talkingto, sizeof(talkingto), " talking to %s", ast_bridged_channel(p->owner)->name);
1708 strcpy(talkingto, " is idle");
1710 } else if (!ast_strlen_zero(p->loginchan)) {
1711 if (ast_tvdiff_ms(ast_tvnow(), p->lastdisc) > 0 || !(p->lastdisc.tv_sec))
1712 snprintf(location, sizeof(location) - 20, "available at '%s'", p->loginchan);
1714 snprintf(location, sizeof(location) - 20, "wrapping up at '%s'", p->loginchan);
1715 talkingto[0] = '\0';
1717 if (p->acknowledged)
1718 strncat(location, " (Confirmed)", sizeof(location) - strlen(location) - 1);
1720 strcpy(location, "not logged in");
1721 talkingto[0] = '\0';
1724 if (!ast_strlen_zero(p->moh))
1725 snprintf(moh, sizeof(moh), " (musiconhold is '%s')", p->moh);
1726 ast_cli(a->fd, "%-12.12s %s%s%s%s\n", p->agent,
1727 username, location, talkingto, moh);
1730 ast_mutex_unlock(&p->lock);
1732 AST_LIST_UNLOCK(&agents);
1733 if ( !count_agents )
1734 ast_cli(a->fd, "No Agents are configured in %s\n",config);
1736 ast_cli(a->fd, "%d agents configured [%d online , %d offline]\n",count_agents, online_agents, offline_agents);
1737 ast_cli(a->fd, "\n");
1743 static char *agents_show_online(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1745 struct agent_pvt *p;
1746 char username[AST_MAX_BUF];
1747 char location[AST_MAX_BUF] = "";
1748 char talkingto[AST_MAX_BUF] = "";
1749 char moh[AST_MAX_BUF];
1750 int count_agents = 0; /* Number of agents configured */
1751 int online_agents = 0; /* Number of online agents */
1752 int agent_status = 0; /* 0 means offline, 1 means online */
1756 e->command = "agent show online";
1758 "Usage: agent show online\n"
1759 " Provides a list of all online agents.\n";
1766 return CLI_SHOWUSAGE;
1768 AST_LIST_LOCK(&agents);
1769 AST_LIST_TRAVERSE(&agents, p, list) {
1770 agent_status = 0; /* reset it to offline */
1771 ast_mutex_lock(&p->lock);
1772 if (!ast_strlen_zero(p->name))
1773 snprintf(username, sizeof(username), "(%s) ", p->name);
1777 snprintf(location, sizeof(location), "logged in on %s", p->chan->name);
1778 if (p->owner && ast_bridged_channel(p->owner))
1779 snprintf(talkingto, sizeof(talkingto), " talking to %s", ast_bridged_channel(p->owner)->name);
1781 strcpy(talkingto, " is idle");
1784 } else if (!ast_strlen_zero(p->loginchan)) {
1785 snprintf(location, sizeof(location) - 20, "available at '%s'", p->loginchan);
1786 talkingto[0] = '\0';
1789 if (p->acknowledged)
1790 strncat(location, " (Confirmed)", sizeof(location) - strlen(location) - 1);
1792 if (!ast_strlen_zero(p->moh))
1793 snprintf(moh, sizeof(moh), " (musiconhold is '%s')", p->moh);
1795 ast_cli(a->fd, "%-12.12s %s%s%s%s\n", p->agent, username, location, talkingto, moh);
1797 ast_mutex_unlock(&p->lock);
1799 AST_LIST_UNLOCK(&agents);
1801 ast_cli(a->fd, "No Agents are configured in %s\n", config);
1803 ast_cli(a->fd, "%d agents online\n", online_agents);
1804 ast_cli(a->fd, "\n");
1808 static const char agent_logoff_usage[] =
1809 "Usage: agent logoff <channel> [soft]\n"
1810 " Sets an agent as no longer logged in.\n"
1811 " If 'soft' is specified, do not hangup existing calls.\n";
1813 static struct ast_cli_entry cli_agents[] = {
1814 AST_CLI_DEFINE(agents_show, "Show status of agents"),
1815 AST_CLI_DEFINE(agents_show_online, "Show all online agents"),
1816 AST_CLI_DEFINE(agent_logoff_cmd, "Sets an agent offline"),
1820 * Called by the AgentLogin application (from the dial plan).
1822 * \brief Log in agent application.
1827 * \sa agentmonitoroutgoing_exec(), load_module().
1829 static int login_exec(struct ast_channel *chan, void *data)
1833 int max_login_tries = maxlogintries;
1834 struct agent_pvt *p;
1835 struct ast_module_user *u;
1836 int login_state = 0;
1837 char user[AST_MAX_AGENT] = "";
1838 char pass[AST_MAX_AGENT];
1839 char agent[AST_MAX_AGENT] = "";
1840 char xpass[AST_MAX_AGENT] = "";
1843 AST_DECLARE_APP_ARGS(args,
1844 AST_APP_ARG(agent_id);
1845 AST_APP_ARG(options);
1846 AST_APP_ARG(extension);
1848 const char *tmpoptions = NULL;
1849 int play_announcement = 1;
1850 char agent_goodbye[AST_MAX_FILENAME_LEN];
1851 int update_cdr = updatecdr;
1852 char *filename = "agent-loginok";
1854 u = ast_module_user_add(chan);
1856 parse = ast_strdupa(data);
1858 AST_STANDARD_APP_ARGS(args, parse);
1860 ast_copy_string(agent_goodbye, agentgoodbye, sizeof(agent_goodbye));
1862 /* Set Channel Specific Login Overrides */
1863 if (!ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENTLMAXLOGINTRIES"))) {
1864 max_login_tries = atoi(pbx_builtin_getvar_helper(chan, "AGENTMAXLOGINTRIES"));
1865 if (max_login_tries < 0)
1866 max_login_tries = 0;
1867 tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTMAXLOGINTRIES");
1868 ast_verb(3, "Saw variable AGENTMAXLOGINTRIES=%s, setting max_login_tries to: %d on Channel '%s'.\n",tmpoptions,max_login_tries,chan->name);
1870 if (!ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENTUPDATECDR"))) {
1871 if (ast_true(pbx_builtin_getvar_helper(chan, "AGENTUPDATECDR")))
1875 tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTUPDATECDR");
1876 ast_verb(3, "Saw variable AGENTUPDATECDR=%s, setting update_cdr to: %d on Channel '%s'.\n",tmpoptions,update_cdr,chan->name);
1878 if (!ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENTGOODBYE"))) {
1879 strcpy(agent_goodbye, pbx_builtin_getvar_helper(chan, "AGENTGOODBYE"));
1880 tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTGOODBYE");
1881 ast_verb(3, "Saw variable AGENTGOODBYE=%s, setting agent_goodbye to: %s on Channel '%s'.\n",tmpoptions,agent_goodbye,chan->name);
1883 /* End Channel Specific Login Overrides */
1885 if (!ast_strlen_zero(args.options)) {
1886 if (strchr(args.options, 's')) {
1887 play_announcement = 0;
1891 if (chan->_state != AST_STATE_UP)
1892 res = ast_answer(chan);
1894 if (!ast_strlen_zero(args.agent_id))
1895 ast_copy_string(user, args.agent_id, AST_MAX_AGENT);
1897 res = ast_app_getdata(chan, "agent-user", user, sizeof(user) - 1, 0);
1899 while (!res && (max_login_tries==0 || tries < max_login_tries)) {
1901 /* Check for password */
1902 AST_LIST_LOCK(&agents);
1903 AST_LIST_TRAVERSE(&agents, p, list) {
1904 if (!strcmp(p->agent, user) && !p->pending)
1905 ast_copy_string(xpass, p->password, sizeof(xpass));
1907 AST_LIST_UNLOCK(&agents);
1909 if (!ast_strlen_zero(xpass))
1910 res = ast_app_getdata(chan, "agent-pass", pass, sizeof(pass) - 1, 0);
1914 errmsg = "agent-incorrect";
1917 ast_log(LOG_NOTICE, "user: %s, pass: %s\n", user, pass);
1920 /* Check again for accuracy */
1921 AST_LIST_LOCK(&agents);
1922 AST_LIST_TRAVERSE(&agents, p, list) {
1923 ast_mutex_lock(&p->lock);
1924 if (!strcmp(p->agent, user) &&
1925 !strcmp(p->password, pass) && !p->pending) {
1926 login_state = 1; /* Successful Login */
1928 /* Ensure we can't be gotten until we're done */
1929 p->lastdisc = ast_tvnow();
1930 p->lastdisc.tv_sec++;
1932 /* Set Channel Specific Agent Overrides */
1933 if (!ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENTACKCALL"))) {
1934 if (!strcasecmp(pbx_builtin_getvar_helper(chan, "AGENTACKCALL"), "always"))
1936 else if (ast_true(pbx_builtin_getvar_helper(chan, "AGENTACKCALL")))
1940 tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTACKCALL");
1941 ast_verb(3, "Saw variable AGENTACKCALL=%s, setting ackcall to: %d for Agent '%s'.\n", tmpoptions, p->ackcall, p->agent);
1943 if (!ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF"))) {
1944 p->autologoff = atoi(pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF"));
1945 if (p->autologoff < 0)
1947 tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF");
1948 ast_verb(3, "Saw variable AGENTAUTOLOGOFF=%s, setting autologff to: %d for Agent '%s'.\n", tmpoptions, p->autologoff, p->agent);
1950 if (!ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME"))) {
1951 p->wrapuptime = atoi(pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME"));
1952 if (p->wrapuptime < 0)
1954 tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME");
1955 ast_verb(3, "Saw variable AGENTWRAPUPTIME=%s, setting wrapuptime to: %d for Agent '%s'.\n", tmpoptions, p->wrapuptime, p->agent);
1957 tmpoptions = pbx_builtin_getvar_helper(chan, "AGENTACCEPTDMTF");
1958 if (!ast_strlen_zero(tmpoptions)) {
1959 p->acceptdtmf = *tmpoptions;
1960 ast_verb(3, "Saw variable AGENTACCEPTDTMF=%s, setting acceptdtmf to: %c for Agent '%s'.\n", tmpoptions, p->acceptdtmf, p->agent);
1962 tmpoptions = pbx_builtin_getvar_helper(chan, "AGENTENDDTMF");
1963 if (!ast_strlen_zero(tmpoptions)) {
1964 p->enddtmf = *tmpoptions;
1965 ast_verb(3, "Saw variable AGENTENDDTMF=%s, setting enddtmf to: %c for Agent '%s'.\n", tmpoptions, p->enddtmf, p->agent);
1967 /* End Channel Specific Agent Overrides */
1970 snprintf(agent, sizeof(agent), "Agent/%s", p->agent);
1972 p->loginchan[0] = '\0';
1973 p->logincallerid[0] = '\0';
1974 p->acknowledged = 0;
1976 ast_mutex_unlock(&p->lock);
1977 AST_LIST_UNLOCK(&agents);
1978 if( !res && play_announcement==1 )
1979 res = ast_streamfile(chan, filename, chan->language);
1981 ast_waitstream(chan, "");
1982 AST_LIST_LOCK(&agents);
1983 ast_mutex_lock(&p->lock);
1985 res = ast_set_read_format(chan, ast_best_codec(chan->nativeformats));
1987 ast_log(LOG_WARNING, "Unable to set read format to %d\n", ast_best_codec(chan->nativeformats));
1990 res = ast_set_write_format(chan, ast_best_codec(chan->nativeformats));
1992 ast_log(LOG_WARNING, "Unable to set write format to %d\n", ast_best_codec(chan->nativeformats));
1994 /* Check once more just in case */
1998 ast_indicate_data(chan, AST_CONTROL_HOLD,
2000 !ast_strlen_zero(p->moh) ? strlen(p->moh) + 1 : 0);
2001 if (p->loginstart == 0)
2002 time(&p->loginstart);
2003 manager_event(EVENT_FLAG_AGENT, "Agentlogin",
2007 p->agent, chan->name, chan->uniqueid);
2008 if (update_cdr && chan->cdr)
2009 snprintf(chan->cdr->channel, sizeof(chan->cdr->channel), "Agent/%s", p->agent);
2010 ast_queue_log("NONE", chan->uniqueid, agent, "AGENTLOGIN", "%s", chan->name);
2011 ast_verb(2, "Agent '%s' logged in (format %s/%s)\n", p->agent,
2012 ast_getformatname(chan->readformat), ast_getformatname(chan->writeformat));
2013 /* Login this channel and wait for it to go away */
2018 check_availability(p, 0);
2019 ast_mutex_unlock(&p->lock);
2020 AST_LIST_UNLOCK(&agents);
2021 ast_device_state_changed("Agent/%s", p->agent);
2023 ast_mutex_lock(&p->lock);
2024 if (p->deferlogoff && p->chan) {
2025 ast_softhangup(p->chan, AST_SOFTHANGUP_EXPLICIT);
2028 if (p->chan != chan)
2030 ast_mutex_unlock(&p->lock);
2031 /* Yield here so other interested threads can kick in. */
2036 AST_LIST_LOCK(&agents);
2037 ast_mutex_lock(&p->lock);
2038 if (p->lastdisc.tv_sec) {
2039 if (ast_tvdiff_ms(ast_tvnow(), p->lastdisc) > 0) {
2040 ast_debug(1, "Wrapup time for %s expired!\n", p->agent);
2041 p->lastdisc = ast_tv(0, 0);
2042 ast_device_state_changed("Agent/%s", p->agent);
2046 check_availability(p, 0);
2049 ast_mutex_unlock(&p->lock);
2050 AST_LIST_UNLOCK(&agents);
2051 /* Synchronize channel ownership between call to agent and itself. */
2052 ast_mutex_lock( &p->app_lock );
2053 ast_mutex_lock(&p->lock);
2054 p->owning_app = pthread_self();
2055 ast_mutex_unlock(&p->lock);
2057 res = agent_ack_sleep(p);
2059 res = ast_safe_sleep_conditional( chan, 1000, agent_cont_sleep, p );
2060 ast_mutex_unlock( &p->app_lock );
2061 if ((p->ackcall > 1) && (res == 1)) {
2062 AST_LIST_LOCK(&agents);
2063 ast_mutex_lock(&p->lock);
2064 check_availability(p, 0);
2065 ast_mutex_unlock(&p->lock);
2066 AST_LIST_UNLOCK(&agents);
2071 ast_mutex_lock(&p->lock);
2072 if (res && p->owner)
2073 ast_log(LOG_WARNING, "Huh? We broke out when there was still an owner?\n");
2074 /* Log us off if appropriate */
2075 if (p->chan == chan)
2077 p->acknowledged = 0;
2078 logintime = time(NULL) - p->loginstart;
2080 ast_mutex_unlock(&p->lock);
2081 manager_event(EVENT_FLAG_AGENT, "Agentlogoff",
2083 "Logintime: %ld\r\n"
2085 p->agent, logintime, chan->uniqueid);
2086 ast_queue_log("NONE", chan->uniqueid, agent, "AGENTLOGOFF", "%s|%ld", chan->name, logintime);
2087 ast_verb(2, "Agent '%s' logged out\n", p->agent);
2088 /* If there is no owner, go ahead and kill it now */
2089 ast_device_state_changed("Agent/%s", p->agent);
2090 if (p->dead && !p->owner) {
2091 ast_mutex_destroy(&p->lock);
2092 ast_mutex_destroy(&p->app_lock);
2097 ast_mutex_unlock(&p->lock);
2102 ast_mutex_unlock(&p->lock);
2103 errmsg = "agent-alreadyon";
2108 ast_mutex_unlock(&p->lock);
2111 AST_LIST_UNLOCK(&agents);
2113 if (!res && (max_login_tries==0 || tries < max_login_tries))
2114 res = ast_app_getdata(chan, errmsg, user, sizeof(user) - 1, 0);
2118 res = ast_safe_sleep(chan, 500);
2120 ast_module_user_remove(u);
2126 * \brief Called by the AgentMonitorOutgoing application (from the dial plan).
2131 * \sa login_exec(), load_module().
2133 static int agentmonitoroutgoing_exec(struct ast_channel *chan, void *data)
2135 int exitifnoagentid = 0;
2137 int changeoutgoing = 0;
2139 char agent[AST_MAX_AGENT];
2142 if (strchr(data, 'd'))
2143 exitifnoagentid = 1;
2144 if (strchr(data, 'n'))
2146 if (strchr(data, 'c'))
2149 if (chan->cid.cid_num) {
2151 char agentvar[AST_MAX_BUF];
2152 snprintf(agentvar, sizeof(agentvar), "%s_%s", GETAGENTBYCALLERID, chan->cid.cid_num);
2153 if ((tmp = pbx_builtin_getvar_helper(NULL, agentvar))) {
2154 struct agent_pvt *p;
2155 ast_copy_string(agent, tmp, sizeof(agent));
2156 AST_LIST_LOCK(&agents);
2157 AST_LIST_TRAVERSE(&agents, p, list) {
2158 if (!strcasecmp(p->agent, tmp)) {
2159 if (changeoutgoing) snprintf(chan->cdr->channel, sizeof(chan->cdr->channel), "Agent/%s", p->agent);
2160 __agent_start_monitoring(chan, p, 1);
2164 AST_LIST_UNLOCK(&agents);
2169 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);
2174 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");
2177 if (exitifnoagentid)
2184 * \brief Dump AgentCallbackLogin agents to the ASTdb database for persistence
2186 static void dump_agents(void)
2188 struct agent_pvt *cur_agent = NULL;
2191 AST_LIST_TRAVERSE(&agents, cur_agent, list) {
2192 if (cur_agent->chan)
2195 if (!ast_strlen_zero(cur_agent->loginchan)) {
2196 snprintf(buf, sizeof(buf), "%s;%s", cur_agent->loginchan, cur_agent->logincallerid);
2197 if (ast_db_put(pa_family, cur_agent->agent, buf))
2198 ast_log(LOG_WARNING, "failed to create persistent entry in ASTdb for %s!\n", buf);
2200 ast_debug(1, "Saved Agent: %s on %s\n", cur_agent->agent, cur_agent->loginchan);
2202 /* Delete - no agent or there is an error */
2203 ast_db_del(pa_family, cur_agent->agent);
2209 * \brief Reload the persistent agents from astdb.
2211 static void reload_agents(void)
2214 struct ast_db_entry *db_tree;
2215 struct ast_db_entry *entry;
2216 struct agent_pvt *cur_agent;
2217 char agent_data[256];
2220 char *agent_callerid;
2222 db_tree = ast_db_gettree(pa_family, NULL);
2224 AST_LIST_LOCK(&agents);
2225 for (entry = db_tree; entry; entry = entry->next) {
2226 agent_num = entry->key + strlen(pa_family) + 2;
2227 AST_LIST_TRAVERSE(&agents, cur_agent, list) {
2228 ast_mutex_lock(&cur_agent->lock);
2229 if (strcmp(agent_num, cur_agent->agent) == 0)
2231 ast_mutex_unlock(&cur_agent->lock);
2234 ast_db_del(pa_family, agent_num);
2237 ast_mutex_unlock(&cur_agent->lock);
2238 if (!ast_db_get(pa_family, agent_num, agent_data, sizeof(agent_data)-1)) {
2239 ast_debug(1, "Reload Agent from AstDB: %s on %s\n", cur_agent->agent, agent_data);
2241 agent_chan = strsep(&parse, ";");
2242 agent_callerid = strsep(&parse, ";");
2243 ast_copy_string(cur_agent->loginchan, agent_chan, sizeof(cur_agent->loginchan));
2244 if (agent_callerid) {
2245 ast_copy_string(cur_agent->logincallerid, agent_callerid, sizeof(cur_agent->logincallerid));
2246 set_agentbycallerid(cur_agent->logincallerid, cur_agent->agent);
2248 cur_agent->logincallerid[0] = '\0';
2249 if (cur_agent->loginstart == 0)
2250 time(&cur_agent->loginstart);
2251 ast_device_state_changed("Agent/%s", cur_agent->agent);
2254 AST_LIST_UNLOCK(&agents);
2256 ast_log(LOG_NOTICE, "Agents successfully reloaded from database.\n");
2257 ast_db_freetree(db_tree);
2261 /*! \brief Part of PBX channel interface */
2262 static int agent_devicestate(void *data)
2264 struct agent_pvt *p;
2266 ast_group_t groupmatch;
2268 int res = AST_DEVICE_INVALID;
2271 if ((s[0] == '@') && (sscanf(s + 1, "%d", &groupoff) == 1))
2272 groupmatch = (1 << groupoff);
2273 else if ((s[0] == ':') && (sscanf(s + 1, "%d", &groupoff) == 1)) {
2274 groupmatch = (1 << groupoff);
2278 /* Check actual logged in agents first */
2279 AST_LIST_LOCK(&agents);
2280 AST_LIST_TRAVERSE(&agents, p, list) {
2281 ast_mutex_lock(&p->lock);
2282 if (!p->pending && ((groupmatch && (p->group & groupmatch)) || !strcmp(data, p->agent))) {
2284 if (res != AST_DEVICE_INUSE)
2285 res = AST_DEVICE_BUSY;
2287 if (res == AST_DEVICE_BUSY)
2288 res = AST_DEVICE_INUSE;
2289 if (p->chan || !ast_strlen_zero(p->loginchan)) {
2290 if (res == AST_DEVICE_INVALID)
2291 res = AST_DEVICE_UNKNOWN;
2292 } else if (res == AST_DEVICE_INVALID)
2293 res = AST_DEVICE_UNAVAILABLE;
2295 if (!strcmp(data, p->agent)) {
2296 ast_mutex_unlock(&p->lock);
2300 ast_mutex_unlock(&p->lock);
2302 AST_LIST_UNLOCK(&agents);
2307 * \note This function expects the agent list to be locked
2309 static struct agent_pvt *find_agent(char *agentid)
2311 struct agent_pvt *cur;
2313 AST_LIST_TRAVERSE(&agents, cur, list) {
2314 if (!strcmp(cur->agent, agentid))
2321 static int function_agent(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
2324 AST_DECLARE_APP_ARGS(args,
2325 AST_APP_ARG(agentid);
2329 struct agent_pvt *agent;
2333 if (ast_strlen_zero(data)) {
2334 ast_log(LOG_WARNING, "The AGENT function requires an argument - agentid!\n");
2338 parse = ast_strdupa(data);
2340 AST_NONSTANDARD_APP_ARGS(args, parse, ':');
2342 args.item = "status";
2344 AST_LIST_LOCK(&agents);
2346 if (!(agent = find_agent(args.agentid))) {
2347 AST_LIST_UNLOCK(&agents);
2348 ast_log(LOG_WARNING, "Agent '%s' not found!\n", args.agentid);
2352 if (!strcasecmp(args.item, "status")) {
2353 char *status = "LOGGEDOUT";
2354 if (agent->chan || !ast_strlen_zero(agent->loginchan))
2355 status = "LOGGEDIN";
2356 ast_copy_string(buf, status, len);
2357 } else if (!strcasecmp(args.item, "password"))
2358 ast_copy_string(buf, agent->password, len);
2359 else if (!strcasecmp(args.item, "name"))
2360 ast_copy_string(buf, agent->name, len);
2361 else if (!strcasecmp(args.item, "mohclass"))
2362 ast_copy_string(buf, agent->moh, len);
2363 else if (!strcasecmp(args.item, "channel")) {
2365 ast_copy_string(buf, agent->chan->name, len);
2366 tmp = strrchr(buf, '-');
2370 } else if (!strcasecmp(args.item, "exten"))
2371 ast_copy_string(buf, agent->loginchan, len);
2373 AST_LIST_UNLOCK(&agents);
2378 struct ast_custom_function agent_function = {
2380 .synopsis = "Gets information about an Agent",
2381 .syntax = "AGENT(<agentid>[:item])",
2382 .read = function_agent,
2383 .desc = "The valid items to retrieve are:\n"
2384 "- status (default) The status of the agent\n"
2385 " LOGGEDIN | LOGGEDOUT\n"
2386 "- password The password of the agent\n"
2387 "- name The name of the agent\n"
2388 "- mohclass MusicOnHold class\n"
2389 "- exten The callback extension for the Agent (AgentCallbackLogin)\n"
2390 "- channel The name of the active channel for the Agent (AgentLogin)\n"
2395 * \brief Initialize the Agents module.
2396 * This function is being called by Asterisk when loading the module.
2397 * Among other things it registers applications, cli commands and reads the cofiguration file.
2399 * \returns int Always 0.
2401 static int load_module(void)
2403 /* Make sure we can register our agent channel type */
2404 if (ast_channel_register(&agent_tech)) {
2405 ast_log(LOG_ERROR, "Unable to register channel class 'Agent'\n");
2406 return AST_MODULE_LOAD_FAILURE;
2408 /* Read in the config */
2409 if (!read_agent_config(0))
2410 return AST_MODULE_LOAD_DECLINE;
2411 if (persistent_agents)
2413 /* Dialplan applications */
2414 ast_register_application(app, login_exec, synopsis, descrip);
2415 ast_register_application(app3, agentmonitoroutgoing_exec, synopsis3, descrip3);
2417 /* Manager commands */
2418 ast_manager_register2("Agents", EVENT_FLAG_AGENT, action_agents, "Lists agents and their status", mandescr_agents);
2419 ast_manager_register2("AgentLogoff", EVENT_FLAG_AGENT, action_agent_logoff, "Sets an agent as no longer logged in", mandescr_agent_logoff);
2422 ast_cli_register_multiple(cli_agents, sizeof(cli_agents) / sizeof(struct ast_cli_entry));
2424 /* Dialplan Functions */
2425 ast_custom_function_register(&agent_function);
2427 return AST_MODULE_LOAD_SUCCESS;
2430 static int reload(void)
2432 if (!read_agent_config(1)) {
2433 if (persistent_agents)
2439 static int unload_module(void)
2441 struct agent_pvt *p;
2442 /* First, take us out of the channel loop */
2443 ast_channel_unregister(&agent_tech);
2444 /* Unregister dialplan functions */
2445 ast_custom_function_unregister(&agent_function);
2446 /* Unregister CLI commands */
2447 ast_cli_unregister_multiple(cli_agents, sizeof(cli_agents) / sizeof(struct ast_cli_entry));
2448 /* Unregister dialplan applications */
2449 ast_unregister_application(app);
2450 ast_unregister_application(app3);
2451 /* Unregister manager command */
2452 ast_manager_unregister("Agents");
2453 ast_manager_unregister("AgentLogoff");
2454 /* Unregister channel */
2455 AST_LIST_LOCK(&agents);
2456 /* Hangup all interfaces if they have an owner */
2457 while ((p = AST_LIST_REMOVE_HEAD(&agents, list))) {
2459 ast_softhangup(p->owner, AST_SOFTHANGUP_APPUNLOAD);
2462 AST_LIST_UNLOCK(&agents);
2466 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Agent Proxy Channel",
2467 .load = load_module,
2468 .unload = unload_module,