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/acl.h"
56 #include "asterisk/callerid.h"
57 #include "asterisk/file.h"
58 #include "asterisk/cli.h"
59 #include "asterisk/app.h"
60 #include "asterisk/musiconhold.h"
61 #include "asterisk/manager.h"
62 #include "asterisk/features.h"
63 #include "asterisk/utils.h"
64 #include "asterisk/causes.h"
65 #include "asterisk/astdb.h"
66 #include "asterisk/devicestate.h"
67 #include "asterisk/monitor.h"
68 #include "asterisk/stringfields.h"
69 #include "asterisk/event.h"
72 <application name="AgentLogin" language="en_US">
77 <parameter name="AgentNo" />
78 <parameter name="options">
81 <para>silent login - do not announce the login ok segment after
82 agent logged on/off</para>
88 <para>Asks the agent to login to the system. Always returns <literal>-1</literal>.
89 While logged in, the agent can receive calls and will hear a <literal>beep</literal>
90 when a new call comes in. The agent can dump the call by pressing the star key.</para>
93 <ref type="application">Queue</ref>
94 <ref type="application">AddQueueMember</ref>
95 <ref type="application">RemoveQueueMember</ref>
96 <ref type="application">PauseQueueMember</ref>
97 <ref type="application">UnpauseQueueMember</ref>
98 <ref type="function">AGENT</ref>
99 <ref type="filename">agents.conf</ref>
100 <ref type="filename">queues.conf</ref>
103 <application name="AgentMonitorOutgoing" language="en_US">
105 Record agent's outgoing call.
108 <parameter name="options">
111 <para>make the app return <literal>-1</literal> if there is an error condition.</para>
114 <para>change the CDR so that the source of the call is
115 <literal>Agent/agent_id</literal></para>
118 <para>don't generate the warnings when there is no callerid or the
119 agentid is not known. It's handy if you want to have one context
120 for agent and non-agent calls.</para>
126 <para>Tries to figure out the id of the agent who is placing outgoing call based on
127 comparison of the callerid of the current interface and the global variable
128 placed by the AgentCallbackLogin application. That's why it should be used only
129 with the AgentCallbackLogin app. Uses the monitoring functions in chan_agent
130 instead of Monitor application. That has to be configured in the
131 <filename>agents.conf</filename> file.</para>
132 <para>Normally the app returns <literal>0</literal> unless the options are passed.</para>
135 <ref type="filename">agents.conf</ref>
138 <function name="AGENT" language="en_US">
140 Gets information about an Agent
143 <parameter name="agentid" required="true" />
144 <parameter name="item">
145 <para>The valid items to retrieve are:</para>
148 <para>(default) The status of the agent (LOGGEDIN | LOGGEDOUT)</para>
150 <enum name="password">
151 <para>The password of the agent</para>
154 <para>The name of the agent</para>
156 <enum name="mohclass">
157 <para>MusicOnHold class</para>
159 <enum name="channel">
160 <para>The name of the active channel for the Agent (AgentLogin)</para>
167 <manager name="Agents" language="en_US">
169 Lists agents and their status.
172 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
175 <para>Will list info about all possible agents.</para>
178 <manager name="AgentLogoff" language="en_US">
180 Sets an agent as no longer logged in.
183 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
184 <parameter name="Agent" required="true">
185 <para>Agent ID of the agent to log off.</para>
187 <parameter name="Soft">
188 <para>Set to <literal>true</literal> to not hangup existing calls.</para>
192 <para>Sets an agent as no longer logged in.</para>
197 static const char tdesc[] = "Call Agent Proxy Channel";
198 static const char config[] = "agents.conf";
200 static const char app[] = "AgentLogin";
201 static const char app3[] = "AgentMonitorOutgoing";
203 static char moh[80] = "default";
205 #define AST_MAX_AGENT 80 /*!< Agent ID or Password max length */
206 #define AST_MAX_BUF 256
207 #define AST_MAX_FILENAME_LEN 256
209 static const char pa_family[] = "Agents"; /*!< Persistent Agents astdb family */
210 #define PA_MAX_LEN 2048 /*!< The maximum length of each persistent member agent database entry */
212 #define DEFAULT_ACCEPTDTMF '#'
213 #define DEFAULT_ENDDTMF '*'
215 static ast_group_t group;
216 static int autologoff;
217 static int wrapuptime;
220 static int multiplelogin = 1;
221 static int autologoffunavail = 0;
222 static char acceptdtmf = DEFAULT_ACCEPTDTMF;
223 static char enddtmf = DEFAULT_ENDDTMF;
225 static int maxlogintries = 3;
226 static char agentgoodbye[AST_MAX_FILENAME_LEN] = "vm-goodbye";
228 static int recordagentcalls = 0;
229 static char recordformat[AST_MAX_BUF] = "";
230 static char recordformatext[AST_MAX_BUF] = "";
231 static char urlprefix[AST_MAX_BUF] = "";
232 static char savecallsin[AST_MAX_BUF] = "";
233 static int updatecdr = 0;
234 static char beep[AST_MAX_BUF] = "beep";
236 #define GETAGENTBYCALLERID "AGENTBYCALLERID"
239 AGENT_FLAG_ACKCALL = (1 << 0),
240 AGENT_FLAG_AUTOLOGOFF = (1 << 1),
241 AGENT_FLAG_WRAPUPTIME = (1 << 2),
242 AGENT_FLAG_ACCEPTDTMF = (1 << 3),
243 AGENT_FLAG_ENDDTMF = (1 << 4),
246 /*! \brief Structure representing an agent. */
248 ast_mutex_t lock; /*!< Channel private lock */
249 int dead; /*!< Poised for destruction? */
250 int pending; /*!< Not a real agent -- just pending a match */
251 int abouttograb; /*!< About to grab */
252 int autologoff; /*!< Auto timeout time */
253 int ackcall; /*!< ackcall */
254 int deferlogoff; /*!< Defer logoff to hangup */
257 time_t loginstart; /*!< When agent first logged in (0 when logged off) */
258 time_t start; /*!< When call started */
259 struct timeval lastdisc; /*!< When last disconnected */
260 int wrapuptime; /*!< Wrapup time in ms */
261 ast_group_t group; /*!< Group memberships */
262 int acknowledged; /*!< Acknowledged */
263 char moh[80]; /*!< Which music on hold */
264 char agent[AST_MAX_AGENT]; /*!< Agent ID */
265 char password[AST_MAX_AGENT]; /*!< Password for Agent login */
266 char name[AST_MAX_AGENT];
267 ast_mutex_t app_lock; /**< Synchronization between owning applications */
269 ast_cond_t app_complete_cond;
270 volatile int app_sleep_cond; /**< Sleep condition for the login app */
271 struct ast_channel *owner; /**< Agent */
272 char logincallerid[80]; /**< Caller ID they had when they logged in */
273 struct ast_channel *chan; /**< Channel we use */
274 unsigned int flags; /**< Flags show if settings were applied with channel vars */
275 AST_LIST_ENTRY(agent_pvt) list; /**< Next Agent in the linked list. */
278 static AST_LIST_HEAD_STATIC(agents, agent_pvt); /*!< Holds the list of agents (loaded form agents.conf). */
280 #define CHECK_FORMATS(ast, p) do { \
282 if (ast->nativeformats != p->chan->nativeformats) { \
283 char tmp1[256], tmp2[256]; \
284 ast_debug(1, "Native formats changing from '%s' to '%s'\n", ast_getformatname_multiple(tmp1, sizeof(tmp1), ast->nativeformats), ast_getformatname_multiple(tmp2, sizeof(tmp2), p->chan->nativeformats)); \
285 /* Native formats changed, reset things */ \
286 ast->nativeformats = p->chan->nativeformats; \
287 ast_debug(1, "Resetting read to '%s' and write to '%s'\n", ast_getformatname_multiple(tmp1, sizeof(tmp1), ast->readformat), ast_getformatname_multiple(tmp2, sizeof(tmp2), ast->writeformat));\
288 ast_set_read_format(ast, ast->readformat); \
289 ast_set_write_format(ast, ast->writeformat); \
291 if (p->chan->readformat != ast->rawreadformat && !p->chan->generator) \
292 ast_set_read_format(p->chan, ast->rawreadformat); \
293 if (p->chan->writeformat != ast->rawwriteformat && !p->chan->generator) \
294 ast_set_write_format(p->chan, ast->rawwriteformat); \
298 /*! \brief Cleanup moves all the relevant FD's from the 2nd to the first, but retains things
299 properly for a timingfd XXX This might need more work if agents were logged in as agents or other
300 totally impractical combinations XXX */
302 #define CLEANUP(ast, p) do { \
305 for (x=0;x<AST_MAX_FDS;x++) {\
306 if (x != AST_TIMING_FD) \
307 ast_channel_set_fd(ast, x, p->chan->fds[x]); \
309 ast_channel_set_fd(ast, AST_AGENT_FD, p->chan->fds[AST_TIMING_FD]); \
313 /*--- Forward declarations */
314 static struct ast_channel *agent_request(const char *type, format_t format, const struct ast_channel *requestor, void *data, int *cause);
315 static int agent_devicestate(void *data);
316 static int agent_digit_begin(struct ast_channel *ast, char digit);
317 static int agent_digit_end(struct ast_channel *ast, char digit, unsigned int duration);
318 static int agent_call(struct ast_channel *ast, char *dest, int timeout);
319 static int agent_hangup(struct ast_channel *ast);
320 static int agent_answer(struct ast_channel *ast);
321 static struct ast_frame *agent_read(struct ast_channel *ast);
322 static int agent_write(struct ast_channel *ast, struct ast_frame *f);
323 static int agent_sendhtml(struct ast_channel *ast, int subclass, const char *data, int datalen);
324 static int agent_sendtext(struct ast_channel *ast, const char *text);
325 static int agent_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen);
326 static int agent_fixup(struct ast_channel *oldchan, struct ast_channel *newchan);
327 static struct ast_channel *agent_bridgedchannel(struct ast_channel *chan, struct ast_channel *bridge);
328 static char *complete_agent_logoff_cmd(const char *line, const char *word, int pos, int state);
329 static struct ast_channel* agent_get_base_channel(struct ast_channel *chan);
330 static int agent_set_base_channel(struct ast_channel *chan, struct ast_channel *base);
331 static int agent_logoff(const char *agent, int soft);
333 /*! \brief Channel interface description for PBX integration */
334 static const struct ast_channel_tech agent_tech = {
336 .description = tdesc,
338 .requester = agent_request,
339 .devicestate = agent_devicestate,
340 .send_digit_begin = agent_digit_begin,
341 .send_digit_end = agent_digit_end,
343 .hangup = agent_hangup,
344 .answer = agent_answer,
346 .write = agent_write,
347 .write_video = agent_write,
348 .send_html = agent_sendhtml,
349 .send_text = agent_sendtext,
350 .exception = agent_read,
351 .indicate = agent_indicate,
352 .fixup = agent_fixup,
353 .bridged_channel = agent_bridgedchannel,
354 .get_base_channel = agent_get_base_channel,
355 .set_base_channel = agent_set_base_channel,
359 * Adds an agent to the global list of agents.
361 * \param agent A string with the username, password and real name of an agent. As defined in agents.conf. Example: "13,169,John Smith"
362 * \param pending If it is pending or not.
363 * @return The just created agent.
364 * \sa agent_pvt, agents.
366 static struct agent_pvt *add_agent(const char *agent, int pending)
369 AST_DECLARE_APP_ARGS(args,
371 AST_APP_ARG(password);
374 char *password = NULL;
379 parse = ast_strdupa(agent);
381 /* Extract username (agt), password and name from agent (args). */
382 AST_STANDARD_APP_ARGS(args, parse);
385 ast_log(LOG_WARNING, "A blank agent line!\n");
389 if(ast_strlen_zero(args.agt) ) {
390 ast_log(LOG_WARNING, "An agent line with no agentid!\n");
395 if(!ast_strlen_zero(args.password)) {
396 password = args.password;
397 while (*password && *password < 33) password++;
399 if(!ast_strlen_zero(args.name)) {
401 while (*name && *name < 33) name++;
404 /* Are we searching for the agent here ? To see if it exists already ? */
405 AST_LIST_TRAVERSE(&agents, p, list) {
406 if (!pending && !strcmp(p->agent, agt))
411 if (!(p = ast_calloc(1, sizeof(*p))))
413 ast_copy_string(p->agent, agt, sizeof(p->agent));
414 ast_mutex_init(&p->lock);
415 ast_mutex_init(&p->app_lock);
416 ast_cond_init(&p->app_complete_cond, NULL);
417 p->app_lock_flag = 0;
418 p->app_sleep_cond = 1;
420 p->pending = pending;
421 AST_LIST_INSERT_TAIL(&agents, p, list);
424 ast_copy_string(p->password, password ? password : "", sizeof(p->password));
425 ast_copy_string(p->name, name ? name : "", sizeof(p->name));
426 ast_copy_string(p->moh, moh, sizeof(p->moh));
427 if (!ast_test_flag(p, AGENT_FLAG_ACKCALL)) {
428 p->ackcall = ackcall;
430 if (!ast_test_flag(p, AGENT_FLAG_AUTOLOGOFF)) {
431 p->autologoff = autologoff;
433 if (!ast_test_flag(p, AGENT_FLAG_ACCEPTDTMF)) {
434 p->acceptdtmf = acceptdtmf;
436 if (!ast_test_flag(p, AGENT_FLAG_ENDDTMF)) {
437 p->enddtmf = enddtmf;
440 /* If someone reduces the wrapuptime and reloads, we want it
441 * to change the wrapuptime immediately on all calls */
442 if (!ast_test_flag(p, AGENT_FLAG_WRAPUPTIME) && p->wrapuptime > wrapuptime) {
443 struct timeval now = ast_tvnow();
444 /* XXX check what is this exactly */
446 /* We won't be pedantic and check the tv_usec val */
447 if (p->lastdisc.tv_sec > (now.tv_sec + wrapuptime/1000)) {
448 p->lastdisc.tv_sec = now.tv_sec + wrapuptime/1000;
449 p->lastdisc.tv_usec = now.tv_usec;
452 p->wrapuptime = wrapuptime;
462 * Deletes an agent after doing some clean up.
463 * Further documentation: How safe is this function ? What state should the agent be to be cleaned.
464 * \param p Agent to be deleted.
467 static int agent_cleanup(struct agent_pvt *p)
469 struct ast_channel *chan = p->owner;
471 chan->tech_pvt = NULL;
472 p->app_sleep_cond = 1;
473 /* Release ownership of the agent to other threads (presumably running the login app). */
474 p->app_lock_flag = 0;
475 ast_cond_signal(&p->app_complete_cond);
477 chan = ast_channel_release(chan);
480 ast_mutex_destroy(&p->lock);
481 ast_mutex_destroy(&p->app_lock);
482 ast_cond_destroy(&p->app_complete_cond);
488 static int check_availability(struct agent_pvt *newlyavailable, int needlock);
490 static int agent_answer(struct ast_channel *ast)
492 ast_log(LOG_WARNING, "Huh? Agent is being asked to answer?\n");
496 static int __agent_start_monitoring(struct ast_channel *ast, struct agent_pvt *p, int needlock)
498 char tmp[AST_MAX_BUF],tmp2[AST_MAX_BUF], *pointer;
499 char filename[AST_MAX_BUF];
504 snprintf(filename, sizeof(filename), "agent-%s-%s",p->agent, ast->uniqueid);
505 /* substitute . for - */
506 if ((pointer = strchr(filename, '.')))
508 snprintf(tmp, sizeof(tmp), "%s%s", savecallsin, filename);
509 ast_monitor_start(ast, recordformat, tmp, needlock, X_REC_IN | X_REC_OUT);
510 ast_monitor_setjoinfiles(ast, 1);
511 snprintf(tmp2, sizeof(tmp2), "%s%s.%s", urlprefix, filename, recordformatext);
513 ast_verbose("name is %s, link is %s\n",tmp, tmp2);
516 ast->cdr = ast_cdr_alloc();
517 ast_cdr_setuserfield(ast, tmp2);
520 ast_log(LOG_ERROR, "Recording already started on that call.\n");
524 static int agent_start_monitoring(struct ast_channel *ast, int needlock)
526 return __agent_start_monitoring(ast, ast->tech_pvt, needlock);
529 static struct ast_frame *agent_read(struct ast_channel *ast)
531 struct agent_pvt *p = ast->tech_pvt;
532 struct ast_frame *f = NULL;
533 static struct ast_frame answer_frame = { AST_FRAME_CONTROL, { AST_CONTROL_ANSWER } };
534 int cur_time = time(NULL);
535 ast_mutex_lock(&p->lock);
536 CHECK_FORMATS(ast, p);
541 ast_copy_flags(p->chan, ast, AST_FLAG_EXCEPTION);
542 p->chan->fdno = (ast->fdno == AST_AGENT_FD) ? AST_TIMING_FD : ast->fdno;
543 f = ast_read(p->chan);
547 /* If there's a channel, make it NULL */
549 p->chan->_bridge = NULL;
551 ast_devstate_changed(AST_DEVICE_UNAVAILABLE, "Agent/%s", p->agent);
555 /* if acknowledgement is not required, and the channel is up, we may have missed
556 an AST_CONTROL_ANSWER (if there was one), so mark the call acknowledged anyway */
557 if (!p->ackcall && !p->acknowledged && p->chan && (p->chan->_state == AST_STATE_UP)) {
561 if (!p->acknowledged) {
562 int howlong = cur_time - p->start;
563 if (p->autologoff && (howlong >= p->autologoff)) {
564 ast_log(LOG_NOTICE, "Agent '%s' didn't answer/confirm within %d seconds (waited %d)\n", p->name, p->autologoff, howlong);
565 if (p->owner || p->chan) {
566 while (p->owner && ast_channel_trylock(p->owner)) {
567 DEADLOCK_AVOIDANCE(&p->lock);
570 ast_softhangup(p->owner, AST_SOFTHANGUP_EXPLICIT);
571 ast_channel_unlock(p->owner);
574 while (p->chan && ast_channel_trylock(p->chan)) {
575 DEADLOCK_AVOIDANCE(&p->lock);
578 ast_softhangup(p->chan, AST_SOFTHANGUP_EXPLICIT);
579 ast_channel_unlock(p->chan);
584 switch (f->frametype) {
585 case AST_FRAME_CONTROL:
586 if (f->subclass.integer == AST_CONTROL_ANSWER) {
588 ast_verb(3, "%s answered, waiting for '%c' to acknowledge\n", p->chan->name, p->acceptdtmf);
589 /* Don't pass answer along */
594 /* Use the builtin answer frame for the
595 recording start check below. */
601 case AST_FRAME_DTMF_BEGIN:
602 /*ignore DTMF begin's as it can cause issues with queue announce files*/
603 if((!p->acknowledged && f->subclass.integer == p->acceptdtmf) || (f->subclass.integer == p->enddtmf && endcall)){
608 case AST_FRAME_DTMF_END:
609 if (!p->acknowledged && (f->subclass.integer == p->acceptdtmf)) {
610 ast_verb(3, "%s acknowledged\n", p->chan->name);
614 } else if (f->subclass.integer == p->enddtmf && endcall) {
615 /* terminates call */
620 case AST_FRAME_VOICE:
621 case AST_FRAME_VIDEO:
622 /* don't pass voice or video until the call is acknowledged */
623 if (!p->acknowledged) {
628 /* pass everything else on through */
634 if (p->chan && !p->chan->_bridge) {
635 if (strcasecmp(p->chan->tech->type, "Local")) {
636 p->chan->_bridge = ast;
638 ast_debug(1, "Bridge on '%s' being set to '%s' (3)\n", p->chan->name, p->chan->_bridge->name);
641 ast_mutex_unlock(&p->lock);
642 if (recordagentcalls && f == &answer_frame)
643 agent_start_monitoring(ast,0);
647 static int agent_sendhtml(struct ast_channel *ast, int subclass, const char *data, int datalen)
649 struct agent_pvt *p = ast->tech_pvt;
651 ast_mutex_lock(&p->lock);
653 res = ast_channel_sendhtml(p->chan, subclass, data, datalen);
654 ast_mutex_unlock(&p->lock);
658 static int agent_sendtext(struct ast_channel *ast, const char *text)
660 struct agent_pvt *p = ast->tech_pvt;
662 ast_mutex_lock(&p->lock);
664 res = ast_sendtext(p->chan, text);
665 ast_mutex_unlock(&p->lock);
669 static int agent_write(struct ast_channel *ast, struct ast_frame *f)
671 struct agent_pvt *p = ast->tech_pvt;
673 CHECK_FORMATS(ast, p);
674 ast_mutex_lock(&p->lock);
678 if ((f->frametype != AST_FRAME_VOICE) ||
679 (f->frametype != AST_FRAME_VIDEO) ||
680 (f->subclass.codec == p->chan->writeformat)) {
681 res = ast_write(p->chan, f);
683 ast_debug(1, "Dropping one incompatible %s frame on '%s' to '%s'\n",
684 f->frametype == AST_FRAME_VOICE ? "audio" : "video",
685 ast->name, p->chan->name);
690 ast_mutex_unlock(&p->lock);
694 static int agent_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
696 struct agent_pvt *p = newchan->tech_pvt;
697 ast_mutex_lock(&p->lock);
698 if (p->owner != oldchan) {
699 ast_log(LOG_WARNING, "old channel wasn't %p but was %p\n", oldchan, p->owner);
700 ast_mutex_unlock(&p->lock);
704 ast_mutex_unlock(&p->lock);
708 static int agent_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen)
710 struct agent_pvt *p = ast->tech_pvt;
712 ast_mutex_lock(&p->lock);
713 if (p->chan && !ast_check_hangup(p->chan)) {
714 while (ast_channel_trylock(p->chan)) {
715 ast_channel_unlock(ast);
717 ast_channel_lock(ast);
719 res = p->chan->tech->indicate ? p->chan->tech->indicate(p->chan, condition, data, datalen) : -1;
720 ast_channel_unlock(p->chan);
723 ast_mutex_unlock(&p->lock);
727 static int agent_digit_begin(struct ast_channel *ast, char digit)
729 struct agent_pvt *p = ast->tech_pvt;
730 ast_mutex_lock(&p->lock);
732 ast_senddigit_begin(p->chan, digit);
734 ast_mutex_unlock(&p->lock);
738 static int agent_digit_end(struct ast_channel *ast, char digit, unsigned int duration)
740 struct agent_pvt *p = ast->tech_pvt;
741 ast_mutex_lock(&p->lock);
743 ast_senddigit_end(p->chan, digit, duration);
745 ast_mutex_unlock(&p->lock);
749 static int agent_call(struct ast_channel *ast, char *dest, int timeout)
751 struct agent_pvt *p = ast->tech_pvt;
754 ast_mutex_lock(&p->lock);
758 ast_debug(1, "Pretending to dial on pending agent\n");
759 newstate = AST_STATE_DIALING;
762 ast_log(LOG_NOTICE, "Whoa, they hung up between alloc and call... what are the odds of that?\n");
765 ast_mutex_unlock(&p->lock);
767 ast_setstate(ast, newstate);
770 ast_verb(3, "agent_call, call to agent '%s' call on '%s'\n", p->agent, p->chan->name);
771 ast_debug(3, "Playing beep, lang '%s'\n", p->chan->language);
772 res = ast_streamfile(p->chan, beep, p->chan->language);
773 ast_debug(3, "Played beep, result '%d'\n", res);
775 res = ast_waitstream(p->chan, "");
776 ast_debug(3, "Waited for stream, result '%d'\n", res);
779 res = ast_set_read_format(p->chan, ast_best_codec(p->chan->nativeformats));
780 ast_debug(3, "Set read format, result '%d'\n", res);
782 ast_log(LOG_WARNING, "Unable to set read format to %s\n", ast_getformatname(ast_best_codec(p->chan->nativeformats)));
786 ast_devstate_changed(AST_DEVICE_UNAVAILABLE, "Agent/%s", p->agent);
790 res = ast_set_write_format(p->chan, ast_best_codec(p->chan->nativeformats));
791 ast_debug(3, "Set write format, result '%d'\n", res);
793 ast_log(LOG_WARNING, "Unable to set write format to %s\n", ast_getformatname(ast_best_codec(p->chan->nativeformats)));
796 /* Call is immediately up, or might need ack */
798 newstate = AST_STATE_RINGING;
800 newstate = AST_STATE_UP;
801 if (recordagentcalls)
802 agent_start_monitoring(ast, 0);
808 ast_mutex_unlock(&p->lock);
810 ast_setstate(ast, newstate);
814 /*! \brief return the channel or base channel if one exists. This function assumes the channel it is called on is already locked */
815 struct ast_channel* agent_get_base_channel(struct ast_channel *chan)
817 struct agent_pvt *p = NULL;
818 struct ast_channel *base = chan;
820 /* chan is locked by the calling function */
821 if (!chan || !chan->tech_pvt) {
822 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);
831 int agent_set_base_channel(struct ast_channel *chan, struct ast_channel *base)
833 struct agent_pvt *p = NULL;
835 if (!chan || !base) {
836 ast_log(LOG_ERROR, "whoa, you need a channel (0x%ld) and a base channel (0x%ld) for setting.\n", (long)chan, (long)base);
841 ast_log(LOG_ERROR, "whoa, channel %s is missing his tech_pvt structure!!.\n", chan->name);
848 static int agent_hangup(struct ast_channel *ast)
850 struct agent_pvt *p = ast->tech_pvt;
853 ast_mutex_lock(&p->lock);
855 ast->tech_pvt = NULL;
856 p->app_sleep_cond = 1;
859 /* if they really are hung up then set start to 0 so the test
860 * later if we're called on an already downed channel
861 * doesn't cause an agent to be logged out like when
862 * agent_request() is followed immediately by agent_hangup()
863 * as in apps/app_chanisavail.c:chanavail_exec()
866 ast_debug(1, "Hangup called for state %s\n", ast_state2str(ast->_state));
867 if (p->start && (ast->_state != AST_STATE_UP)) {
868 howlong = time(NULL) - p->start;
870 } else if (ast->_state == AST_STATE_RESERVED)
875 p->chan->_bridge = NULL;
876 /* If they're dead, go ahead and hang up on the agent now */
878 ast_channel_lock(p->chan);
879 ast_softhangup(p->chan, AST_SOFTHANGUP_EXPLICIT);
880 ast_channel_unlock(p->chan);
881 } else if (p->loginstart) {
882 ast_channel_lock(p->chan);
883 ast_indicate_data(p->chan, AST_CONTROL_HOLD,
885 !ast_strlen_zero(p->moh) ? strlen(p->moh) + 1 : 0);
886 ast_channel_unlock(p->chan);
889 ast_mutex_unlock(&p->lock);
891 /* Only register a device state change if the agent is still logged in */
892 if (!p->loginstart) {
893 p->logincallerid[0] = '\0';
895 ast_devstate_changed(AST_DEVICE_NOT_INUSE, "Agent/%s", p->agent);
899 AST_LIST_LOCK(&agents);
900 AST_LIST_REMOVE(&agents, p, list);
901 AST_LIST_UNLOCK(&agents);
903 if (p->abouttograb) {
904 /* Let the "about to grab" thread know this isn't valid anymore, and let it
907 } else if (p->dead) {
908 ast_mutex_destroy(&p->lock);
909 ast_mutex_destroy(&p->app_lock);
910 ast_cond_destroy(&p->app_complete_cond);
914 /* Not dead -- check availability now */
915 ast_mutex_lock(&p->lock);
916 /* Store last disconnect time */
917 p->lastdisc = ast_tvadd(ast_tvnow(), ast_samp2tv(p->wrapuptime, 1000));
918 ast_mutex_unlock(&p->lock);
920 /* Release ownership of the agent to other threads (presumably running the login app). */
921 p->app_lock_flag = 0;
922 ast_cond_signal(&p->app_complete_cond);
927 static int agent_cont_sleep( void *data )
932 p = (struct agent_pvt *)data;
934 ast_mutex_lock(&p->lock);
935 res = p->app_sleep_cond;
936 if (p->lastdisc.tv_sec) {
937 if (ast_tvdiff_ms(ast_tvnow(), p->lastdisc) > 0)
940 ast_mutex_unlock(&p->lock);
943 ast_debug(5, "agent_cont_sleep() returning %d\n", res );
948 static int agent_ack_sleep(void *data)
955 /* Wait a second and look for something */
957 p = (struct agent_pvt *) data;
962 to = ast_waitfor(p->chan, to);
967 f = ast_read(p->chan);
970 if (f->frametype == AST_FRAME_DTMF)
971 res = f->subclass.integer;
975 ast_mutex_lock(&p->lock);
976 if (!p->app_sleep_cond) {
977 ast_mutex_unlock(&p->lock);
979 } else if (res == p->acceptdtmf) {
980 ast_mutex_unlock(&p->lock);
983 ast_mutex_unlock(&p->lock);
989 static struct ast_channel *agent_bridgedchannel(struct ast_channel *chan, struct ast_channel *bridge)
991 struct agent_pvt *p = bridge->tech_pvt;
992 struct ast_channel *ret = NULL;
996 ret = bridge->_bridge;
997 else if (chan == bridge->_bridge)
1001 ast_debug(1, "Asked for bridged channel on '%s'/'%s', returning '%s'\n", chan->name, bridge->name, ret ? ret->name : "<none>");
1005 /*! \brief Create new agent channel */
1006 static struct ast_channel *agent_new(struct agent_pvt *p, int state, const char *linkedid)
1008 struct ast_channel *tmp;
1012 ast_log(LOG_WARNING, "No channel? :(\n");
1017 tmp = ast_channel_alloc(0, state, 0, 0, "", p->chan ? p->chan->exten:"", p->chan ? p->chan->context:"", linkedid, 0, "Agent/P%s-%d", p->agent, (int) ast_random() & 0xffff);
1019 tmp = ast_channel_alloc(0, state, 0, 0, "", p->chan ? p->chan->exten:"", p->chan ? p->chan->context:"", linkedid, 0, "Agent/%s", p->agent);
1021 ast_log(LOG_WARNING, "Unable to allocate agent channel structure\n");
1025 tmp->tech = &agent_tech;
1027 tmp->nativeformats = p->chan->nativeformats;
1028 tmp->writeformat = p->chan->writeformat;
1029 tmp->rawwriteformat = p->chan->writeformat;
1030 tmp->readformat = p->chan->readformat;
1031 tmp->rawreadformat = p->chan->readformat;
1032 ast_string_field_set(tmp, language, p->chan->language);
1033 ast_copy_string(tmp->context, p->chan->context, sizeof(tmp->context));
1034 ast_copy_string(tmp->exten, p->chan->exten, sizeof(tmp->exten));
1035 /* XXX Is this really all we copy form the originating channel?? */
1037 tmp->nativeformats = AST_FORMAT_SLINEAR;
1038 tmp->writeformat = AST_FORMAT_SLINEAR;
1039 tmp->rawwriteformat = AST_FORMAT_SLINEAR;
1040 tmp->readformat = AST_FORMAT_SLINEAR;
1041 tmp->rawreadformat = AST_FORMAT_SLINEAR;
1043 /* Safe, agentlock already held */
1047 /* Wake up and wait for other applications (by definition the login app)
1048 * to release this channel). Takes ownership of the agent channel
1049 * to this thread only.
1050 * For signalling the other thread, ast_queue_frame is used until we
1051 * can safely use signals for this purpose. The pselect() needs to be
1052 * implemented in the kernel for this.
1054 p->app_sleep_cond = 0;
1056 alreadylocked = p->app_lock_flag;
1057 p->app_lock_flag = 1;
1059 if (alreadylocked) {
1061 ast_queue_frame(p->chan, &ast_null_frame);
1062 ast_mutex_unlock(&p->lock); /* For other thread to read the condition. */
1063 p->app_lock_flag = 1;
1064 ast_mutex_lock(&p->lock);
1066 ast_log(LOG_WARNING, "Agent disconnected while we were connecting the call\n");
1068 tmp->tech_pvt = NULL;
1069 p->app_sleep_cond = 1;
1070 tmp = ast_channel_release(tmp);
1071 ast_mutex_unlock(&p->lock); /* For other thread to read the condition. */
1072 p->app_lock_flag = 0;
1073 ast_cond_signal(&p->app_complete_cond);
1078 ast_indicate(p->chan, AST_CONTROL_UNHOLD);
1084 * Read configuration data. The file named agents.conf.
1086 * \returns Always 0, or so it seems.
1088 static int read_agent_config(int reload)
1090 struct ast_config *cfg;
1091 struct ast_config *ucfg;
1092 struct ast_variable *v;
1093 struct agent_pvt *p;
1094 const char *catname;
1095 const char *hasagent;
1097 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
1104 cfg = ast_config_load(config, config_flags);
1106 ast_log(LOG_NOTICE, "No agent configuration found -- agent support disabled\n");
1108 } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
1110 } else if (cfg == CONFIG_STATUS_FILEINVALID) {
1111 ast_log(LOG_ERROR, "%s contains a parsing error. Aborting\n", config);
1114 if ((ucfg = ast_config_load("users.conf", config_flags))) {
1115 if (ucfg == CONFIG_STATUS_FILEUNCHANGED) {
1117 } else if (ucfg == CONFIG_STATUS_FILEINVALID) {
1118 ast_log(LOG_ERROR, "users.conf contains a parsing error. Aborting\n");
1123 AST_LIST_LOCK(&agents);
1124 AST_LIST_TRAVERSE(&agents, p, list) {
1127 strcpy(moh, "default");
1128 /* set the default recording values */
1129 recordagentcalls = 0;
1130 strcpy(recordformat, "wav");
1131 strcpy(recordformatext, "wav");
1132 urlprefix[0] = '\0';
1133 savecallsin[0] = '\0';
1135 /* Read in [general] section for persistence */
1136 multiplelogin = ast_true(ast_variable_retrieve(cfg, "general", "multiplelogin"));
1138 /* Read in the [agents] section */
1139 v = ast_variable_browse(cfg, "agents");
1141 /* Create the interface list */
1142 if (!strcasecmp(v->name, "agent")) {
1143 add_agent(v->value, 0);
1144 } else if (!strcasecmp(v->name, "group")) {
1145 group = ast_get_group(v->value);
1146 } else if (!strcasecmp(v->name, "autologoff")) {
1147 autologoff = atoi(v->value);
1150 } else if (!strcasecmp(v->name, "ackcall")) {
1151 if (ast_true(v->value) || !strcasecmp(v->value, "always")) {
1154 } else if (!strcasecmp(v->name, "endcall")) {
1155 endcall = ast_true(v->value);
1156 } else if (!strcasecmp(v->name, "acceptdtmf")) {
1157 acceptdtmf = *(v->value);
1158 ast_log(LOG_NOTICE, "Set acceptdtmf to %c\n", acceptdtmf);
1159 } else if (!strcasecmp(v->name, "enddtmf")) {
1160 enddtmf = *(v->value);
1161 } else if (!strcasecmp(v->name, "wrapuptime")) {
1162 wrapuptime = atoi(v->value);
1165 } else if (!strcasecmp(v->name, "maxlogintries") && !ast_strlen_zero(v->value)) {
1166 maxlogintries = atoi(v->value);
1167 if (maxlogintries < 0)
1169 } else if (!strcasecmp(v->name, "goodbye") && !ast_strlen_zero(v->value)) {
1170 strcpy(agentgoodbye,v->value);
1171 } else if (!strcasecmp(v->name, "musiconhold")) {
1172 ast_copy_string(moh, v->value, sizeof(moh));
1173 } else if (!strcasecmp(v->name, "updatecdr")) {
1174 if (ast_true(v->value))
1178 } else if (!strcasecmp(v->name, "autologoffunavail")) {
1179 if (ast_true(v->value))
1180 autologoffunavail = 1;
1182 autologoffunavail = 0;
1183 } else if (!strcasecmp(v->name, "recordagentcalls")) {
1184 recordagentcalls = ast_true(v->value);
1185 } else if (!strcasecmp(v->name, "recordformat")) {
1186 ast_copy_string(recordformat, v->value, sizeof(recordformat));
1187 if (!strcasecmp(v->value, "wav49"))
1188 strcpy(recordformatext, "WAV");
1190 ast_copy_string(recordformatext, v->value, sizeof(recordformatext));
1191 } else if (!strcasecmp(v->name, "urlprefix")) {
1192 ast_copy_string(urlprefix, v->value, sizeof(urlprefix));
1193 if (urlprefix[strlen(urlprefix) - 1] != '/')
1194 strncat(urlprefix, "/", sizeof(urlprefix) - strlen(urlprefix) - 1);
1195 } else if (!strcasecmp(v->name, "savecallsin")) {
1196 if (v->value[0] == '/')
1197 ast_copy_string(savecallsin, v->value, sizeof(savecallsin));
1199 snprintf(savecallsin, sizeof(savecallsin) - 2, "/%s", v->value);
1200 if (savecallsin[strlen(savecallsin) - 1] != '/')
1201 strncat(savecallsin, "/", sizeof(savecallsin) - strlen(savecallsin) - 1);
1202 } else if (!strcasecmp(v->name, "custom_beep")) {
1203 ast_copy_string(beep, v->value, sizeof(beep));
1208 genhasagent = ast_true(ast_variable_retrieve(ucfg, "general", "hasagent"));
1209 catname = ast_category_browse(ucfg, NULL);
1211 if (strcasecmp(catname, "general")) {
1212 hasagent = ast_variable_retrieve(ucfg, catname, "hasagent");
1213 if (ast_true(hasagent) || (!hasagent && genhasagent)) {
1215 const char *fullname = ast_variable_retrieve(ucfg, catname, "fullname");
1216 const char *secret = ast_variable_retrieve(ucfg, catname, "secret");
1221 snprintf(tmp, sizeof(tmp), "%s,%s,%s", catname, secret,fullname);
1225 catname = ast_category_browse(ucfg, catname);
1227 ast_config_destroy(ucfg);
1229 AST_LIST_TRAVERSE_SAFE_BEGIN(&agents, p, list) {
1231 AST_LIST_REMOVE_CURRENT(list);
1232 /* Destroy if appropriate */
1235 ast_mutex_destroy(&p->lock);
1236 ast_mutex_destroy(&p->app_lock);
1237 ast_cond_destroy(&p->app_complete_cond);
1240 /* Cause them to hang up */
1241 ast_softhangup(p->chan, AST_SOFTHANGUP_EXPLICIT);
1246 AST_LIST_TRAVERSE_SAFE_END;
1247 AST_LIST_UNLOCK(&agents);
1248 ast_config_destroy(cfg);
1252 static int check_availability(struct agent_pvt *newlyavailable, int needlock)
1254 struct ast_channel *chan=NULL, *parent=NULL;
1255 struct agent_pvt *p;
1258 ast_debug(1, "Checking availability of '%s'\n", newlyavailable->agent);
1260 AST_LIST_LOCK(&agents);
1261 AST_LIST_TRAVERSE(&agents, p, list) {
1262 if (p == newlyavailable) {
1265 ast_mutex_lock(&p->lock);
1266 if (!p->abouttograb && p->pending && ((p->group && (newlyavailable->group & p->group)) || !strcmp(p->agent, newlyavailable->agent))) {
1267 ast_debug(1, "Call '%s' looks like a winner for agent '%s'\n", p->owner->name, newlyavailable->agent);
1268 /* We found a pending call, time to merge */
1269 chan = agent_new(newlyavailable, AST_STATE_DOWN, p->owner ? p->owner->linkedid : NULL);
1272 ast_mutex_unlock(&p->lock);
1275 ast_mutex_unlock(&p->lock);
1278 AST_LIST_UNLOCK(&agents);
1279 if (parent && chan) {
1280 if (newlyavailable->ackcall) {
1281 /* Don't do beep here */
1284 ast_debug(3, "Playing beep, lang '%s'\n", newlyavailable->chan->language);
1285 res = ast_streamfile(newlyavailable->chan, beep, newlyavailable->chan->language);
1286 ast_debug(3, "Played beep, result '%d'\n", res);
1288 res = ast_waitstream(newlyavailable->chan, "");
1289 ast_debug(1, "Waited for stream, result '%d'\n", res);
1293 /* Note -- parent may have disappeared */
1294 if (p->abouttograb) {
1295 newlyavailable->acknowledged = 1;
1296 /* Safe -- agent lock already held */
1297 ast_setstate(parent, AST_STATE_UP);
1298 ast_setstate(chan, AST_STATE_UP);
1299 ast_copy_string(parent->context, chan->context, sizeof(parent->context));
1300 /* Go ahead and mark the channel as a zombie so that masquerade will
1301 destroy it for us, and we need not call ast_hangup */
1302 ast_set_flag(chan, AST_FLAG_ZOMBIE);
1303 ast_channel_masquerade(parent, chan);
1306 ast_debug(1, "Sneaky, parent disappeared in the mean time...\n");
1307 agent_cleanup(newlyavailable);
1310 ast_debug(1, "Ugh... Agent hung up at exactly the wrong time\n");
1311 agent_cleanup(newlyavailable);
1317 static int check_beep(struct agent_pvt *newlyavailable, int needlock)
1319 struct agent_pvt *p;
1322 ast_debug(1, "Checking beep availability of '%s'\n", newlyavailable->agent);
1324 AST_LIST_LOCK(&agents);
1325 AST_LIST_TRAVERSE(&agents, p, list) {
1326 if (p == newlyavailable) {
1329 ast_mutex_lock(&p->lock);
1330 if (!p->abouttograb && p->pending && ((p->group && (newlyavailable->group & p->group)) || !strcmp(p->agent, newlyavailable->agent))) {
1331 ast_debug(1, "Call '%s' looks like a would-be winner for agent '%s'\n", p->owner->name, newlyavailable->agent);
1332 ast_mutex_unlock(&p->lock);
1335 ast_mutex_unlock(&p->lock);
1338 AST_LIST_UNLOCK(&agents);
1340 ast_mutex_unlock(&newlyavailable->lock);
1341 ast_debug(3, "Playing beep, lang '%s'\n", newlyavailable->chan->language);
1342 res = ast_streamfile(newlyavailable->chan, beep, newlyavailable->chan->language);
1343 ast_debug(1, "Played beep, result '%d'\n", res);
1345 res = ast_waitstream(newlyavailable->chan, "");
1346 ast_debug(1, "Waited for stream, result '%d'\n", res);
1348 ast_mutex_lock(&newlyavailable->lock);
1353 /*! \brief Part of the Asterisk PBX interface */
1354 static struct ast_channel *agent_request(const char *type, format_t format, const struct ast_channel* requestor, void *data, int *cause)
1356 struct agent_pvt *p;
1357 struct ast_channel *chan = NULL;
1359 ast_group_t groupmatch;
1366 if ((s[0] == '@') && (sscanf(s + 1, "%30d", &groupoff) == 1)) {
1367 groupmatch = (1 << groupoff);
1368 } else if ((s[0] == ':') && (sscanf(s + 1, "%30d", &groupoff) == 1)) {
1369 groupmatch = (1 << groupoff);
1374 /* Check actual logged in agents first */
1375 AST_LIST_LOCK(&agents);
1376 AST_LIST_TRAVERSE(&agents, p, list) {
1377 ast_mutex_lock(&p->lock);
1378 if (!p->pending && ((groupmatch && (p->group & groupmatch)) || !strcmp(data, p->agent))) {
1382 if (!p->lastdisc.tv_sec || (now.tv_sec >= p->lastdisc.tv_sec)) {
1383 p->lastdisc = ast_tv(0, 0);
1384 /* Agent must be registered, but not have any active call, and not be in a waiting state */
1385 if (!p->owner && p->chan) {
1387 chan = agent_new(p, AST_STATE_DOWN, requestor ? requestor->linkedid : NULL);
1390 ast_mutex_unlock(&p->lock);
1395 ast_mutex_unlock(&p->lock);
1398 AST_LIST_TRAVERSE(&agents, p, list) {
1399 ast_mutex_lock(&p->lock);
1400 if (!p->pending && ((groupmatch && (p->group & groupmatch)) || !strcmp(data, p->agent))) {
1405 if (!p->lastdisc.tv_sec || (now.tv_sec >= p->lastdisc.tv_sec)) {
1406 p->lastdisc = ast_tv(0, 0);
1407 /* Agent must be registered, but not have any active call, and not be in a waiting state */
1408 if (!p->owner && p->chan) {
1409 /* Could still get a fixed agent */
1410 chan = agent_new(p, AST_STATE_DOWN, requestor ? requestor->linkedid : NULL);
1413 ast_mutex_unlock(&p->lock);
1418 ast_mutex_unlock(&p->lock);
1422 if (!chan && waitforagent) {
1423 /* No agent available -- but we're requesting to wait for one.
1424 Allocate a place holder */
1426 ast_debug(1, "Creating place holder for '%s'\n", s);
1427 p = add_agent(data, 1);
1428 p->group = groupmatch;
1429 chan = agent_new(p, AST_STATE_DOWN, requestor ? requestor->linkedid : NULL);
1431 ast_log(LOG_WARNING, "Weird... Fix this to drop the unused pending agent\n");
1433 ast_debug(1, "Not creating place holder for '%s' since nobody logged in\n", s);
1436 *cause = hasagent ? AST_CAUSE_BUSY : AST_CAUSE_UNREGISTERED;
1437 AST_LIST_UNLOCK(&agents);
1441 static force_inline int powerof(unsigned int d)
1452 * Lists agents and their status to the Manager API.
1453 * It is registered on load_module() and it gets called by the manager backend.
1457 * \sa action_agent_logoff(), load_module().
1459 static int action_agents(struct mansession *s, const struct message *m)
1461 const char *id = astman_get_header(m,"ActionID");
1462 char idText[256] = "";
1463 struct agent_pvt *p;
1464 char *username = NULL;
1465 char *loginChan = NULL;
1466 char *talkingto = NULL;
1467 char *talkingtoChan = NULL;
1468 char *status = NULL;
1470 if (!ast_strlen_zero(id))
1471 snprintf(idText, sizeof(idText) ,"ActionID: %s\r\n", id);
1472 astman_send_ack(s, m, "Agents will follow");
1473 AST_LIST_LOCK(&agents);
1474 AST_LIST_TRAVERSE(&agents, p, list) {
1475 ast_mutex_lock(&p->lock);
1478 AGENT_LOGGEDOFF - Agent isn't logged in
1479 AGENT_IDLE - Agent is logged in, and waiting for call
1480 AGENT_ONCALL - Agent is logged in, and on a call
1481 AGENT_UNKNOWN - Don't know anything about agent. Shouldn't ever get this. */
1483 username = S_OR(p->name, "None");
1485 /* Set a default status. It 'should' get changed. */
1486 status = "AGENT_UNKNOWN";
1489 loginChan = ast_strdupa(p->chan->name);
1490 if (p->owner && p->owner->_bridge) {
1491 talkingto = p->chan->cid.cid_num;
1492 if (ast_bridged_channel(p->owner))
1493 talkingtoChan = ast_strdupa(ast_bridged_channel(p->owner)->name);
1495 talkingtoChan = "n/a";
1496 status = "AGENT_ONCALL";
1499 talkingtoChan = "n/a";
1500 status = "AGENT_IDLE";
1505 talkingtoChan = "n/a";
1506 status = "AGENT_LOGGEDOFF";
1509 astman_append(s, "Event: Agents\r\n"
1513 "LoggedInChan: %s\r\n"
1514 "LoggedInTime: %d\r\n"
1516 "TalkingToChan: %s\r\n"
1519 p->agent, username, status, loginChan, (int)p->loginstart, talkingto, talkingtoChan, idText);
1520 ast_mutex_unlock(&p->lock);
1522 AST_LIST_UNLOCK(&agents);
1523 astman_append(s, "Event: AgentsComplete\r\n"
1529 static int agent_logoff(const char *agent, int soft)
1531 struct agent_pvt *p;
1532 int ret = -1; /* Return -1 if no agent if found */
1534 AST_LIST_LOCK(&agents);
1535 AST_LIST_TRAVERSE(&agents, p, list) {
1536 if (!strcasecmp(p->agent, agent)) {
1538 if (p->owner || p->chan) {
1540 ast_mutex_lock(&p->lock);
1542 while (p->owner && ast_channel_trylock(p->owner)) {
1543 DEADLOCK_AVOIDANCE(&p->lock);
1546 ast_softhangup(p->owner, AST_SOFTHANGUP_EXPLICIT);
1547 ast_channel_unlock(p->owner);
1550 while (p->chan && ast_channel_trylock(p->chan)) {
1551 DEADLOCK_AVOIDANCE(&p->lock);
1554 ast_softhangup(p->chan, AST_SOFTHANGUP_EXPLICIT);
1555 ast_channel_unlock(p->chan);
1558 ast_mutex_unlock(&p->lock);
1565 AST_LIST_UNLOCK(&agents);
1570 static char *agent_logoff_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1577 e->command = "agent logoff";
1579 "Usage: agent logoff <channel> [soft]\n"
1580 " Sets an agent as no longer logged in.\n"
1581 " If 'soft' is specified, do not hangup existing calls.\n";
1584 return complete_agent_logoff_cmd(a->line, a->word, a->pos, a->n);
1587 if (a->argc < 3 || a->argc > 4)
1588 return CLI_SHOWUSAGE;
1589 if (a->argc == 4 && strcasecmp(a->argv[3], "soft"))
1590 return CLI_SHOWUSAGE;
1592 agent = a->argv[2] + 6;
1593 ret = agent_logoff(agent, a->argc == 4);
1595 ast_cli(a->fd, "Logging out %s\n", agent);
1601 * Sets an agent as no longer logged in in the Manager API.
1602 * It is registered on load_module() and it gets called by the manager backend.
1606 * \sa action_agents(), load_module().
1608 static int action_agent_logoff(struct mansession *s, const struct message *m)
1610 const char *agent = astman_get_header(m, "Agent");
1611 const char *soft_s = astman_get_header(m, "Soft"); /* "true" is don't hangup */
1613 int ret; /* return value of agent_logoff */
1615 if (ast_strlen_zero(agent)) {
1616 astman_send_error(s, m, "No agent specified");
1620 soft = ast_true(soft_s) ? 1 : 0;
1621 ret = agent_logoff(agent, soft);
1623 astman_send_ack(s, m, "Agent logged out");
1625 astman_send_error(s, m, "No such agent");
1630 static char *complete_agent_logoff_cmd(const char *line, const char *word, int pos, int state)
1635 struct agent_pvt *p;
1636 char name[AST_MAX_AGENT];
1637 int which = 0, len = strlen(word);
1639 AST_LIST_LOCK(&agents);
1640 AST_LIST_TRAVERSE(&agents, p, list) {
1641 snprintf(name, sizeof(name), "Agent/%s", p->agent);
1642 if (!strncasecmp(word, name, len) && p->loginstart && ++which > state) {
1643 ret = ast_strdup(name);
1647 AST_LIST_UNLOCK(&agents);
1648 } else if (pos == 3 && state == 0)
1649 return ast_strdup("soft");
1655 * Show agents in cli.
1657 static char *agents_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1659 struct agent_pvt *p;
1660 char username[AST_MAX_BUF];
1661 char location[AST_MAX_BUF] = "";
1662 char talkingto[AST_MAX_BUF] = "";
1663 char music[AST_MAX_BUF];
1664 int count_agents = 0; /*!< Number of agents configured */
1665 int online_agents = 0; /*!< Number of online agents */
1666 int offline_agents = 0; /*!< Number of offline agents */
1670 e->command = "agent show";
1672 "Usage: agent show\n"
1673 " Provides summary information on agents.\n";
1680 return CLI_SHOWUSAGE;
1682 AST_LIST_LOCK(&agents);
1683 AST_LIST_TRAVERSE(&agents, p, list) {
1684 ast_mutex_lock(&p->lock);
1687 ast_cli(a->fd, "-- Pending call to group %d\n", powerof(p->group));
1689 ast_cli(a->fd, "-- Pending call to agent %s\n", p->agent);
1691 if (!ast_strlen_zero(p->name))
1692 snprintf(username, sizeof(username), "(%s) ", p->name);
1696 snprintf(location, sizeof(location), "logged in on %s", p->chan->name);
1697 if (p->owner && ast_bridged_channel(p->owner))
1698 snprintf(talkingto, sizeof(talkingto), " talking to %s", ast_bridged_channel(p->owner)->name);
1700 strcpy(talkingto, " is idle");
1703 strcpy(location, "not logged in");
1704 talkingto[0] = '\0';
1707 if (!ast_strlen_zero(p->moh))
1708 snprintf(music, sizeof(music), " (musiconhold is '%s')", p->moh);
1709 ast_cli(a->fd, "%-12.12s %s%s%s%s\n", p->agent,
1710 username, location, talkingto, music);
1713 ast_mutex_unlock(&p->lock);
1715 AST_LIST_UNLOCK(&agents);
1716 if ( !count_agents )
1717 ast_cli(a->fd, "No Agents are configured in %s\n",config);
1719 ast_cli(a->fd, "%d agents configured [%d online , %d offline]\n",count_agents, online_agents, offline_agents);
1720 ast_cli(a->fd, "\n");
1726 static char *agents_show_online(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1728 struct agent_pvt *p;
1729 char username[AST_MAX_BUF];
1730 char location[AST_MAX_BUF] = "";
1731 char talkingto[AST_MAX_BUF] = "";
1732 char music[AST_MAX_BUF];
1733 int count_agents = 0; /* Number of agents configured */
1734 int online_agents = 0; /* Number of online agents */
1735 int agent_status = 0; /* 0 means offline, 1 means online */
1739 e->command = "agent show online";
1741 "Usage: agent show online\n"
1742 " Provides a list of all online agents.\n";
1749 return CLI_SHOWUSAGE;
1751 AST_LIST_LOCK(&agents);
1752 AST_LIST_TRAVERSE(&agents, p, list) {
1753 agent_status = 0; /* reset it to offline */
1754 ast_mutex_lock(&p->lock);
1755 if (!ast_strlen_zero(p->name))
1756 snprintf(username, sizeof(username), "(%s) ", p->name);
1760 snprintf(location, sizeof(location), "logged in on %s", p->chan->name);
1761 if (p->owner && ast_bridged_channel(p->owner))
1762 snprintf(talkingto, sizeof(talkingto), " talking to %s", ast_bridged_channel(p->owner)->name);
1764 strcpy(talkingto, " is idle");
1768 if (!ast_strlen_zero(p->moh))
1769 snprintf(music, sizeof(music), " (musiconhold is '%s')", p->moh);
1771 ast_cli(a->fd, "%-12.12s %s%s%s%s\n", p->agent, username, location, talkingto, music);
1773 ast_mutex_unlock(&p->lock);
1775 AST_LIST_UNLOCK(&agents);
1777 ast_cli(a->fd, "No Agents are configured in %s\n", config);
1779 ast_cli(a->fd, "%d agents online\n", online_agents);
1780 ast_cli(a->fd, "\n");
1784 static const char agent_logoff_usage[] =
1785 "Usage: agent logoff <channel> [soft]\n"
1786 " Sets an agent as no longer logged in.\n"
1787 " If 'soft' is specified, do not hangup existing calls.\n";
1789 static struct ast_cli_entry cli_agents[] = {
1790 AST_CLI_DEFINE(agents_show, "Show status of agents"),
1791 AST_CLI_DEFINE(agents_show_online, "Show all online agents"),
1792 AST_CLI_DEFINE(agent_logoff_cmd, "Sets an agent offline"),
1796 * Called by the AgentLogin application (from the dial plan).
1798 * \brief Log in agent application.
1803 * \sa agentmonitoroutgoing_exec(), load_module().
1805 static int login_exec(struct ast_channel *chan, const char *data)
1809 int max_login_tries = maxlogintries;
1810 struct agent_pvt *p;
1811 struct ast_module_user *u;
1812 int login_state = 0;
1813 char user[AST_MAX_AGENT] = "";
1814 char pass[AST_MAX_AGENT];
1815 char agent[AST_MAX_AGENT] = "";
1816 char xpass[AST_MAX_AGENT] = "";
1819 AST_DECLARE_APP_ARGS(args,
1820 AST_APP_ARG(agent_id);
1821 AST_APP_ARG(options);
1822 AST_APP_ARG(extension);
1824 const char *tmpoptions = NULL;
1825 int play_announcement = 1;
1826 char agent_goodbye[AST_MAX_FILENAME_LEN];
1827 int update_cdr = updatecdr;
1828 char *filename = "agent-loginok";
1830 u = ast_module_user_add(chan);
1832 parse = ast_strdupa(data);
1834 AST_STANDARD_APP_ARGS(args, parse);
1836 ast_copy_string(agent_goodbye, agentgoodbye, sizeof(agent_goodbye));
1838 ast_channel_lock(chan);
1839 /* Set Channel Specific Login Overrides */
1840 if (!ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENTLMAXLOGINTRIES"))) {
1841 max_login_tries = atoi(pbx_builtin_getvar_helper(chan, "AGENTMAXLOGINTRIES"));
1842 if (max_login_tries < 0)
1843 max_login_tries = 0;
1844 tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTMAXLOGINTRIES");
1845 ast_verb(3, "Saw variable AGENTMAXLOGINTRIES=%s, setting max_login_tries to: %d on Channel '%s'.\n",tmpoptions,max_login_tries,chan->name);
1847 if (!ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENTUPDATECDR"))) {
1848 if (ast_true(pbx_builtin_getvar_helper(chan, "AGENTUPDATECDR")))
1852 tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTUPDATECDR");
1853 ast_verb(3, "Saw variable AGENTUPDATECDR=%s, setting update_cdr to: %d on Channel '%s'.\n",tmpoptions,update_cdr,chan->name);
1855 if (!ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENTGOODBYE"))) {
1856 strcpy(agent_goodbye, pbx_builtin_getvar_helper(chan, "AGENTGOODBYE"));
1857 tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTGOODBYE");
1858 ast_verb(3, "Saw variable AGENTGOODBYE=%s, setting agent_goodbye to: %s on Channel '%s'.\n",tmpoptions,agent_goodbye,chan->name);
1860 ast_channel_unlock(chan);
1861 /* End Channel Specific Login Overrides */
1863 if (!ast_strlen_zero(args.options)) {
1864 if (strchr(args.options, 's')) {
1865 play_announcement = 0;
1869 if (chan->_state != AST_STATE_UP)
1870 res = ast_answer(chan);
1872 if (!ast_strlen_zero(args.agent_id))
1873 ast_copy_string(user, args.agent_id, AST_MAX_AGENT);
1875 res = ast_app_getdata(chan, "agent-user", user, sizeof(user) - 1, 0);
1877 while (!res && (max_login_tries==0 || tries < max_login_tries)) {
1879 /* Check for password */
1880 AST_LIST_LOCK(&agents);
1881 AST_LIST_TRAVERSE(&agents, p, list) {
1882 if (!strcmp(p->agent, user) && !p->pending)
1883 ast_copy_string(xpass, p->password, sizeof(xpass));
1885 AST_LIST_UNLOCK(&agents);
1887 if (!ast_strlen_zero(xpass))
1888 res = ast_app_getdata(chan, "agent-pass", pass, sizeof(pass) - 1, 0);
1892 errmsg = "agent-incorrect";
1895 ast_log(LOG_NOTICE, "user: %s, pass: %s\n", user, pass);
1898 /* Check again for accuracy */
1899 AST_LIST_LOCK(&agents);
1900 AST_LIST_TRAVERSE(&agents, p, list) {
1901 int unlock_channel = 1;
1902 ast_channel_lock(chan);
1903 ast_mutex_lock(&p->lock);
1904 if (!strcmp(p->agent, user) &&
1905 !strcmp(p->password, pass) && !p->pending) {
1906 login_state = 1; /* Successful Login */
1908 /* Ensure we can't be gotten until we're done */
1909 p->lastdisc = ast_tvnow();
1910 p->lastdisc.tv_sec++;
1912 /* Set Channel Specific Agent Overrides */
1913 if (!ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENTACKCALL"))) {
1914 if (ast_true(pbx_builtin_getvar_helper(chan, "AGENTACKCALL"))) {
1919 tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTACKCALL");
1920 ast_verb(3, "Saw variable AGENTACKCALL=%s, setting ackcall to: %d for Agent '%s'.\n", tmpoptions, p->ackcall, p->agent);
1921 ast_set_flag(p, AGENT_FLAG_ACKCALL);
1923 p->ackcall = ackcall;
1925 if (!ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF"))) {
1926 p->autologoff = atoi(pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF"));
1927 if (p->autologoff < 0)
1929 tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF");
1930 ast_verb(3, "Saw variable AGENTAUTOLOGOFF=%s, setting autologff to: %d for Agent '%s'.\n", tmpoptions, p->autologoff, p->agent);
1931 ast_set_flag(p, AGENT_FLAG_AUTOLOGOFF);
1933 p->autologoff = autologoff;
1935 if (!ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME"))) {
1936 p->wrapuptime = atoi(pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME"));
1937 if (p->wrapuptime < 0)
1939 tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME");
1940 ast_verb(3, "Saw variable AGENTWRAPUPTIME=%s, setting wrapuptime to: %d for Agent '%s'.\n", tmpoptions, p->wrapuptime, p->agent);
1941 ast_set_flag(p, AGENT_FLAG_WRAPUPTIME);
1943 p->wrapuptime = wrapuptime;
1945 tmpoptions = pbx_builtin_getvar_helper(chan, "AGENTACCEPTDTMF");
1946 if (!ast_strlen_zero(tmpoptions)) {
1947 p->acceptdtmf = *tmpoptions;
1948 ast_verb(3, "Saw variable AGENTACCEPTDTMF=%s, setting acceptdtmf to: %c for Agent '%s'.\n", tmpoptions, p->acceptdtmf, p->agent);
1949 ast_set_flag(p, AGENT_FLAG_ACCEPTDTMF);
1951 tmpoptions = pbx_builtin_getvar_helper(chan, "AGENTENDDTMF");
1952 if (!ast_strlen_zero(tmpoptions)) {
1953 p->enddtmf = *tmpoptions;
1954 ast_verb(3, "Saw variable AGENTENDDTMF=%s, setting enddtmf to: %c for Agent '%s'.\n", tmpoptions, p->enddtmf, p->agent);
1955 ast_set_flag(p, AGENT_FLAG_ENDDTMF);
1957 ast_channel_unlock(chan);
1959 /* End Channel Specific Agent Overrides */
1962 snprintf(agent, sizeof(agent), "Agent/%s", p->agent);
1964 p->logincallerid[0] = '\0';
1965 p->acknowledged = 0;
1967 ast_mutex_unlock(&p->lock);
1968 AST_LIST_UNLOCK(&agents);
1969 if( !res && play_announcement==1 )
1970 res = ast_streamfile(chan, filename, chan->language);
1972 ast_waitstream(chan, "");
1973 AST_LIST_LOCK(&agents);
1974 ast_mutex_lock(&p->lock);
1976 res = ast_set_read_format(chan, ast_best_codec(chan->nativeformats));
1978 ast_log(LOG_WARNING, "Unable to set read format to %s\n", ast_getformatname(ast_best_codec(chan->nativeformats)));
1981 res = ast_set_write_format(chan, ast_best_codec(chan->nativeformats));
1983 ast_log(LOG_WARNING, "Unable to set write format to %s\n", ast_getformatname(ast_best_codec(chan->nativeformats)));
1985 /* Check once more just in case */
1989 ast_indicate_data(chan, AST_CONTROL_HOLD,
1991 !ast_strlen_zero(p->moh) ? strlen(p->moh) + 1 : 0);
1992 if (p->loginstart == 0)
1993 time(&p->loginstart);
1994 manager_event(EVENT_FLAG_AGENT, "Agentlogin",
1998 p->agent, chan->name, chan->uniqueid);
1999 if (update_cdr && chan->cdr)
2000 snprintf(chan->cdr->channel, sizeof(chan->cdr->channel), "Agent/%s", p->agent);
2001 ast_queue_log("NONE", chan->uniqueid, agent, "AGENTLOGIN", "%s", chan->name);
2002 ast_verb(2, "Agent '%s' logged in (format %s/%s)\n", p->agent,
2003 ast_getformatname(chan->readformat), ast_getformatname(chan->writeformat));
2004 /* Login this channel and wait for it to go away */
2009 check_availability(p, 0);
2011 ast_mutex_unlock(&p->lock);
2012 AST_LIST_UNLOCK(&agents);
2013 ast_devstate_changed(AST_DEVICE_NOT_INUSE, "Agent/%s", p->agent);
2015 ast_mutex_lock(&p->lock);
2016 if (p->deferlogoff && p->chan) {
2017 ast_softhangup(p->chan, AST_SOFTHANGUP_EXPLICIT);
2020 if (p->chan != chan)
2022 ast_mutex_unlock(&p->lock);
2023 /* Yield here so other interested threads can kick in. */
2028 AST_LIST_LOCK(&agents);
2029 ast_mutex_lock(&p->lock);
2030 if (p->lastdisc.tv_sec) {
2031 if (ast_tvdiff_ms(ast_tvnow(), p->lastdisc) > 0) {
2032 ast_debug(1, "Wrapup time for %s expired!\n", p->agent);
2033 p->lastdisc = ast_tv(0, 0);
2034 ast_devstate_changed(AST_DEVICE_NOT_INUSE, "Agent/%s", p->agent);
2038 check_availability(p, 0);
2042 ast_mutex_unlock(&p->lock);
2043 AST_LIST_UNLOCK(&agents);
2044 /* Synchronize channel ownership between call to agent and itself. */
2045 ast_mutex_lock(&p->app_lock);
2046 if (p->app_lock_flag == 1) {
2047 ast_cond_wait(&p->app_complete_cond, &p->app_lock);
2049 ast_mutex_unlock(&p->app_lock);
2050 ast_mutex_lock(&p->lock);
2051 ast_mutex_unlock(&p->lock);
2053 res = agent_ack_sleep(p);
2055 res = ast_safe_sleep_conditional( chan, 1000, agent_cont_sleep, p );
2057 if (p->ackcall && (res == 1)) {
2058 AST_LIST_LOCK(&agents);
2059 ast_mutex_lock(&p->lock);
2060 check_availability(p, 0);
2061 ast_mutex_unlock(&p->lock);
2062 AST_LIST_UNLOCK(&agents);
2067 ast_mutex_lock(&p->lock);
2068 if (res && p->owner)
2069 ast_log(LOG_WARNING, "Huh? We broke out when there was still an owner?\n");
2070 /* Log us off if appropriate */
2071 if (p->chan == chan) {
2074 p->acknowledged = 0;
2075 logintime = time(NULL) - p->loginstart;
2077 ast_mutex_unlock(&p->lock);
2078 manager_event(EVENT_FLAG_AGENT, "Agentlogoff",
2080 "Logintime: %ld\r\n"
2082 p->agent, logintime, chan->uniqueid);
2083 ast_queue_log("NONE", chan->uniqueid, agent, "AGENTLOGOFF", "%s|%ld", chan->name, logintime);
2084 ast_verb(2, "Agent '%s' logged out\n", p->agent);
2085 /* If there is no owner, go ahead and kill it now */
2086 ast_devstate_changed(AST_DEVICE_UNAVAILABLE, "Agent/%s", p->agent);
2087 if (p->dead && !p->owner) {
2088 ast_mutex_destroy(&p->lock);
2089 ast_mutex_destroy(&p->app_lock);
2090 ast_cond_destroy(&p->app_complete_cond);
2095 ast_mutex_unlock(&p->lock);
2100 ast_mutex_unlock(&p->lock);
2101 errmsg = "agent-alreadyon";
2106 ast_mutex_unlock(&p->lock);
2107 if (unlock_channel) {
2108 ast_channel_unlock(chan);
2112 AST_LIST_UNLOCK(&agents);
2114 if (!res && (max_login_tries==0 || tries < max_login_tries))
2115 res = ast_app_getdata(chan, errmsg, user, sizeof(user) - 1, 0);
2119 res = ast_safe_sleep(chan, 500);
2121 ast_module_user_remove(u);
2127 * \brief Called by the AgentMonitorOutgoing application (from the dial plan).
2132 * \sa login_exec(), load_module().
2134 static int agentmonitoroutgoing_exec(struct ast_channel *chan, const char *data)
2136 int exitifnoagentid = 0;
2138 int changeoutgoing = 0;
2140 char agent[AST_MAX_AGENT];
2143 if (strchr(data, 'd'))
2144 exitifnoagentid = 1;
2145 if (strchr(data, 'n'))
2147 if (strchr(data, 'c'))
2150 if (chan->cid.cid_num) {
2152 char agentvar[AST_MAX_BUF];
2153 snprintf(agentvar, sizeof(agentvar), "%s_%s", GETAGENTBYCALLERID, chan->cid.cid_num);
2154 if ((tmp = pbx_builtin_getvar_helper(NULL, agentvar))) {
2155 struct agent_pvt *p;
2156 ast_copy_string(agent, tmp, sizeof(agent));
2157 AST_LIST_LOCK(&agents);
2158 AST_LIST_TRAVERSE(&agents, p, list) {
2159 if (!strcasecmp(p->agent, tmp)) {
2160 if (changeoutgoing) snprintf(chan->cdr->channel, sizeof(chan->cdr->channel), "Agent/%s", p->agent);
2161 __agent_start_monitoring(chan, p, 1);
2165 AST_LIST_UNLOCK(&agents);
2170 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);
2175 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");
2178 if (exitifnoagentid)
2184 /*! \brief Part of PBX channel interface */
2185 static int agent_devicestate(void *data)
2187 struct agent_pvt *p;
2189 ast_group_t groupmatch;
2191 int res = AST_DEVICE_INVALID;
2194 if ((s[0] == '@') && (sscanf(s + 1, "%30d", &groupoff) == 1))
2195 groupmatch = (1 << groupoff);
2196 else if ((s[0] == ':') && (sscanf(s + 1, "%30d", &groupoff) == 1)) {
2197 groupmatch = (1 << groupoff);
2201 /* Check actual logged in agents first */
2202 AST_LIST_LOCK(&agents);
2203 AST_LIST_TRAVERSE(&agents, p, list) {
2204 ast_mutex_lock(&p->lock);
2205 if (!p->pending && ((groupmatch && (p->group & groupmatch)) || !strcmp(data, p->agent))) {
2207 if (res != AST_DEVICE_INUSE)
2208 res = AST_DEVICE_BUSY;
2210 if (res == AST_DEVICE_BUSY)
2211 res = AST_DEVICE_INUSE;
2213 if (res == AST_DEVICE_INVALID)
2214 res = AST_DEVICE_UNKNOWN;
2215 } else if (res == AST_DEVICE_INVALID)
2216 res = AST_DEVICE_UNAVAILABLE;
2218 if (!strcmp(data, p->agent)) {
2219 ast_mutex_unlock(&p->lock);
2223 ast_mutex_unlock(&p->lock);
2225 AST_LIST_UNLOCK(&agents);
2230 * \note This function expects the agent list to be locked
2232 static struct agent_pvt *find_agent(char *agentid)
2234 struct agent_pvt *cur;
2236 AST_LIST_TRAVERSE(&agents, cur, list) {
2237 if (!strcmp(cur->agent, agentid))
2244 static int function_agent(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
2247 AST_DECLARE_APP_ARGS(args,
2248 AST_APP_ARG(agentid);
2252 struct agent_pvt *agent;
2256 if (ast_strlen_zero(data)) {
2257 ast_log(LOG_WARNING, "The AGENT function requires an argument - agentid!\n");
2261 parse = ast_strdupa(data);
2263 AST_NONSTANDARD_APP_ARGS(args, parse, ':');
2265 args.item = "status";
2267 AST_LIST_LOCK(&agents);
2269 if (!(agent = find_agent(args.agentid))) {
2270 AST_LIST_UNLOCK(&agents);
2271 ast_log(LOG_WARNING, "Agent '%s' not found!\n", args.agentid);
2275 if (!strcasecmp(args.item, "status")) {
2276 char *status = "LOGGEDOUT";
2278 status = "LOGGEDIN";
2280 ast_copy_string(buf, status, len);
2281 } else if (!strcasecmp(args.item, "password"))
2282 ast_copy_string(buf, agent->password, len);
2283 else if (!strcasecmp(args.item, "name"))
2284 ast_copy_string(buf, agent->name, len);
2285 else if (!strcasecmp(args.item, "mohclass"))
2286 ast_copy_string(buf, agent->moh, len);
2287 else if (!strcasecmp(args.item, "channel")) {
2289 ast_copy_string(buf, agent->chan->name, len);
2290 tmp = strrchr(buf, '-');
2294 } else if (!strcasecmp(args.item, "exten")) {
2298 AST_LIST_UNLOCK(&agents);
2303 static struct ast_custom_function agent_function = {
2305 .read = function_agent,
2310 * \brief Initialize the Agents module.
2311 * This function is being called by Asterisk when loading the module.
2312 * Among other things it registers applications, cli commands and reads the cofiguration file.
2314 * \returns int Always 0.
2316 static int load_module(void)
2318 /* Make sure we can register our agent channel type */
2319 if (ast_channel_register(&agent_tech)) {
2320 ast_log(LOG_ERROR, "Unable to register channel class 'Agent'\n");
2321 return AST_MODULE_LOAD_FAILURE;
2323 /* Read in the config */
2324 if (!read_agent_config(0))
2325 return AST_MODULE_LOAD_DECLINE;
2326 /* Dialplan applications */
2327 ast_register_application_xml(app, login_exec);
2328 ast_register_application_xml(app3, agentmonitoroutgoing_exec);
2330 /* Manager commands */
2331 ast_manager_register_xml("Agents", EVENT_FLAG_AGENT, action_agents);
2332 ast_manager_register_xml("AgentLogoff", EVENT_FLAG_AGENT, action_agent_logoff);
2335 ast_cli_register_multiple(cli_agents, ARRAY_LEN(cli_agents));
2337 /* Dialplan Functions */
2338 ast_custom_function_register(&agent_function);
2340 return AST_MODULE_LOAD_SUCCESS;
2343 static int reload(void)
2345 return read_agent_config(1);
2348 static int unload_module(void)
2350 struct agent_pvt *p;
2351 /* First, take us out of the channel loop */
2352 ast_channel_unregister(&agent_tech);
2353 /* Unregister dialplan functions */
2354 ast_custom_function_unregister(&agent_function);
2355 /* Unregister CLI commands */
2356 ast_cli_unregister_multiple(cli_agents, ARRAY_LEN(cli_agents));
2357 /* Unregister dialplan applications */
2358 ast_unregister_application(app);
2359 ast_unregister_application(app3);
2360 /* Unregister manager command */
2361 ast_manager_unregister("Agents");
2362 ast_manager_unregister("AgentLogoff");
2363 /* Unregister channel */
2364 AST_LIST_LOCK(&agents);
2365 /* Hangup all interfaces if they have an owner */
2366 while ((p = AST_LIST_REMOVE_HEAD(&agents, list))) {
2368 ast_softhangup(p->owner, AST_SOFTHANGUP_APPUNLOAD);
2371 AST_LIST_UNLOCK(&agents);
2375 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Agent Proxy Channel",
2376 .load = load_module,
2377 .unload = unload_module,