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 static ast_group_t group;
128 static int autologoff;
129 static int wrapuptime;
132 static int multiplelogin = 1;
133 static int autologoffunavail = 0;
135 static int maxlogintries = 3;
136 static char agentgoodbye[AST_MAX_FILENAME_LEN] = "vm-goodbye";
138 static int recordagentcalls = 0;
139 static char recordformat[AST_MAX_BUF] = "";
140 static char recordformatext[AST_MAX_BUF] = "";
141 static char urlprefix[AST_MAX_BUF] = "";
142 static char savecallsin[AST_MAX_BUF] = "";
143 static int updatecdr = 0;
144 static char beep[AST_MAX_BUF] = "beep";
146 #define GETAGENTBYCALLERID "AGENTBYCALLERID"
148 /*! \brief Structure representing an agent. */
150 ast_mutex_t lock; /*!< Channel private lock */
151 int dead; /*!< Poised for destruction? */
152 int pending; /*!< Not a real agent -- just pending a match */
153 int abouttograb; /*!< About to grab */
154 int autologoff; /*!< Auto timeout time */
155 int ackcall; /*!< ackcall */
156 int deferlogoff; /*!< Defer logoff to hangup */
157 time_t loginstart; /*!< When agent first logged in (0 when logged off) */
158 time_t start; /*!< When call started */
159 struct timeval lastdisc; /*!< When last disconnected */
160 int wrapuptime; /*!< Wrapup time in ms */
161 ast_group_t group; /*!< Group memberships */
162 int acknowledged; /*!< Acknowledged */
163 char moh[80]; /*!< Which music on hold */
164 char agent[AST_MAX_AGENT]; /*!< Agent ID */
165 char password[AST_MAX_AGENT]; /*!< Password for Agent login */
166 char name[AST_MAX_AGENT];
167 ast_mutex_t app_lock; /**< Synchronization between owning applications */
168 volatile pthread_t owning_app; /**< Owning application thread id */
169 volatile int app_sleep_cond; /**< Sleep condition for the login app */
170 struct ast_channel *owner; /**< Agent */
171 char loginchan[80]; /**< channel they logged in from */
172 char logincallerid[80]; /**< Caller ID they had when they logged in */
173 struct ast_channel *chan; /**< Channel we use */
174 AST_LIST_ENTRY(agent_pvt) list; /**< Next Agent in the linked list. */
177 static AST_LIST_HEAD_STATIC(agents, agent_pvt); /*!< Holds the list of agents (loaded form agents.conf). */
179 #define CHECK_FORMATS(ast, p) do { \
181 if (ast->nativeformats != p->chan->nativeformats) { \
182 ast_debug(1, "Native formats changing from %d to %d\n", ast->nativeformats, p->chan->nativeformats); \
183 /* Native formats changed, reset things */ \
184 ast->nativeformats = p->chan->nativeformats; \
185 ast_debug(1, "Resetting read to %d and write to %d\n", ast->readformat, ast->writeformat);\
186 ast_set_read_format(ast, ast->readformat); \
187 ast_set_write_format(ast, ast->writeformat); \
189 if (p->chan->readformat != ast->rawreadformat && !p->chan->generator) \
190 ast_set_read_format(p->chan, ast->rawreadformat); \
191 if (p->chan->writeformat != ast->rawwriteformat && !p->chan->generator) \
192 ast_set_write_format(p->chan, ast->rawwriteformat); \
196 /*! \brief Cleanup moves all the relevant FD's from the 2nd to the first, but retains things
197 properly for a timingfd XXX This might need more work if agents were logged in as agents or other
198 totally impractical combinations XXX */
200 #define CLEANUP(ast, p) do { \
203 for (x=0;x<AST_MAX_FDS;x++) {\
204 if (x != AST_TIMING_FD) \
205 ast_channel_set_fd(ast, x, p->chan->fds[x]); \
207 ast_channel_set_fd(ast, AST_AGENT_FD, p->chan->fds[AST_TIMING_FD]); \
211 /*--- Forward declarations */
212 static struct ast_channel *agent_request(const char *type, int format, void *data, int *cause);
213 static int agent_devicestate(void *data);
214 static void agent_logoff_maintenance(struct agent_pvt *p, char *loginchan, long logintime, const char *uniqueid, char *logcommand);
215 static int agent_digit_begin(struct ast_channel *ast, char digit);
216 static int agent_digit_end(struct ast_channel *ast, char digit, unsigned int duration);
217 static int agent_call(struct ast_channel *ast, char *dest, int timeout);
218 static int agent_hangup(struct ast_channel *ast);
219 static int agent_answer(struct ast_channel *ast);
220 static struct ast_frame *agent_read(struct ast_channel *ast);
221 static int agent_write(struct ast_channel *ast, struct ast_frame *f);
222 static int agent_sendhtml(struct ast_channel *ast, int subclass, const char *data, int datalen);
223 static int agent_sendtext(struct ast_channel *ast, const char *text);
224 static int agent_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen);
225 static int agent_fixup(struct ast_channel *oldchan, struct ast_channel *newchan);
226 static struct ast_channel *agent_bridgedchannel(struct ast_channel *chan, struct ast_channel *bridge);
227 static void set_agentbycallerid(const char *callerid, const char *agent);
228 static char *complete_agent_logoff_cmd(const char *line, const char *word, int pos, int state);
229 static struct ast_channel* agent_get_base_channel(struct ast_channel *chan);
230 static int agent_set_base_channel(struct ast_channel *chan, struct ast_channel *base);
232 /*! \brief Channel interface description for PBX integration */
233 static const struct ast_channel_tech agent_tech = {
235 .description = tdesc,
237 .requester = agent_request,
238 .devicestate = agent_devicestate,
239 .send_digit_begin = agent_digit_begin,
240 .send_digit_end = agent_digit_end,
242 .hangup = agent_hangup,
243 .answer = agent_answer,
245 .write = agent_write,
246 .write_video = agent_write,
247 .send_html = agent_sendhtml,
248 .send_text = agent_sendtext,
249 .exception = agent_read,
250 .indicate = agent_indicate,
251 .fixup = agent_fixup,
252 .bridged_channel = agent_bridgedchannel,
253 .get_base_channel = agent_get_base_channel,
254 .set_base_channel = agent_set_base_channel,
258 * Adds an agent to the global list of agents.
260 * \param agent A string with the username, password and real name of an agent. As defined in agents.conf. Example: "13,169,John Smith"
261 * \param pending If it is pending or not.
262 * @return The just created agent.
263 * \sa agent_pvt, agents.
265 static struct agent_pvt *add_agent(const char *agent, int pending)
268 AST_DECLARE_APP_ARGS(args,
270 AST_APP_ARG(password);
273 char *password = NULL;
278 parse = ast_strdupa(agent);
280 /* Extract username (agt), password and name from agent (args). */
281 AST_STANDARD_APP_ARGS(args, parse);
284 ast_log(LOG_WARNING, "A blank agent line!\n");
288 if(ast_strlen_zero(args.agt) ) {
289 ast_log(LOG_WARNING, "An agent line with no agentid!\n");
294 if(!ast_strlen_zero(args.password)) {
295 password = args.password;
296 while (*password && *password < 33) password++;
298 if(!ast_strlen_zero(args.name)) {
300 while (*name && *name < 33) name++;
303 /* Are we searching for the agent here ? To see if it exists already ? */
304 AST_LIST_TRAVERSE(&agents, p, list) {
305 if (!pending && !strcmp(p->agent, agt))
310 if (!(p = ast_calloc(1, sizeof(*p))))
312 ast_copy_string(p->agent, agt, sizeof(p->agent));
313 ast_mutex_init(&p->lock);
314 ast_mutex_init(&p->app_lock);
315 p->owning_app = (pthread_t) -1;
316 p->app_sleep_cond = 1;
318 p->pending = pending;
319 AST_LIST_INSERT_TAIL(&agents, p, list);
322 ast_copy_string(p->password, password ? password : "", sizeof(p->password));
323 ast_copy_string(p->name, name ? name : "", sizeof(p->name));
324 ast_copy_string(p->moh, moh, sizeof(p->moh));
325 p->ackcall = ackcall;
326 p->autologoff = autologoff;
328 /* If someone reduces the wrapuptime and reloads, we want it
329 * to change the wrapuptime immediately on all calls */
330 if (p->wrapuptime > wrapuptime) {
331 struct timeval now = ast_tvnow();
332 /* XXX check what is this exactly */
334 /* We won't be pedantic and check the tv_usec val */
335 if (p->lastdisc.tv_sec > (now.tv_sec + wrapuptime/1000)) {
336 p->lastdisc.tv_sec = now.tv_sec + wrapuptime/1000;
337 p->lastdisc.tv_usec = now.tv_usec;
340 p->wrapuptime = wrapuptime;
350 * Deletes an agent after doing some clean up.
351 * Further documentation: How safe is this function ? What state should the agent be to be cleaned.
352 * \param p Agent to be deleted.
355 static int agent_cleanup(struct agent_pvt *p)
357 struct ast_channel *chan = p->owner;
359 chan->tech_pvt = NULL;
360 p->app_sleep_cond = 1;
361 /* Release ownership of the agent to other threads (presumably running the login app). */
362 ast_mutex_unlock(&p->app_lock);
364 ast_channel_free(chan);
366 ast_mutex_destroy(&p->lock);
367 ast_mutex_destroy(&p->app_lock);
373 static int check_availability(struct agent_pvt *newlyavailable, int needlock);
375 static int agent_answer(struct ast_channel *ast)
377 ast_log(LOG_WARNING, "Huh? Agent is being asked to answer?\n");
381 static int __agent_start_monitoring(struct ast_channel *ast, struct agent_pvt *p, int needlock)
383 char tmp[AST_MAX_BUF],tmp2[AST_MAX_BUF], *pointer;
384 char filename[AST_MAX_BUF];
389 snprintf(filename, sizeof(filename), "agent-%s-%s",p->agent, ast->uniqueid);
390 /* substitute . for - */
391 if ((pointer = strchr(filename, '.')))
393 snprintf(tmp, sizeof(tmp), "%s%s", savecallsin, filename);
394 ast_monitor_start(ast, recordformat, tmp, needlock, X_REC_IN | X_REC_OUT);
395 ast_monitor_setjoinfiles(ast, 1);
396 snprintf(tmp2, sizeof(tmp2), "%s%s.%s", urlprefix, filename, recordformatext);
398 ast_verbose("name is %s, link is %s\n",tmp, tmp2);
401 ast->cdr = ast_cdr_alloc();
402 ast_cdr_setuserfield(ast, tmp2);
405 ast_log(LOG_ERROR, "Recording already started on that call.\n");
409 static int agent_start_monitoring(struct ast_channel *ast, int needlock)
411 return __agent_start_monitoring(ast, ast->tech_pvt, needlock);
414 static struct ast_frame *agent_read(struct ast_channel *ast)
416 struct agent_pvt *p = ast->tech_pvt;
417 struct ast_frame *f = NULL;
418 static struct ast_frame answer_frame = { AST_FRAME_CONTROL, AST_CONTROL_ANSWER };
420 ast_mutex_lock(&p->lock);
421 CHECK_FORMATS(ast, p);
423 ast_copy_flags(p->chan, ast, AST_FLAG_EXCEPTION);
424 p->chan->fdno = (ast->fdno == AST_AGENT_FD) ? AST_TIMING_FD : ast->fdno;
425 f = ast_read(p->chan);
429 /* If there's a channel, hang it up (if it's on a callback) make it NULL */
431 p->chan->_bridge = NULL;
432 /* Note that we don't hangup if it's not a callback because Asterisk will do it
433 for us when the PBX instance that called login finishes */
434 if (!ast_strlen_zero(p->loginchan)) {
436 ast_debug(1, "Bridge on '%s' being cleared (2)\n", p->chan->name);
437 if (p->owner->_state != AST_STATE_UP) {
438 int howlong = time(NULL) - p->start;
439 if (p->autologoff && howlong > p->autologoff) {
440 long logintime = time(NULL) - p->loginstart;
442 ast_log(LOG_NOTICE, "Agent '%s' didn't answer/confirm within %d seconds (waited %d)\n", p->name, p->autologoff, howlong);
443 agent_logoff_maintenance(p, p->loginchan, logintime, ast->uniqueid, "Autologoff");
444 if (persistent_agents)
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 ast_verb(3, "%s answered, waiting for '#' to acknowledge\n", p->chan->name);
472 /* Don't pass answer along */
477 /* Use the builtin answer frame for the
478 recording start check below. */
484 case AST_FRAME_DTMF_BEGIN:
485 /*ignore DTMF begin's as it can cause issues with queue announce files*/
486 if((!p->acknowledged && f->subclass == '#') || (f->subclass == '*' && endcall)){
491 case AST_FRAME_DTMF_END:
492 if (!p->acknowledged && (f->subclass == '#')) {
493 ast_verb(3, "%s acknowledged\n", p->chan->name);
497 } else if (f->subclass == '*' && endcall) {
498 /* terminates call */
503 case AST_FRAME_VOICE:
504 case AST_FRAME_VIDEO:
505 /* don't pass voice or video until the call is acknowledged */
506 if (!p->acknowledged) {
511 /* pass everything else on through */
517 if (p->chan && !p->chan->_bridge) {
518 if (strcasecmp(p->chan->tech->type, "Local")) {
519 p->chan->_bridge = ast;
521 ast_debug(1, "Bridge on '%s' being set to '%s' (3)\n", p->chan->name, p->chan->_bridge->name);
524 ast_mutex_unlock(&p->lock);
525 if (recordagentcalls && f == &answer_frame)
526 agent_start_monitoring(ast,0);
530 static int agent_sendhtml(struct ast_channel *ast, int subclass, const char *data, int datalen)
532 struct agent_pvt *p = ast->tech_pvt;
534 ast_mutex_lock(&p->lock);
536 res = ast_channel_sendhtml(p->chan, subclass, data, datalen);
537 ast_mutex_unlock(&p->lock);
541 static int agent_sendtext(struct ast_channel *ast, const char *text)
543 struct agent_pvt *p = ast->tech_pvt;
545 ast_mutex_lock(&p->lock);
547 res = ast_sendtext(p->chan, text);
548 ast_mutex_unlock(&p->lock);
552 static int agent_write(struct ast_channel *ast, struct ast_frame *f)
554 struct agent_pvt *p = ast->tech_pvt;
556 CHECK_FORMATS(ast, p);
557 ast_mutex_lock(&p->lock);
561 if ((f->frametype != AST_FRAME_VOICE) ||
562 (f->frametype != AST_FRAME_VIDEO) ||
563 (f->subclass == p->chan->writeformat)) {
564 res = ast_write(p->chan, f);
566 ast_debug(1, "Dropping one incompatible %s frame on '%s' to '%s'\n",
567 f->frametype == AST_FRAME_VOICE ? "audio" : "video",
568 ast->name, p->chan->name);
573 ast_mutex_unlock(&p->lock);
577 static int agent_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
579 struct agent_pvt *p = newchan->tech_pvt;
580 ast_mutex_lock(&p->lock);
581 if (p->owner != oldchan) {
582 ast_log(LOG_WARNING, "old channel wasn't %p but was %p\n", oldchan, p->owner);
583 ast_mutex_unlock(&p->lock);
587 ast_mutex_unlock(&p->lock);
591 static int agent_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen)
593 struct agent_pvt *p = ast->tech_pvt;
595 ast_mutex_lock(&p->lock);
597 res = p->chan->tech->indicate ? p->chan->tech->indicate(p->chan, condition, data, datalen) : -1;
600 ast_mutex_unlock(&p->lock);
604 static int agent_digit_begin(struct ast_channel *ast, char digit)
606 struct agent_pvt *p = ast->tech_pvt;
607 ast_mutex_lock(&p->lock);
609 ast_senddigit_begin(p->chan, digit);
611 ast_mutex_unlock(&p->lock);
615 static int agent_digit_end(struct ast_channel *ast, char digit, unsigned int duration)
617 struct agent_pvt *p = ast->tech_pvt;
618 ast_mutex_lock(&p->lock);
620 ast_senddigit_end(p->chan, digit, duration);
622 ast_mutex_unlock(&p->lock);
626 static int agent_call(struct ast_channel *ast, char *dest, int timeout)
628 struct agent_pvt *p = ast->tech_pvt;
631 ast_mutex_lock(&p->lock);
635 ast_debug(1, "Pretending to dial on pending agent\n");
636 newstate = AST_STATE_DIALING;
639 ast_log(LOG_NOTICE, "Whoa, they hung up between alloc and call... what are the odds of that?\n");
642 ast_mutex_unlock(&p->lock);
644 ast_setstate(ast, newstate);
646 } else if (!ast_strlen_zero(p->loginchan)) {
648 /* Call on this agent */
649 ast_verb(3, "outgoing agentcall, to agent '%s', on '%s'\n", p->agent, p->chan->name);
650 ast_set_callerid(p->chan,
651 ast->cid.cid_num, ast->cid.cid_name, NULL);
652 ast_channel_inherit_variables(ast, p->chan);
653 res = ast_call(p->chan, p->loginchan, 0);
655 ast_mutex_unlock(&p->lock);
658 ast_verb(3, "agent_call, call to agent '%s' call on '%s'\n", p->agent, p->chan->name);
659 ast_debug(3, "Playing beep, lang '%s'\n", p->chan->language);
660 res = ast_streamfile(p->chan, beep, p->chan->language);
661 ast_debug(3, "Played beep, result '%d'\n", res);
663 res = ast_waitstream(p->chan, "");
664 ast_debug(3, "Waited for stream, result '%d'\n", res);
667 res = ast_set_read_format(p->chan, ast_best_codec(p->chan->nativeformats));
668 ast_debug(3, "Set read format, result '%d'\n", res);
670 ast_log(LOG_WARNING, "Unable to set read format to %s\n", ast_getformatname(ast_best_codec(p->chan->nativeformats)));
677 res = ast_set_write_format(p->chan, ast_best_codec(p->chan->nativeformats));
678 ast_debug(3, "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 /*! \brief return the channel or base channel if one exists. This function assumes the channel it is called on is already locked */
715 struct ast_channel* agent_get_base_channel(struct ast_channel *chan)
717 struct agent_pvt *p = NULL;
718 struct ast_channel *base = chan;
720 /* chan is locked by the calling function */
721 if (!chan || !chan->tech_pvt) {
722 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);
731 int agent_set_base_channel(struct ast_channel *chan, struct ast_channel *base)
733 struct agent_pvt *p = NULL;
735 if (!chan || !base) {
736 ast_log(LOG_ERROR, "whoa, you need a channel (0x%ld) and a base channel (0x%ld) for setting.\n", (long)chan, (long)base);
741 ast_log(LOG_ERROR, "whoa, channel %s is missing his tech_pvt structure!!.\n", chan->name);
748 static int agent_hangup(struct ast_channel *ast)
750 struct agent_pvt *p = ast->tech_pvt;
753 ast_mutex_lock(&p->lock);
755 ast->tech_pvt = NULL;
756 p->app_sleep_cond = 1;
759 /* if they really are hung up then set start to 0 so the test
760 * later if we're called on an already downed channel
761 * doesn't cause an agent to be logged out like when
762 * agent_request() is followed immediately by agent_hangup()
763 * as in apps/app_chanisavail.c:chanavail_exec()
766 ast_debug(1, "Hangup called for state %s\n", ast_state2str(ast->_state));
767 if (p->start && (ast->_state != AST_STATE_UP)) {
768 howlong = time(NULL) - p->start;
770 } else if (ast->_state == AST_STATE_RESERVED)
775 p->chan->_bridge = NULL;
776 /* If they're dead, go ahead and hang up on the agent now */
777 if (!ast_strlen_zero(p->loginchan)) {
778 /* Store last disconnect time */
780 p->lastdisc = ast_tvadd(ast_tvnow(), ast_samp2tv(p->wrapuptime, 1000));
782 p->lastdisc = ast_tv(0,0);
784 status = pbx_builtin_getvar_helper(p->chan, "CHANLOCALSTATUS");
785 if (autologoffunavail && status && !strcasecmp(status, "CHANUNAVAIL")) {
786 long logintime = time(NULL) - p->loginstart;
788 ast_log(LOG_NOTICE, "Agent hangup: '%s' is not available now, auto logoff\n", p->name);
789 agent_logoff_maintenance(p, p->loginchan, logintime, ast->uniqueid, "Chanunavail");
791 /* Recognize the hangup and pass it along immediately */
795 ast_debug(1, "Hungup, howlong is %d, autologoff is %d\n", howlong, p->autologoff);
796 if ((p->deferlogoff) || (howlong && p->autologoff && (howlong > p->autologoff))) {
797 long logintime = time(NULL) - p->loginstart;
800 ast_log(LOG_NOTICE, "Agent '%s' didn't answer/confirm within %d seconds (waited %d)\n", p->name, p->autologoff, howlong);
802 agent_logoff_maintenance(p, p->loginchan, logintime, ast->uniqueid, "Autologoff");
803 if (persistent_agents)
806 } else if (p->dead) {
807 ast_channel_lock(p->chan);
808 ast_softhangup(p->chan, AST_SOFTHANGUP_EXPLICIT);
809 ast_channel_unlock(p->chan);
810 } else if (p->loginstart) {
811 ast_channel_lock(p->chan);
812 ast_indicate_data(p->chan, AST_CONTROL_HOLD,
814 !ast_strlen_zero(p->moh) ? strlen(p->moh) + 1 : 0);
815 ast_channel_unlock(p->chan);
818 ast_mutex_unlock(&p->lock);
820 /* Only register a device state change if the agent is still logged in */
821 if (!p->loginstart) {
822 p->loginchan[0] = '\0';
823 p->logincallerid[0] = '\0';
824 if (persistent_agents)
827 ast_device_state_changed("Agent/%s", p->agent);
831 AST_LIST_LOCK(&agents);
832 AST_LIST_REMOVE(&agents, p, list);
833 AST_LIST_UNLOCK(&agents);
835 if (p->abouttograb) {
836 /* Let the "about to grab" thread know this isn't valid anymore, and let it
839 } else if (p->dead) {
840 ast_mutex_destroy(&p->lock);
841 ast_mutex_destroy(&p->app_lock);
845 /* Not dead -- check availability now */
846 ast_mutex_lock(&p->lock);
847 /* Store last disconnect time */
848 p->lastdisc = ast_tvadd(ast_tvnow(), ast_samp2tv(p->wrapuptime, 1000));
849 ast_mutex_unlock(&p->lock);
851 /* Release ownership of the agent to other threads (presumably running the login app). */
852 if (ast_strlen_zero(p->loginchan))
853 ast_mutex_unlock(&p->app_lock);
858 static int agent_cont_sleep( void *data )
863 p = (struct agent_pvt *)data;
865 ast_mutex_lock(&p->lock);
866 res = p->app_sleep_cond;
867 if (p->lastdisc.tv_sec) {
868 if (ast_tvdiff_ms(ast_tvnow(), p->lastdisc) > 0)
871 ast_mutex_unlock(&p->lock);
874 ast_debug(5, "agent_cont_sleep() returning %d\n", res );
879 static int agent_ack_sleep(void *data)
886 /* Wait a second and look for something */
888 p = (struct agent_pvt *) data;
893 to = ast_waitfor(p->chan, to);
898 f = ast_read(p->chan);
901 if (f->frametype == AST_FRAME_DTMF)
906 ast_mutex_lock(&p->lock);
907 if (!p->app_sleep_cond) {
908 ast_mutex_unlock(&p->lock);
910 } else if (res == '#') {
911 ast_mutex_unlock(&p->lock);
914 ast_mutex_unlock(&p->lock);
920 static struct ast_channel *agent_bridgedchannel(struct ast_channel *chan, struct ast_channel *bridge)
922 struct agent_pvt *p = bridge->tech_pvt;
923 struct ast_channel *ret = NULL;
927 ret = bridge->_bridge;
928 else if (chan == bridge->_bridge)
932 ast_debug(1, "Asked for bridged channel on '%s'/'%s', returning '%s'\n", chan->name, bridge->name, ret ? ret->name : "<none>");
936 /*! \brief Create new agent channel */
937 static struct ast_channel *agent_new(struct agent_pvt *p, int state)
939 struct ast_channel *tmp;
942 ast_log(LOG_WARNING, "No channel? :(\n");
947 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);
949 tmp = ast_channel_alloc(0, state, 0, 0, "", p->chan ? p->chan->exten:"", p->chan ? p->chan->context:"", 0, "Agent/%s", p->agent);
951 ast_log(LOG_WARNING, "Unable to allocate agent channel structure\n");
955 tmp->tech = &agent_tech;
957 tmp->nativeformats = p->chan->nativeformats;
958 tmp->writeformat = p->chan->writeformat;
959 tmp->rawwriteformat = p->chan->writeformat;
960 tmp->readformat = p->chan->readformat;
961 tmp->rawreadformat = p->chan->readformat;
962 ast_string_field_set(tmp, language, p->chan->language);
963 ast_copy_string(tmp->context, p->chan->context, sizeof(tmp->context));
964 ast_copy_string(tmp->exten, p->chan->exten, sizeof(tmp->exten));
965 /* XXX Is this really all we copy form the originating channel?? */
967 tmp->nativeformats = AST_FORMAT_SLINEAR;
968 tmp->writeformat = AST_FORMAT_SLINEAR;
969 tmp->rawwriteformat = AST_FORMAT_SLINEAR;
970 tmp->readformat = AST_FORMAT_SLINEAR;
971 tmp->rawreadformat = AST_FORMAT_SLINEAR;
973 /* Safe, agentlock already held */
977 /* Wake up and wait for other applications (by definition the login app)
978 * to release this channel). Takes ownership of the agent channel
979 * to this thread only.
980 * For signalling the other thread, ast_queue_frame is used until we
981 * can safely use signals for this purpose. The pselect() needs to be
982 * implemented in the kernel for this.
984 p->app_sleep_cond = 0;
985 if(ast_strlen_zero(p->loginchan) && ast_mutex_trylock(&p->app_lock)) {
987 ast_queue_frame(p->chan, &ast_null_frame);
988 ast_mutex_unlock(&p->lock); /* For other thread to read the condition. */
989 ast_mutex_lock(&p->app_lock);
990 ast_mutex_lock(&p->lock);
992 ast_log(LOG_WARNING, "Agent disconnected while we were connecting the call\n");
994 tmp->tech_pvt = NULL;
995 p->app_sleep_cond = 1;
996 ast_channel_free( tmp );
997 ast_mutex_unlock(&p->lock); /* For other thread to read the condition. */
998 ast_mutex_unlock(&p->app_lock);
1001 } else if (!ast_strlen_zero(p->loginchan)) {
1003 ast_queue_frame(p->chan, &ast_null_frame);
1005 ast_log(LOG_WARNING, "Agent disconnected while we were connecting the call\n");
1007 tmp->tech_pvt = NULL;
1008 p->app_sleep_cond = 1;
1009 ast_channel_free( tmp );
1010 ast_mutex_unlock(&p->lock); /* For other thread to read the condition. */
1015 ast_indicate(p->chan, AST_CONTROL_UNHOLD);
1016 p->owning_app = pthread_self();
1017 /* After the above step, there should not be any blockers. */
1019 if (ast_test_flag(p->chan, AST_FLAG_BLOCKING)) {
1020 ast_log( LOG_ERROR, "A blocker exists after agent channel ownership acquired\n" );
1021 ast_assert(ast_test_flag(p->chan, AST_FLAG_BLOCKING) == 0);
1029 * Read configuration data. The file named agents.conf.
1031 * \returns Always 0, or so it seems.
1033 static int read_agent_config(int reload)
1035 struct ast_config *cfg;
1036 struct ast_config *ucfg;
1037 struct ast_variable *v;
1038 struct agent_pvt *p;
1039 const char *general_val;
1040 const char *catname;
1041 const char *hasagent;
1043 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
1050 cfg = ast_config_load(config, config_flags);
1052 ast_log(LOG_NOTICE, "No agent configuration found -- agent support disabled\n");
1054 } else if (cfg == CONFIG_STATUS_FILEUNCHANGED)
1056 AST_LIST_LOCK(&agents);
1057 AST_LIST_TRAVERSE(&agents, p, list) {
1060 strcpy(moh, "default");
1061 /* set the default recording values */
1062 recordagentcalls = 0;
1063 strcpy(recordformat, "wav");
1064 strcpy(recordformatext, "wav");
1065 urlprefix[0] = '\0';
1066 savecallsin[0] = '\0';
1068 /* Read in [general] section for persistence */
1069 if ((general_val = ast_variable_retrieve(cfg, "general", "persistentagents")))
1070 persistent_agents = ast_true(general_val);
1071 multiplelogin = ast_true(ast_variable_retrieve(cfg, "general", "multiplelogin"));
1073 /* Read in the [agents] section */
1074 v = ast_variable_browse(cfg, "agents");
1076 /* Create the interface list */
1077 if (!strcasecmp(v->name, "agent")) {
1078 add_agent(v->value, 0);
1079 } else if (!strcasecmp(v->name, "group")) {
1080 group = ast_get_group(v->value);
1081 } else if (!strcasecmp(v->name, "autologoff")) {
1082 autologoff = atoi(v->value);
1085 } else if (!strcasecmp(v->name, "ackcall")) {
1086 if (!strcasecmp(v->value, "always"))
1088 else if (ast_true(v->value))
1092 } else if (!strcasecmp(v->name, "endcall")) {
1093 endcall = ast_true(v->value);
1094 } else if (!strcasecmp(v->name, "wrapuptime")) {
1095 wrapuptime = atoi(v->value);
1098 } else if (!strcasecmp(v->name, "maxlogintries") && !ast_strlen_zero(v->value)) {
1099 maxlogintries = atoi(v->value);
1100 if (maxlogintries < 0)
1102 } else if (!strcasecmp(v->name, "goodbye") && !ast_strlen_zero(v->value)) {
1103 strcpy(agentgoodbye,v->value);
1104 } else if (!strcasecmp(v->name, "musiconhold")) {
1105 ast_copy_string(moh, v->value, sizeof(moh));
1106 } else if (!strcasecmp(v->name, "updatecdr")) {
1107 if (ast_true(v->value))
1111 } else if (!strcasecmp(v->name, "autologoffunavail")) {
1112 if (ast_true(v->value))
1113 autologoffunavail = 1;
1115 autologoffunavail = 0;
1116 } else if (!strcasecmp(v->name, "recordagentcalls")) {
1117 recordagentcalls = ast_true(v->value);
1118 } else if (!strcasecmp(v->name, "recordformat")) {
1119 ast_copy_string(recordformat, v->value, sizeof(recordformat));
1120 if (!strcasecmp(v->value, "wav49"))
1121 strcpy(recordformatext, "WAV");
1123 ast_copy_string(recordformatext, v->value, sizeof(recordformatext));
1124 } else if (!strcasecmp(v->name, "urlprefix")) {
1125 ast_copy_string(urlprefix, v->value, sizeof(urlprefix));
1126 if (urlprefix[strlen(urlprefix) - 1] != '/')
1127 strncat(urlprefix, "/", sizeof(urlprefix) - strlen(urlprefix) - 1);
1128 } else if (!strcasecmp(v->name, "savecallsin")) {
1129 if (v->value[0] == '/')
1130 ast_copy_string(savecallsin, v->value, sizeof(savecallsin));
1132 snprintf(savecallsin, sizeof(savecallsin) - 2, "/%s", v->value);
1133 if (savecallsin[strlen(savecallsin) - 1] != '/')
1134 strncat(savecallsin, "/", sizeof(savecallsin) - strlen(savecallsin) - 1);
1135 } else if (!strcasecmp(v->name, "custom_beep")) {
1136 ast_copy_string(beep, v->value, sizeof(beep));
1140 if ((ucfg = ast_config_load("users.conf", config_flags)) && ucfg != CONFIG_STATUS_FILEUNCHANGED) {
1141 genhasagent = ast_true(ast_variable_retrieve(ucfg, "general", "hasagent"));
1142 catname = ast_category_browse(ucfg, NULL);
1144 if (strcasecmp(catname, "general")) {
1145 hasagent = ast_variable_retrieve(ucfg, catname, "hasagent");
1146 if (ast_true(hasagent) || (!hasagent && genhasagent)) {
1148 const char *fullname = ast_variable_retrieve(ucfg, catname, "fullname");
1149 const char *secret = ast_variable_retrieve(ucfg, catname, "secret");
1154 snprintf(tmp, sizeof(tmp), "%s,%s,%s", catname, secret,fullname);
1158 catname = ast_category_browse(ucfg, catname);
1160 ast_config_destroy(ucfg);
1162 AST_LIST_TRAVERSE_SAFE_BEGIN(&agents, p, list) {
1164 AST_LIST_REMOVE_CURRENT(list);
1165 /* Destroy if appropriate */
1168 ast_mutex_destroy(&p->lock);
1169 ast_mutex_destroy(&p->app_lock);
1172 /* Cause them to hang up */
1173 ast_softhangup(p->chan, AST_SOFTHANGUP_EXPLICIT);
1178 AST_LIST_TRAVERSE_SAFE_END;
1179 AST_LIST_UNLOCK(&agents);
1180 ast_config_destroy(cfg);
1184 static int check_availability(struct agent_pvt *newlyavailable, int needlock)
1186 struct ast_channel *chan=NULL, *parent=NULL;
1187 struct agent_pvt *p;
1190 ast_debug(1, "Checking availability of '%s'\n", newlyavailable->agent);
1192 AST_LIST_LOCK(&agents);
1193 AST_LIST_TRAVERSE(&agents, p, list) {
1194 if (p == newlyavailable) {
1197 ast_mutex_lock(&p->lock);
1198 if (!p->abouttograb && p->pending && ((p->group && (newlyavailable->group & p->group)) || !strcmp(p->agent, newlyavailable->agent))) {
1199 ast_debug(1, "Call '%s' looks like a winner for agent '%s'\n", p->owner->name, newlyavailable->agent);
1200 /* We found a pending call, time to merge */
1201 chan = agent_new(newlyavailable, AST_STATE_DOWN);
1204 ast_mutex_unlock(&p->lock);
1207 ast_mutex_unlock(&p->lock);
1210 AST_LIST_UNLOCK(&agents);
1211 if (parent && chan) {
1212 if (newlyavailable->ackcall > 1) {
1213 /* Don't do beep here */
1216 ast_debug(3, "Playing beep, lang '%s'\n", newlyavailable->chan->language);
1217 res = ast_streamfile(newlyavailable->chan, beep, newlyavailable->chan->language);
1218 ast_debug(3, "Played beep, result '%d'\n", res);
1220 res = ast_waitstream(newlyavailable->chan, "");
1221 ast_debug(1, "Waited for stream, result '%d'\n", res);
1225 /* Note -- parent may have disappeared */
1226 if (p->abouttograb) {
1227 newlyavailable->acknowledged = 1;
1228 /* Safe -- agent lock already held */
1229 ast_setstate(parent, AST_STATE_UP);
1230 ast_setstate(chan, AST_STATE_UP);
1231 ast_copy_string(parent->context, chan->context, sizeof(parent->context));
1232 /* Go ahead and mark the channel as a zombie so that masquerade will
1233 destroy it for us, and we need not call ast_hangup */
1234 ast_set_flag(chan, AST_FLAG_ZOMBIE);
1235 ast_channel_masquerade(parent, chan);
1238 ast_debug(1, "Sneaky, parent disappeared in the mean time...\n");
1239 agent_cleanup(newlyavailable);
1242 ast_debug(1, "Ugh... Agent hung up at exactly the wrong time\n");
1243 agent_cleanup(newlyavailable);
1249 static int check_beep(struct agent_pvt *newlyavailable, int needlock)
1251 struct agent_pvt *p;
1254 ast_debug(1, "Checking beep availability of '%s'\n", newlyavailable->agent);
1256 AST_LIST_LOCK(&agents);
1257 AST_LIST_TRAVERSE(&agents, p, list) {
1258 if (p == newlyavailable) {
1261 ast_mutex_lock(&p->lock);
1262 if (!p->abouttograb && p->pending && ((p->group && (newlyavailable->group & p->group)) || !strcmp(p->agent, newlyavailable->agent))) {
1263 ast_debug(1, "Call '%s' looks like a would-be winner for agent '%s'\n", p->owner->name, newlyavailable->agent);
1264 ast_mutex_unlock(&p->lock);
1267 ast_mutex_unlock(&p->lock);
1270 AST_LIST_UNLOCK(&agents);
1272 ast_mutex_unlock(&newlyavailable->lock);
1273 ast_debug(3, "Playing beep, lang '%s'\n", newlyavailable->chan->language);
1274 res = ast_streamfile(newlyavailable->chan, beep, newlyavailable->chan->language);
1275 ast_debug(1, "Played beep, result '%d'\n", res);
1277 res = ast_waitstream(newlyavailable->chan, "");
1278 ast_debug(1, "Waited for stream, result '%d'\n", res);
1280 ast_mutex_lock(&newlyavailable->lock);
1285 /*! \brief Part of the Asterisk PBX interface */
1286 static struct ast_channel *agent_request(const char *type, int format, void *data, int *cause)
1288 struct agent_pvt *p;
1289 struct ast_channel *chan = NULL;
1291 ast_group_t groupmatch;
1298 if ((s[0] == '@') && (sscanf(s + 1, "%d", &groupoff) == 1)) {
1299 groupmatch = (1 << groupoff);
1300 } else if ((s[0] == ':') && (sscanf(s + 1, "%d", &groupoff) == 1)) {
1301 groupmatch = (1 << groupoff);
1306 /* Check actual logged in agents first */
1307 AST_LIST_LOCK(&agents);
1308 AST_LIST_TRAVERSE(&agents, p, list) {
1309 ast_mutex_lock(&p->lock);
1310 if (!p->pending && ((groupmatch && (p->group & groupmatch)) || !strcmp(data, p->agent)) &&
1311 ast_strlen_zero(p->loginchan)) {
1315 if (!p->lastdisc.tv_sec || (tv.tv_sec >= p->lastdisc.tv_sec)) {
1316 p->lastdisc = ast_tv(0, 0);
1317 /* Agent must be registered, but not have any active call, and not be in a waiting state */
1318 if (!p->owner && p->chan) {
1320 chan = agent_new(p, AST_STATE_DOWN);
1323 ast_mutex_unlock(&p->lock);
1328 ast_mutex_unlock(&p->lock);
1331 AST_LIST_TRAVERSE(&agents, p, list) {
1332 ast_mutex_lock(&p->lock);
1333 if (!p->pending && ((groupmatch && (p->group & groupmatch)) || !strcmp(data, p->agent))) {
1334 if (p->chan || !ast_strlen_zero(p->loginchan))
1338 ast_log(LOG_NOTICE, "Time now: %ld, Time of lastdisc: %ld\n", tv.tv_sec, p->lastdisc.tv_sec);
1340 if (!p->lastdisc.tv_sec || (tv.tv_sec >= p->lastdisc.tv_sec)) {
1341 p->lastdisc = ast_tv(0, 0);
1342 /* Agent must be registered, but not have any active call, and not be in a waiting state */
1343 if (!p->owner && p->chan) {
1344 /* Could still get a fixed agent */
1345 chan = agent_new(p, AST_STATE_DOWN);
1346 } else if (!p->owner && !ast_strlen_zero(p->loginchan)) {
1347 /* Adjustable agent */
1348 p->chan = ast_request("Local", format, p->loginchan, cause);
1350 chan = agent_new(p, AST_STATE_DOWN);
1353 ast_mutex_unlock(&p->lock);
1358 ast_mutex_unlock(&p->lock);
1362 if (!chan && waitforagent) {
1363 /* No agent available -- but we're requesting to wait for one.
1364 Allocate a place holder */
1366 ast_debug(1, "Creating place holder for '%s'\n", s);
1367 p = add_agent(data, 1);
1368 p->group = groupmatch;
1369 chan = agent_new(p, AST_STATE_DOWN);
1371 ast_log(LOG_WARNING, "Weird... Fix this to drop the unused pending agent\n");
1373 ast_debug(1, "Not creating place holder for '%s' since nobody logged in\n", s);
1376 *cause = hasagent ? AST_CAUSE_BUSY : AST_CAUSE_UNREGISTERED;
1377 AST_LIST_UNLOCK(&agents);
1381 static force_inline int powerof(unsigned int d)
1392 * Lists agents and their status to the Manager API.
1393 * It is registered on load_module() and it gets called by the manager backend.
1397 * \sa action_agent_logoff(), load_module().
1399 static int action_agents(struct mansession *s, const struct message *m)
1401 const char *id = astman_get_header(m,"ActionID");
1402 char idText[256] = "";
1404 struct agent_pvt *p;
1405 char *username = NULL;
1406 char *loginChan = NULL;
1407 char *talkingto = NULL;
1408 char *talkingtoChan = NULL;
1409 char *status = NULL;
1411 if (!ast_strlen_zero(id))
1412 snprintf(idText, sizeof(idText) ,"ActionID: %s\r\n", id);
1413 astman_send_ack(s, m, "Agents will follow");
1414 AST_LIST_LOCK(&agents);
1415 AST_LIST_TRAVERSE(&agents, p, list) {
1416 ast_mutex_lock(&p->lock);
1419 AGENT_LOGGEDOFF - Agent isn't logged in
1420 AGENT_IDLE - Agent is logged in, and waiting for call
1421 AGENT_ONCALL - Agent is logged in, and on a call
1422 AGENT_UNKNOWN - Don't know anything about agent. Shouldn't ever get this. */
1424 username = S_OR(p->name, "None");
1426 /* Set a default status. It 'should' get changed. */
1427 status = "AGENT_UNKNOWN";
1429 if (!ast_strlen_zero(p->loginchan) && !p->chan) {
1430 loginChan = p->loginchan;
1432 talkingtoChan = "n/a";
1433 status = "AGENT_IDLE";
1434 if (p->acknowledged) {
1435 snprintf(chanbuf, sizeof(chanbuf), " %s (Confirmed)", p->loginchan);
1436 loginChan = chanbuf;
1438 } else if (p->chan) {
1439 loginChan = ast_strdupa(p->chan->name);
1440 if (p->owner && p->owner->_bridge) {
1441 talkingto = p->chan->cid.cid_num;
1442 if (ast_bridged_channel(p->owner))
1443 talkingtoChan = ast_strdupa(ast_bridged_channel(p->owner)->name);
1445 talkingtoChan = "n/a";
1446 status = "AGENT_ONCALL";
1449 talkingtoChan = "n/a";
1450 status = "AGENT_IDLE";
1455 talkingtoChan = "n/a";
1456 status = "AGENT_LOGGEDOFF";
1459 astman_append(s, "Event: Agents\r\n"
1463 "LoggedInChan: %s\r\n"
1464 "LoggedInTime: %d\r\n"
1466 "TalkingToChan: %s\r\n"
1469 p->agent, username, status, loginChan, (int)p->loginstart, talkingto, talkingtoChan, idText);
1470 ast_mutex_unlock(&p->lock);
1472 AST_LIST_UNLOCK(&agents);
1473 astman_append(s, "Event: AgentsComplete\r\n"
1479 static void agent_logoff_maintenance(struct agent_pvt *p, char *loginchan, long logintime, const char *uniqueid, char *logcommand)
1482 char agent[AST_MAX_AGENT];
1484 if (!ast_strlen_zero(logcommand))
1487 tmp = ast_strdupa("");
1489 snprintf(agent, sizeof(agent), "Agent/%s", p->agent);
1491 if (!ast_strlen_zero(uniqueid)) {
1492 manager_event(EVENT_FLAG_AGENT, "Agentcallbacklogoff",
1496 "Logintime: %ld\r\n"
1498 p->agent, tmp, loginchan, logintime, uniqueid);
1500 manager_event(EVENT_FLAG_AGENT, "Agentcallbacklogoff",
1504 "Logintime: %ld\r\n",
1505 p->agent, tmp, loginchan, logintime);
1508 ast_queue_log("NONE", ast_strlen_zero(uniqueid) ? "NONE" : uniqueid, agent, "AGENTCALLBACKLOGOFF", "%s|%ld|%s", loginchan, logintime, tmp);
1509 set_agentbycallerid(p->logincallerid, NULL);
1510 p->loginchan[0] ='\0';
1511 p->logincallerid[0] = '\0';
1512 ast_device_state_changed("Agent/%s", p->agent);
1513 if (persistent_agents)
1518 static int agent_logoff(const char *agent, int soft)
1520 struct agent_pvt *p;
1522 int ret = -1; /* Return -1 if no agent if found */
1524 AST_LIST_LOCK(&agents);
1525 AST_LIST_TRAVERSE(&agents, p, list) {
1526 if (!strcasecmp(p->agent, agent)) {
1528 if (p->owner || p->chan) {
1530 ast_mutex_lock(&p->lock);
1532 while (p->owner && ast_channel_trylock(p->owner)) {
1533 DEADLOCK_AVOIDANCE(&p->lock);
1536 ast_softhangup(p->owner, AST_SOFTHANGUP_EXPLICIT);
1537 ast_channel_unlock(p->owner);
1540 while (p->chan && ast_channel_trylock(p->chan)) {
1541 DEADLOCK_AVOIDANCE(&p->lock);
1544 ast_softhangup(p->chan, AST_SOFTHANGUP_EXPLICIT);
1545 ast_channel_unlock(p->chan);
1548 ast_mutex_unlock(&p->lock);
1552 logintime = time(NULL) - p->loginstart;
1554 agent_logoff_maintenance(p, p->loginchan, logintime, NULL, "CommandLogoff");
1559 AST_LIST_UNLOCK(&agents);
1564 static char *agent_logoff_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1571 e->command = "agent logoff";
1573 "Usage: agent logoff <channel> [soft]\n"
1574 " Sets an agent as no longer logged in.\n"
1575 " If 'soft' is specified, do not hangup existing calls.\n";
1578 return complete_agent_logoff_cmd(a->line, a->word, a->pos, a->n);
1581 if (a->argc < 3 || a->argc > 4)
1582 return CLI_SHOWUSAGE;
1583 if (a->argc == 4 && strcasecmp(a->argv[3], "soft"))
1584 return CLI_SHOWUSAGE;
1586 agent = a->argv[2] + 6;
1587 ret = agent_logoff(agent, a->argc == 4);
1589 ast_cli(a->fd, "Logging out %s\n", agent);
1595 * Sets an agent as no longer logged in in the Manager API.
1596 * It is registered on load_module() and it gets called by the manager backend.
1600 * \sa action_agents(), load_module().
1602 static int action_agent_logoff(struct mansession *s, const struct message *m)
1604 const char *agent = astman_get_header(m, "Agent");
1605 const char *soft_s = astman_get_header(m, "Soft"); /* "true" is don't hangup */
1607 int ret; /* return value of agent_logoff */
1609 if (ast_strlen_zero(agent)) {
1610 astman_send_error(s, m, "No agent specified");
1614 soft = ast_true(soft_s) ? 1 : 0;
1615 ret = agent_logoff(agent, soft);
1617 astman_send_ack(s, m, "Agent logged out");
1619 astman_send_error(s, m, "No such agent");
1624 static char *complete_agent_logoff_cmd(const char *line, const char *word, int pos, int state)
1629 struct agent_pvt *p;
1630 char name[AST_MAX_AGENT];
1631 int which = 0, len = strlen(word);
1633 AST_LIST_LOCK(&agents);
1634 AST_LIST_TRAVERSE(&agents, p, list) {
1635 snprintf(name, sizeof(name), "Agent/%s", p->agent);
1636 if (!strncasecmp(word, name, len) && p->loginstart && ++which > state) {
1637 ret = ast_strdup(name);
1641 AST_LIST_UNLOCK(&agents);
1642 } else if (pos == 3 && state == 0)
1643 return ast_strdup("soft");
1649 * Show agents in cli.
1651 static char *agents_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1653 struct agent_pvt *p;
1654 char username[AST_MAX_BUF];
1655 char location[AST_MAX_BUF] = "";
1656 char talkingto[AST_MAX_BUF] = "";
1657 char moh[AST_MAX_BUF];
1658 int count_agents = 0; /*!< Number of agents configured */
1659 int online_agents = 0; /*!< Number of online agents */
1660 int offline_agents = 0; /*!< Number of offline agents */
1664 e->command = "agent show";
1666 "Usage: agent show\n"
1667 " Provides summary information on agents.\n";
1674 return CLI_SHOWUSAGE;
1676 AST_LIST_LOCK(&agents);
1677 AST_LIST_TRAVERSE(&agents, p, list) {
1678 ast_mutex_lock(&p->lock);
1681 ast_cli(a->fd, "-- Pending call to group %d\n", powerof(p->group));
1683 ast_cli(a->fd, "-- Pending call to agent %s\n", p->agent);
1685 if (!ast_strlen_zero(p->name))
1686 snprintf(username, sizeof(username), "(%s) ", p->name);
1690 snprintf(location, sizeof(location), "logged in on %s", p->chan->name);
1691 if (p->owner && ast_bridged_channel(p->owner))
1692 snprintf(talkingto, sizeof(talkingto), " talking to %s", ast_bridged_channel(p->owner)->name);
1694 strcpy(talkingto, " is idle");
1696 } else if (!ast_strlen_zero(p->loginchan)) {
1697 if (ast_tvdiff_ms(ast_tvnow(), p->lastdisc) > 0 || !(p->lastdisc.tv_sec))
1698 snprintf(location, sizeof(location) - 20, "available at '%s'", p->loginchan);
1700 snprintf(location, sizeof(location) - 20, "wrapping up at '%s'", p->loginchan);
1701 talkingto[0] = '\0';
1703 if (p->acknowledged)
1704 strncat(location, " (Confirmed)", sizeof(location) - strlen(location) - 1);
1706 strcpy(location, "not logged in");
1707 talkingto[0] = '\0';
1710 if (!ast_strlen_zero(p->moh))
1711 snprintf(moh, sizeof(moh), " (musiconhold is '%s')", p->moh);
1712 ast_cli(a->fd, "%-12.12s %s%s%s%s\n", p->agent,
1713 username, location, talkingto, moh);
1716 ast_mutex_unlock(&p->lock);
1718 AST_LIST_UNLOCK(&agents);
1719 if ( !count_agents )
1720 ast_cli(a->fd, "No Agents are configured in %s\n",config);
1722 ast_cli(a->fd, "%d agents configured [%d online , %d offline]\n",count_agents, online_agents, offline_agents);
1723 ast_cli(a->fd, "\n");
1729 static char *agents_show_online(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1731 struct agent_pvt *p;
1732 char username[AST_MAX_BUF];
1733 char location[AST_MAX_BUF] = "";
1734 char talkingto[AST_MAX_BUF] = "";
1735 char moh[AST_MAX_BUF];
1736 int count_agents = 0; /* Number of agents configured */
1737 int online_agents = 0; /* Number of online agents */
1738 int agent_status = 0; /* 0 means offline, 1 means online */
1742 e->command = "agent show online";
1744 "Usage: agent show online\n"
1745 " Provides a list of all online agents.\n";
1752 return CLI_SHOWUSAGE;
1754 AST_LIST_LOCK(&agents);
1755 AST_LIST_TRAVERSE(&agents, p, list) {
1756 agent_status = 0; /* reset it to offline */
1757 ast_mutex_lock(&p->lock);
1758 if (!ast_strlen_zero(p->name))
1759 snprintf(username, sizeof(username), "(%s) ", p->name);
1763 snprintf(location, sizeof(location), "logged in on %s", p->chan->name);
1764 if (p->owner && ast_bridged_channel(p->owner))
1765 snprintf(talkingto, sizeof(talkingto), " talking to %s", ast_bridged_channel(p->owner)->name);
1767 strcpy(talkingto, " is idle");
1770 } else if (!ast_strlen_zero(p->loginchan)) {
1771 snprintf(location, sizeof(location) - 20, "available at '%s'", p->loginchan);
1772 talkingto[0] = '\0';
1775 if (p->acknowledged)
1776 strncat(location, " (Confirmed)", sizeof(location) - strlen(location) - 1);
1778 if (!ast_strlen_zero(p->moh))
1779 snprintf(moh, sizeof(moh), " (musiconhold is '%s')", p->moh);
1781 ast_cli(a->fd, "%-12.12s %s%s%s%s\n", p->agent, username, location, talkingto, moh);
1783 ast_mutex_unlock(&p->lock);
1785 AST_LIST_UNLOCK(&agents);
1787 ast_cli(a->fd, "No Agents are configured in %s\n", config);
1789 ast_cli(a->fd, "%d agents online\n", online_agents);
1790 ast_cli(a->fd, "\n");
1794 static const char agent_logoff_usage[] =
1795 "Usage: agent logoff <channel> [soft]\n"
1796 " Sets an agent as no longer logged in.\n"
1797 " If 'soft' is specified, do not hangup existing calls.\n";
1799 static struct ast_cli_entry cli_agents[] = {
1800 AST_CLI_DEFINE(agents_show, "Show status of agents"),
1801 AST_CLI_DEFINE(agents_show_online, "Show all online agents"),
1802 AST_CLI_DEFINE(agent_logoff_cmd, "Sets an agent offline"),
1806 * Called by the AgentLogin application (from the dial plan).
1808 * \brief Log in agent application.
1813 * \sa agentmonitoroutgoing_exec(), load_module().
1815 static int login_exec(struct ast_channel *chan, void *data)
1819 int max_login_tries = maxlogintries;
1820 struct agent_pvt *p;
1821 struct ast_module_user *u;
1822 int login_state = 0;
1823 char user[AST_MAX_AGENT] = "";
1824 char pass[AST_MAX_AGENT];
1825 char agent[AST_MAX_AGENT] = "";
1826 char xpass[AST_MAX_AGENT] = "";
1829 AST_DECLARE_APP_ARGS(args,
1830 AST_APP_ARG(agent_id);
1831 AST_APP_ARG(options);
1832 AST_APP_ARG(extension);
1834 const char *tmpoptions = NULL;
1835 int play_announcement = 1;
1836 char agent_goodbye[AST_MAX_FILENAME_LEN];
1837 int update_cdr = updatecdr;
1838 char *filename = "agent-loginok";
1840 u = ast_module_user_add(chan);
1842 parse = ast_strdupa(data);
1844 AST_STANDARD_APP_ARGS(args, parse);
1846 ast_copy_string(agent_goodbye, agentgoodbye, sizeof(agent_goodbye));
1848 /* Set Channel Specific Login Overrides */
1849 if (!ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENTLMAXLOGINTRIES"))) {
1850 max_login_tries = atoi(pbx_builtin_getvar_helper(chan, "AGENTMAXLOGINTRIES"));
1851 if (max_login_tries < 0)
1852 max_login_tries = 0;
1853 tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTMAXLOGINTRIES");
1854 ast_verb(3, "Saw variable AGENTMAXLOGINTRIES=%s, setting max_login_tries to: %d on Channel '%s'.\n",tmpoptions,max_login_tries,chan->name);
1856 if (!ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENTUPDATECDR"))) {
1857 if (ast_true(pbx_builtin_getvar_helper(chan, "AGENTUPDATECDR")))
1861 tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTUPDATECDR");
1862 ast_verb(3, "Saw variable AGENTUPDATECDR=%s, setting update_cdr to: %d on Channel '%s'.\n",tmpoptions,update_cdr,chan->name);
1864 if (!ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENTGOODBYE"))) {
1865 strcpy(agent_goodbye, pbx_builtin_getvar_helper(chan, "AGENTGOODBYE"));
1866 tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTGOODBYE");
1867 ast_verb(3, "Saw variable AGENTGOODBYE=%s, setting agent_goodbye to: %s on Channel '%s'.\n",tmpoptions,agent_goodbye,chan->name);
1869 /* End Channel Specific Login Overrides */
1871 if (!ast_strlen_zero(args.options)) {
1872 if (strchr(args.options, 's')) {
1873 play_announcement = 0;
1877 if (chan->_state != AST_STATE_UP)
1878 res = ast_answer(chan);
1880 if (!ast_strlen_zero(args.agent_id))
1881 ast_copy_string(user, args.agent_id, AST_MAX_AGENT);
1883 res = ast_app_getdata(chan, "agent-user", user, sizeof(user) - 1, 0);
1885 while (!res && (max_login_tries==0 || tries < max_login_tries)) {
1887 /* Check for password */
1888 AST_LIST_LOCK(&agents);
1889 AST_LIST_TRAVERSE(&agents, p, list) {
1890 if (!strcmp(p->agent, user) && !p->pending)
1891 ast_copy_string(xpass, p->password, sizeof(xpass));
1893 AST_LIST_UNLOCK(&agents);
1895 if (!ast_strlen_zero(xpass))
1896 res = ast_app_getdata(chan, "agent-pass", pass, sizeof(pass) - 1, 0);
1900 errmsg = "agent-incorrect";
1903 ast_log(LOG_NOTICE, "user: %s, pass: %s\n", user, pass);
1906 /* Check again for accuracy */
1907 AST_LIST_LOCK(&agents);
1908 AST_LIST_TRAVERSE(&agents, p, list) {
1909 ast_mutex_lock(&p->lock);
1910 if (!strcmp(p->agent, user) &&
1911 !strcmp(p->password, pass) && !p->pending) {
1912 login_state = 1; /* Successful Login */
1914 /* Ensure we can't be gotten until we're done */
1915 p->lastdisc = ast_tvnow();
1916 p->lastdisc.tv_sec++;
1918 /* Set Channel Specific Agent Overrides */
1919 if (!ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENTACKCALL"))) {
1920 if (!strcasecmp(pbx_builtin_getvar_helper(chan, "AGENTACKCALL"), "always"))
1922 else if (ast_true(pbx_builtin_getvar_helper(chan, "AGENTACKCALL")))
1926 tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTACKCALL");
1927 ast_verb(3, "Saw variable AGENTACKCALL=%s, setting ackcall to: %d for Agent '%s'.\n",tmpoptions,p->ackcall,p->agent);
1929 if (!ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF"))) {
1930 p->autologoff = atoi(pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF"));
1931 if (p->autologoff < 0)
1933 tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF");
1934 ast_verb(3, "Saw variable AGENTAUTOLOGOFF=%s, setting autologff to: %d for Agent '%s'.\n",tmpoptions,p->autologoff,p->agent);
1936 if (!ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME"))) {
1937 p->wrapuptime = atoi(pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME"));
1938 if (p->wrapuptime < 0)
1940 tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME");
1941 ast_verb(3, "Saw variable AGENTWRAPUPTIME=%s, setting wrapuptime to: %d for Agent '%s'.\n",tmpoptions,p->wrapuptime,p->agent);
1943 /* End Channel Specific Agent Overrides */
1946 snprintf(agent, sizeof(agent), "Agent/%s", p->agent);
1948 p->loginchan[0] = '\0';
1949 p->logincallerid[0] = '\0';
1950 p->acknowledged = 0;
1952 ast_mutex_unlock(&p->lock);
1953 AST_LIST_UNLOCK(&agents);
1954 if( !res && play_announcement==1 )
1955 res = ast_streamfile(chan, filename, chan->language);
1957 ast_waitstream(chan, "");
1958 AST_LIST_LOCK(&agents);
1959 ast_mutex_lock(&p->lock);
1961 res = ast_set_read_format(chan, ast_best_codec(chan->nativeformats));
1963 ast_log(LOG_WARNING, "Unable to set read format to %d\n", ast_best_codec(chan->nativeformats));
1966 res = ast_set_write_format(chan, ast_best_codec(chan->nativeformats));
1968 ast_log(LOG_WARNING, "Unable to set write format to %d\n", ast_best_codec(chan->nativeformats));
1970 /* Check once more just in case */
1974 ast_indicate_data(chan, AST_CONTROL_HOLD,
1976 !ast_strlen_zero(p->moh) ? strlen(p->moh) + 1 : 0);
1977 if (p->loginstart == 0)
1978 time(&p->loginstart);
1979 manager_event(EVENT_FLAG_AGENT, "Agentlogin",
1983 p->agent, chan->name, chan->uniqueid);
1984 if (update_cdr && chan->cdr)
1985 snprintf(chan->cdr->channel, sizeof(chan->cdr->channel), "Agent/%s", p->agent);
1986 ast_queue_log("NONE", chan->uniqueid, agent, "AGENTLOGIN", "%s", chan->name);
1987 ast_verb(2, "Agent '%s' logged in (format %s/%s)\n", p->agent,
1988 ast_getformatname(chan->readformat), ast_getformatname(chan->writeformat));
1989 /* Login this channel and wait for it to go away */
1994 check_availability(p, 0);
1995 ast_mutex_unlock(&p->lock);
1996 AST_LIST_UNLOCK(&agents);
1997 ast_device_state_changed("Agent/%s", p->agent);
1999 ast_mutex_lock(&p->lock);
2000 if (p->deferlogoff && p->chan) {
2001 ast_softhangup(p->chan, AST_SOFTHANGUP_EXPLICIT);
2004 if (p->chan != chan)
2006 ast_mutex_unlock(&p->lock);
2007 /* Yield here so other interested threads can kick in. */
2012 AST_LIST_LOCK(&agents);
2013 ast_mutex_lock(&p->lock);
2014 if (p->lastdisc.tv_sec) {
2015 if (ast_tvdiff_ms(ast_tvnow(), p->lastdisc) > 0) {
2016 ast_debug(1, "Wrapup time for %s expired!\n", p->agent);
2017 p->lastdisc = ast_tv(0, 0);
2018 ast_device_state_changed("Agent/%s", p->agent);
2022 check_availability(p, 0);
2025 ast_mutex_unlock(&p->lock);
2026 AST_LIST_UNLOCK(&agents);
2027 /* Synchronize channel ownership between call to agent and itself. */
2028 ast_mutex_lock( &p->app_lock );
2029 ast_mutex_lock(&p->lock);
2030 p->owning_app = pthread_self();
2031 ast_mutex_unlock(&p->lock);
2033 res = agent_ack_sleep(p);
2035 res = ast_safe_sleep_conditional( chan, 1000, agent_cont_sleep, p );
2036 ast_mutex_unlock( &p->app_lock );
2037 if ((p->ackcall > 1) && (res == 1)) {
2038 AST_LIST_LOCK(&agents);
2039 ast_mutex_lock(&p->lock);
2040 check_availability(p, 0);
2041 ast_mutex_unlock(&p->lock);
2042 AST_LIST_UNLOCK(&agents);
2047 ast_mutex_lock(&p->lock);
2048 if (res && p->owner)
2049 ast_log(LOG_WARNING, "Huh? We broke out when there was still an owner?\n");
2050 /* Log us off if appropriate */
2051 if (p->chan == chan)
2053 p->acknowledged = 0;
2054 logintime = time(NULL) - p->loginstart;
2056 ast_mutex_unlock(&p->lock);
2057 manager_event(EVENT_FLAG_AGENT, "Agentlogoff",
2059 "Logintime: %ld\r\n"
2061 p->agent, logintime, chan->uniqueid);
2062 ast_queue_log("NONE", chan->uniqueid, agent, "AGENTLOGOFF", "%s|%ld", chan->name, logintime);
2063 ast_verb(2, "Agent '%s' logged out\n", p->agent);
2064 /* If there is no owner, go ahead and kill it now */
2065 ast_device_state_changed("Agent/%s", p->agent);
2066 if (p->dead && !p->owner) {
2067 ast_mutex_destroy(&p->lock);
2068 ast_mutex_destroy(&p->app_lock);
2073 ast_mutex_unlock(&p->lock);
2078 ast_mutex_unlock(&p->lock);
2079 errmsg = "agent-alreadyon";
2084 ast_mutex_unlock(&p->lock);
2087 AST_LIST_UNLOCK(&agents);
2089 if (!res && (max_login_tries==0 || tries < max_login_tries))
2090 res = ast_app_getdata(chan, errmsg, user, sizeof(user) - 1, 0);
2094 res = ast_safe_sleep(chan, 500);
2096 ast_module_user_remove(u);
2102 * \brief Called by the AgentMonitorOutgoing application (from the dial plan).
2107 * \sa login_exec(), load_module().
2109 static int agentmonitoroutgoing_exec(struct ast_channel *chan, void *data)
2111 int exitifnoagentid = 0;
2113 int changeoutgoing = 0;
2115 char agent[AST_MAX_AGENT];
2118 if (strchr(data, 'd'))
2119 exitifnoagentid = 1;
2120 if (strchr(data, 'n'))
2122 if (strchr(data, 'c'))
2125 if (chan->cid.cid_num) {
2127 char agentvar[AST_MAX_BUF];
2128 snprintf(agentvar, sizeof(agentvar), "%s_%s", GETAGENTBYCALLERID, chan->cid.cid_num);
2129 if ((tmp = pbx_builtin_getvar_helper(NULL, agentvar))) {
2130 struct agent_pvt *p;
2131 ast_copy_string(agent, tmp, sizeof(agent));
2132 AST_LIST_LOCK(&agents);
2133 AST_LIST_TRAVERSE(&agents, p, list) {
2134 if (!strcasecmp(p->agent, tmp)) {
2135 if (changeoutgoing) snprintf(chan->cdr->channel, sizeof(chan->cdr->channel), "Agent/%s", p->agent);
2136 __agent_start_monitoring(chan, p, 1);
2140 AST_LIST_UNLOCK(&agents);
2145 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);
2150 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");
2153 if (exitifnoagentid)
2160 * \brief Dump AgentCallbackLogin agents to the ASTdb database for persistence
2162 static void dump_agents(void)
2164 struct agent_pvt *cur_agent = NULL;
2167 AST_LIST_TRAVERSE(&agents, cur_agent, list) {
2168 if (cur_agent->chan)
2171 if (!ast_strlen_zero(cur_agent->loginchan)) {
2172 snprintf(buf, sizeof(buf), "%s;%s", cur_agent->loginchan, cur_agent->logincallerid);
2173 if (ast_db_put(pa_family, cur_agent->agent, buf))
2174 ast_log(LOG_WARNING, "failed to create persistent entry in ASTdb for %s!\n", buf);
2176 ast_debug(1, "Saved Agent: %s on %s\n", cur_agent->agent, cur_agent->loginchan);
2178 /* Delete - no agent or there is an error */
2179 ast_db_del(pa_family, cur_agent->agent);
2185 * \brief Reload the persistent agents from astdb.
2187 static void reload_agents(void)
2190 struct ast_db_entry *db_tree;
2191 struct ast_db_entry *entry;
2192 struct agent_pvt *cur_agent;
2193 char agent_data[256];
2196 char *agent_callerid;
2198 db_tree = ast_db_gettree(pa_family, NULL);
2200 AST_LIST_LOCK(&agents);
2201 for (entry = db_tree; entry; entry = entry->next) {
2202 agent_num = entry->key + strlen(pa_family) + 2;
2203 AST_LIST_TRAVERSE(&agents, cur_agent, list) {
2204 ast_mutex_lock(&cur_agent->lock);
2205 if (strcmp(agent_num, cur_agent->agent) == 0)
2207 ast_mutex_unlock(&cur_agent->lock);
2210 ast_db_del(pa_family, agent_num);
2213 ast_mutex_unlock(&cur_agent->lock);
2214 if (!ast_db_get(pa_family, agent_num, agent_data, sizeof(agent_data)-1)) {
2215 ast_debug(1, "Reload Agent from AstDB: %s on %s\n", cur_agent->agent, agent_data);
2217 agent_chan = strsep(&parse, ";");
2218 agent_callerid = strsep(&parse, ";");
2219 ast_copy_string(cur_agent->loginchan, agent_chan, sizeof(cur_agent->loginchan));
2220 if (agent_callerid) {
2221 ast_copy_string(cur_agent->logincallerid, agent_callerid, sizeof(cur_agent->logincallerid));
2222 set_agentbycallerid(cur_agent->logincallerid, cur_agent->agent);
2224 cur_agent->logincallerid[0] = '\0';
2225 if (cur_agent->loginstart == 0)
2226 time(&cur_agent->loginstart);
2227 ast_device_state_changed("Agent/%s", cur_agent->agent);
2230 AST_LIST_UNLOCK(&agents);
2232 ast_log(LOG_NOTICE, "Agents successfully reloaded from database.\n");
2233 ast_db_freetree(db_tree);
2237 /*! \brief Part of PBX channel interface */
2238 static int agent_devicestate(void *data)
2240 struct agent_pvt *p;
2242 ast_group_t groupmatch;
2244 int res = AST_DEVICE_INVALID;
2247 if ((s[0] == '@') && (sscanf(s + 1, "%d", &groupoff) == 1))
2248 groupmatch = (1 << groupoff);
2249 else if ((s[0] == ':') && (sscanf(s + 1, "%d", &groupoff) == 1)) {
2250 groupmatch = (1 << groupoff);
2254 /* Check actual logged in agents first */
2255 AST_LIST_LOCK(&agents);
2256 AST_LIST_TRAVERSE(&agents, p, list) {
2257 ast_mutex_lock(&p->lock);
2258 if (!p->pending && ((groupmatch && (p->group & groupmatch)) || !strcmp(data, p->agent))) {
2260 if (res != AST_DEVICE_INUSE)
2261 res = AST_DEVICE_BUSY;
2263 if (res == AST_DEVICE_BUSY)
2264 res = AST_DEVICE_INUSE;
2265 if (p->chan || !ast_strlen_zero(p->loginchan)) {
2266 if (res == AST_DEVICE_INVALID)
2267 res = AST_DEVICE_UNKNOWN;
2268 } else if (res == AST_DEVICE_INVALID)
2269 res = AST_DEVICE_UNAVAILABLE;
2271 if (!strcmp(data, p->agent)) {
2272 ast_mutex_unlock(&p->lock);
2276 ast_mutex_unlock(&p->lock);
2278 AST_LIST_UNLOCK(&agents);
2283 * \note This function expects the agent list to be locked
2285 static struct agent_pvt *find_agent(char *agentid)
2287 struct agent_pvt *cur;
2289 AST_LIST_TRAVERSE(&agents, cur, list) {
2290 if (!strcmp(cur->agent, agentid))
2297 static int function_agent(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
2300 AST_DECLARE_APP_ARGS(args,
2301 AST_APP_ARG(agentid);
2305 struct agent_pvt *agent;
2309 if (ast_strlen_zero(data)) {
2310 ast_log(LOG_WARNING, "The AGENT function requires an argument - agentid!\n");
2314 parse = ast_strdupa(data);
2316 AST_NONSTANDARD_APP_ARGS(args, parse, ':');
2318 args.item = "status";
2320 AST_LIST_LOCK(&agents);
2322 if (!(agent = find_agent(args.agentid))) {
2323 AST_LIST_UNLOCK(&agents);
2324 ast_log(LOG_WARNING, "Agent '%s' not found!\n", args.agentid);
2328 if (!strcasecmp(args.item, "status")) {
2329 char *status = "LOGGEDOUT";
2330 if (agent->chan || !ast_strlen_zero(agent->loginchan))
2331 status = "LOGGEDIN";
2332 ast_copy_string(buf, status, len);
2333 } else if (!strcasecmp(args.item, "password"))
2334 ast_copy_string(buf, agent->password, len);
2335 else if (!strcasecmp(args.item, "name"))
2336 ast_copy_string(buf, agent->name, len);
2337 else if (!strcasecmp(args.item, "mohclass"))
2338 ast_copy_string(buf, agent->moh, len);
2339 else if (!strcasecmp(args.item, "channel")) {
2341 ast_copy_string(buf, agent->chan->name, len);
2342 tmp = strrchr(buf, '-');
2346 } else if (!strcasecmp(args.item, "exten"))
2347 ast_copy_string(buf, agent->loginchan, len);
2349 AST_LIST_UNLOCK(&agents);
2354 struct ast_custom_function agent_function = {
2356 .synopsis = "Gets information about an Agent",
2357 .syntax = "AGENT(<agentid>[:item])",
2358 .read = function_agent,
2359 .desc = "The valid items to retrieve are:\n"
2360 "- status (default) The status of the agent\n"
2361 " LOGGEDIN | LOGGEDOUT\n"
2362 "- password The password of the agent\n"
2363 "- name The name of the agent\n"
2364 "- mohclass MusicOnHold class\n"
2365 "- exten The callback extension for the Agent (AgentCallbackLogin)\n"
2366 "- channel The name of the active channel for the Agent (AgentLogin)\n"
2371 * \brief Initialize the Agents module.
2372 * This function is being called by Asterisk when loading the module.
2373 * Among other things it registers applications, cli commands and reads the cofiguration file.
2375 * \returns int Always 0.
2377 static int load_module(void)
2379 /* Make sure we can register our agent channel type */
2380 if (ast_channel_register(&agent_tech)) {
2381 ast_log(LOG_ERROR, "Unable to register channel class 'Agent'\n");
2382 return AST_MODULE_LOAD_FAILURE;
2384 /* Read in the config */
2385 if (!read_agent_config(0))
2386 return AST_MODULE_LOAD_DECLINE;
2387 if (persistent_agents)
2389 /* Dialplan applications */
2390 ast_register_application(app, login_exec, synopsis, descrip);
2391 ast_register_application(app3, agentmonitoroutgoing_exec, synopsis3, descrip3);
2393 /* Manager commands */
2394 ast_manager_register2("Agents", EVENT_FLAG_AGENT, action_agents, "Lists agents and their status", mandescr_agents);
2395 ast_manager_register2("AgentLogoff", EVENT_FLAG_AGENT, action_agent_logoff, "Sets an agent as no longer logged in", mandescr_agent_logoff);
2398 ast_cli_register_multiple(cli_agents, sizeof(cli_agents) / sizeof(struct ast_cli_entry));
2400 /* Dialplan Functions */
2401 ast_custom_function_register(&agent_function);
2403 return AST_MODULE_LOAD_SUCCESS;
2406 static int reload(void)
2408 if (!read_agent_config(1)) {
2409 if (persistent_agents)
2415 static int unload_module(void)
2417 struct agent_pvt *p;
2418 /* First, take us out of the channel loop */
2419 ast_channel_unregister(&agent_tech);
2420 /* Unregister dialplan functions */
2421 ast_custom_function_unregister(&agent_function);
2422 /* Unregister CLI commands */
2423 ast_cli_unregister_multiple(cli_agents, sizeof(cli_agents) / sizeof(struct ast_cli_entry));
2424 /* Unregister dialplan applications */
2425 ast_unregister_application(app);
2426 ast_unregister_application(app3);
2427 /* Unregister manager command */
2428 ast_manager_unregister("Agents");
2429 ast_manager_unregister("AgentLogoff");
2430 /* Unregister channel */
2431 AST_LIST_LOCK(&agents);
2432 /* Hangup all interfaces if they have an owner */
2433 while ((p = AST_LIST_REMOVE_HEAD(&agents, list))) {
2435 ast_softhangup(p->owner, AST_SOFTHANGUP_APPUNLOAD);
2438 AST_LIST_UNLOCK(&agents);
2442 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Agent Proxy Channel",
2443 .load = load_module,
2444 .unload = unload_module,