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$")
45 #include <sys/socket.h>
49 #include <netinet/in.h>
50 #include <arpa/inet.h>
51 #include <sys/signal.h>
53 #include "asterisk/lock.h"
54 #include "asterisk/channel.h"
55 #include "asterisk/config.h"
56 #include "asterisk/logger.h"
57 #include "asterisk/module.h"
58 #include "asterisk/pbx.h"
59 #include "asterisk/options.h"
60 #include "asterisk/lock.h"
61 #include "asterisk/sched.h"
62 #include "asterisk/io.h"
63 #include "asterisk/rtp.h"
64 #include "asterisk/acl.h"
65 #include "asterisk/callerid.h"
66 #include "asterisk/file.h"
67 #include "asterisk/cli.h"
68 #include "asterisk/app.h"
69 #include "asterisk/musiconhold.h"
70 #include "asterisk/manager.h"
71 #include "asterisk/features.h"
72 #include "asterisk/utils.h"
73 #include "asterisk/causes.h"
74 #include "asterisk/astdb.h"
75 #include "asterisk/devicestate.h"
76 #include "asterisk/monitor.h"
77 #include "asterisk/stringfields.h"
79 static const char tdesc
[] = "Call Agent Proxy Channel";
80 static const char config
[] = "agents.conf";
82 static const char app
[] = "AgentLogin";
83 static const char app2
[] = "AgentCallbackLogin";
84 static const char app3
[] = "AgentMonitorOutgoing";
86 static const char synopsis
[] = "Call agent login";
87 static const char synopsis2
[] = "Call agent callback login";
88 static const char synopsis3
[] = "Record agent's outgoing call";
90 static const char descrip
[] =
91 " AgentLogin([AgentNo][|options]):\n"
92 "Asks the agent to login to the system. Always returns -1. While\n"
93 "logged in, the agent can receive calls and will hear a 'beep'\n"
94 "when a new call comes in. The agent can dump the call by pressing\n"
96 "The option string may contain zero or more of the following characters:\n"
97 " 's' -- silent login - do not announce the login ok segment after agent logged in/off\n";
99 static const char descrip2
[] =
100 " AgentCallbackLogin([AgentNo][|[options][|[exten]@context]]):\n"
101 "Asks the agent to login to the system with callback.\n"
102 "The agent's callback extension is called (optionally with the specified\n"
104 "The option string may contain zero or more of the following characters:\n"
105 " 's' -- silent login - do not announce the login ok segment agent logged in/off\n";
107 static const char descrip3
[] =
108 " AgentMonitorOutgoing([options]):\n"
109 "Tries to figure out the id of the agent who is placing outgoing call based on\n"
110 "comparison of the callerid of the current interface and the global variable \n"
111 "placed by the AgentCallbackLogin application. That's why it should be used only\n"
112 "with the AgentCallbackLogin app. Uses the monitoring functions in chan_agent \n"
113 "instead of Monitor application. That have to be configured in the agents.conf file.\n"
115 "Normally the app returns 0 unless the options are passed. Also if the callerid or\n"
116 "the agentid are not specified it'll look for n+101 priority.\n"
118 " 'd' - make the app return -1 if there is an error condition and there is\n"
119 " no extension n+101\n"
120 " 'c' - change the CDR so that the source of the call is 'Agent/agent_id'\n"
121 " 'n' - don't generate the warnings when there is no callerid or the\n"
122 " agentid is not known.\n"
123 " It's handy if you want to have one context for agent and non-agent calls.\n";
125 static const char mandescr_agents
[] =
126 "Description: Will list info about all possible agents.\n"
129 static const char mandescr_agent_logoff
[] =
130 "Description: Sets an agent as no longer logged in.\n"
131 "Variables: (Names marked with * are required)\n"
132 " *Agent: Agent ID of the agent to log off\n"
133 " Soft: Set to 'true' to not hangup existing calls\n";
135 static const char mandescr_agent_callback_login
[] =
136 "Description: Sets an agent as logged in with callback.\n"
137 "Variables: (Names marked with * are required)\n"
138 " *Agent: Agent ID of the agent to login\n"
139 " *Exten: Extension to use for callback\n"
140 " Context: Context to use for callback\n"
141 " AckCall: Set to 'true' to require an acknowledgement by '#' when agent is called back\n"
142 " WrapupTime: the minimum amount of time after disconnecting before the caller can receive a new call\n";
144 static char moh
[80] = "default";
146 #define AST_MAX_AGENT 80 /*!< Agent ID or Password max length */
147 #define AST_MAX_BUF 256
148 #define AST_MAX_FILENAME_LEN 256
150 static const char pa_family
[] = "Agents"; /*!< Persistent Agents astdb family */
151 #define PA_MAX_LEN 2048 /*!< The maximum length of each persistent member agent database entry */
153 static int persistent_agents
= 0; /*!< queues.conf [general] option */
154 static void dump_agents(void);
156 static ast_group_t group
;
157 static int autologoff
;
158 static int wrapuptime
;
161 static int multiplelogin
= 1;
162 static int autologoffunavail
= 0;
164 static int maxlogintries
= 3;
165 static char agentgoodbye
[AST_MAX_FILENAME_LEN
] = "vm-goodbye";
167 static int recordagentcalls
= 0;
168 static char recordformat
[AST_MAX_BUF
] = "";
169 static char recordformatext
[AST_MAX_BUF
] = "";
170 static char urlprefix
[AST_MAX_BUF
] = "";
171 static char savecallsin
[AST_MAX_BUF
] = "";
172 static int updatecdr
= 0;
173 static char beep
[AST_MAX_BUF
] = "beep";
175 #define GETAGENTBYCALLERID "AGENTBYCALLERID"
177 /*! \brief Structure representing an agent. */
179 ast_mutex_t lock
; /*!< Channel private lock */
180 int dead
; /*!< Poised for destruction? */
181 int pending
; /*!< Not a real agent -- just pending a match */
182 int abouttograb
; /*!< About to grab */
183 int autologoff
; /*!< Auto timeout time */
184 int ackcall
; /*!< ackcall */
185 int deferlogoff
; /*!< Defer logoff to hangup */
186 time_t loginstart
; /*!< When agent first logged in (0 when logged off) */
187 time_t start
; /*!< When call started */
188 struct timeval lastdisc
; /*!< When last disconnected */
189 int wrapuptime
; /*!< Wrapup time in ms */
190 ast_group_t group
; /*!< Group memberships */
191 int acknowledged
; /*!< Acknowledged */
192 char moh
[80]; /*!< Which music on hold */
193 char agent
[AST_MAX_AGENT
]; /*!< Agent ID */
194 char password
[AST_MAX_AGENT
]; /*!< Password for Agent login */
195 char name
[AST_MAX_AGENT
];
196 int inherited_devicestate
; /*!< Does the underlying channel have a devicestate to pass? */
197 ast_mutex_t app_lock
; /**< Synchronization between owning applications */
198 volatile pthread_t owning_app
; /**< Owning application thread id */
199 volatile int app_sleep_cond
; /**< Sleep condition for the login app */
200 struct ast_channel
*owner
; /**< Agent */
201 char loginchan
[80]; /**< channel they logged in from */
202 char logincallerid
[80]; /**< Caller ID they had when they logged in */
203 struct ast_channel
*chan
; /**< Channel we use */
204 AST_LIST_ENTRY(agent_pvt
) list
; /**< Next Agent in the linked list. */
207 static AST_LIST_HEAD_STATIC(agents
, agent_pvt
); /*!< Holds the list of agents (loaded form agents.conf). */
209 #define CHECK_FORMATS(ast, p) do { \
211 if (ast->nativeformats != p->chan->nativeformats) { \
212 ast_log(LOG_DEBUG, "Native formats changing from %d to %d\n", ast->nativeformats, p->chan->nativeformats); \
213 /* Native formats changed, reset things */ \
214 ast->nativeformats = p->chan->nativeformats; \
215 ast_log(LOG_DEBUG, "Resetting read to %d and write to %d\n", ast->readformat, ast->writeformat);\
216 ast_set_read_format(ast, ast->readformat); \
217 ast_set_write_format(ast, ast->writeformat); \
219 if (p->chan->readformat != ast->rawreadformat && !p->chan->generator) \
220 ast_set_read_format(p->chan, ast->rawreadformat); \
221 if (p->chan->writeformat != ast->rawwriteformat && !p->chan->generator) \
222 ast_set_write_format(p->chan, ast->rawwriteformat); \
226 /*! \brief Cleanup moves all the relevant FD's from the 2nd to the first, but retains things
227 properly for a timingfd XXX This might need more work if agents were logged in as agents or other
228 totally impractical combinations XXX */
230 #define CLEANUP(ast, p) do { \
233 for (x=0;x<AST_MAX_FDS;x++) {\
234 if (x != AST_TIMING_FD) \
235 ast->fds[x] = p->chan->fds[x]; \
237 ast->fds[AST_AGENT_FD] = p->chan->fds[AST_TIMING_FD]; \
241 /*--- Forward declarations */
242 static struct ast_channel
*agent_request(const char *type
, int format
, void *data
, int *cause
);
243 static int agent_devicestate(void *data
);
244 static void agent_logoff_maintenance(struct agent_pvt
*p
, char *loginchan
, long logintime
, const char *uniqueid
, char *logcommand
);
245 static int agent_digit_begin(struct ast_channel
*ast
, char digit
);
246 static int agent_digit_end(struct ast_channel
*ast
, char digit
, unsigned int duration
);
247 static int agent_call(struct ast_channel
*ast
, char *dest
, int timeout
);
248 static int agent_hangup(struct ast_channel
*ast
);
249 static int agent_answer(struct ast_channel
*ast
);
250 static struct ast_frame
*agent_read(struct ast_channel
*ast
);
251 static int agent_write(struct ast_channel
*ast
, struct ast_frame
*f
);
252 static int agent_sendhtml(struct ast_channel
*ast
, int subclass
, const char *data
, int datalen
);
253 static int agent_sendtext(struct ast_channel
*ast
, const char *text
);
254 static int agent_indicate(struct ast_channel
*ast
, int condition
, const void *data
, size_t datalen
);
255 static int agent_fixup(struct ast_channel
*oldchan
, struct ast_channel
*newchan
);
256 static struct ast_channel
*agent_bridgedchannel(struct ast_channel
*chan
, struct ast_channel
*bridge
);
257 static void set_agentbycallerid(const char *callerid
, const char *agent
);
258 static struct ast_channel
* agent_get_base_channel(struct ast_channel
*chan
);
259 static int agent_set_base_channel(struct ast_channel
*chan
, struct ast_channel
*base
);
261 /*! \brief Channel interface description for PBX integration */
262 static const struct ast_channel_tech agent_tech
= {
264 .description
= tdesc
,
266 .requester
= agent_request
,
267 .devicestate
= agent_devicestate
,
268 .send_digit_begin
= agent_digit_begin
,
269 .send_digit_end
= agent_digit_end
,
271 .hangup
= agent_hangup
,
272 .answer
= agent_answer
,
274 .write
= agent_write
,
275 .write_video
= agent_write
,
276 .send_html
= agent_sendhtml
,
277 .send_text
= agent_sendtext
,
278 .exception
= agent_read
,
279 .indicate
= agent_indicate
,
280 .fixup
= agent_fixup
,
281 .bridged_channel
= agent_bridgedchannel
,
282 .get_base_channel
= agent_get_base_channel
,
283 .set_base_channel
= agent_set_base_channel
,
286 static int agent_devicestate_cb(const char *dev
, int state
, void *data
)
290 char basename
[AST_CHANNEL_NAME
], *tmp
;
292 /* Skip Agent status */
293 if (!strncasecmp(dev
, "Agent/", 6)) {
297 /* Try to be safe, but don't deadlock */
298 for (i
= 0; i
< 10; i
++) {
299 if ((res
= AST_LIST_TRYLOCK(&agents
)) == 0) {
307 AST_LIST_TRAVERSE(&agents
, p
, list
) {
308 ast_mutex_lock(&p
->lock
);
310 ast_copy_string(basename
, p
->chan
->name
, sizeof(basename
));
311 if ((tmp
= strrchr(basename
, '-'))) {
314 if (strcasecmp(p
->chan
->name
, dev
) == 0 || strcasecmp(basename
, dev
) == 0) {
315 p
->inherited_devicestate
= state
;
316 ast_device_state_changed("Agent/%s", p
->agent
);
319 ast_mutex_unlock(&p
->lock
);
321 AST_LIST_UNLOCK(&agents
);
326 * Adds an agent to the global list of agents.
328 * \param agent A string with the username, password and real name of an agent. As defined in agents.conf. Example: "13,169,John Smith"
329 * \param pending If it is pending or not.
330 * @return The just created agent.
331 * \sa agent_pvt, agents.
333 static struct agent_pvt
*add_agent(char *agent
, int pending
)
336 AST_DECLARE_APP_ARGS(args
,
338 AST_APP_ARG(password
);
341 char *password
= NULL
;
346 parse
= ast_strdupa(agent
);
348 /* Extract username (agt), password and name from agent (args). */
349 AST_NONSTANDARD_APP_ARGS(args
, parse
, ',');
352 ast_log(LOG_WARNING
, "A blank agent line!\n");
356 if(ast_strlen_zero(args
.agt
) ) {
357 ast_log(LOG_WARNING
, "An agent line with no agentid!\n");
362 if(!ast_strlen_zero(args
.password
)) {
363 password
= args
.password
;
364 while (*password
&& *password
< 33) password
++;
366 if(!ast_strlen_zero(args
.name
)) {
368 while (*name
&& *name
< 33) name
++;
371 /* Are we searching for the agent here ? To see if it exists already ? */
372 AST_LIST_TRAVERSE(&agents
, p
, list
) {
373 if (!pending
&& !strcmp(p
->agent
, agt
))
378 if (!(p
= ast_calloc(1, sizeof(*p
))))
380 ast_copy_string(p
->agent
, agt
, sizeof(p
->agent
));
381 ast_mutex_init(&p
->lock
);
382 ast_mutex_init(&p
->app_lock
);
383 p
->owning_app
= (pthread_t
) -1;
384 p
->app_sleep_cond
= 1;
386 p
->pending
= pending
;
387 p
->inherited_devicestate
= -1;
388 AST_LIST_INSERT_TAIL(&agents
, p
, list
);
391 ast_copy_string(p
->password
, password
? password
: "", sizeof(p
->password
));
392 ast_copy_string(p
->name
, name
? name
: "", sizeof(p
->name
));
393 ast_copy_string(p
->moh
, moh
, sizeof(p
->moh
));
394 p
->ackcall
= ackcall
;
395 p
->autologoff
= autologoff
;
397 /* If someone reduces the wrapuptime and reloads, we want it
398 * to change the wrapuptime immediately on all calls */
399 if (p
->wrapuptime
> wrapuptime
) {
400 struct timeval now
= ast_tvnow();
401 /* XXX check what is this exactly */
403 /* We won't be pedantic and check the tv_usec val */
404 if (p
->lastdisc
.tv_sec
> (now
.tv_sec
+ wrapuptime
/1000)) {
405 p
->lastdisc
.tv_sec
= now
.tv_sec
+ wrapuptime
/1000;
406 p
->lastdisc
.tv_usec
= now
.tv_usec
;
409 p
->wrapuptime
= wrapuptime
;
419 * Deletes an agent after doing some clean up.
420 * Further documentation: How safe is this function ? What state should the agent be to be cleaned.
421 * \param p Agent to be deleted.
424 static int agent_cleanup(struct agent_pvt
*p
)
426 struct ast_channel
*chan
= p
->owner
;
428 chan
->tech_pvt
= NULL
;
429 p
->app_sleep_cond
= 1;
430 /* Release ownership of the agent to other threads (presumably running the login app). */
431 ast_mutex_unlock(&p
->app_lock
);
433 ast_channel_free(chan
);
435 ast_mutex_destroy(&p
->lock
);
436 ast_mutex_destroy(&p
->app_lock
);
442 static int check_availability(struct agent_pvt
*newlyavailable
, int needlock
);
444 static int agent_answer(struct ast_channel
*ast
)
446 ast_log(LOG_WARNING
, "Huh? Agent is being asked to answer?\n");
450 static int __agent_start_monitoring(struct ast_channel
*ast
, struct agent_pvt
*p
, int needlock
)
452 char tmp
[AST_MAX_BUF
],tmp2
[AST_MAX_BUF
], *pointer
;
453 char filename
[AST_MAX_BUF
];
458 snprintf(filename
, sizeof(filename
), "agent-%s-%s",p
->agent
, ast
->uniqueid
);
459 /* substitute . for - */
460 if ((pointer
= strchr(filename
, '.')))
462 snprintf(tmp
, sizeof(tmp
), "%s%s", savecallsin
, filename
);
463 ast_monitor_start(ast
, recordformat
, tmp
, needlock
);
464 ast_monitor_setjoinfiles(ast
, 1);
465 snprintf(tmp2
, sizeof(tmp2
), "%s%s.%s", urlprefix
, filename
, recordformatext
);
467 ast_verbose("name is %s, link is %s\n",tmp
, tmp2
);
470 ast
->cdr
= ast_cdr_alloc();
471 ast_cdr_setuserfield(ast
, tmp2
);
474 ast_log(LOG_ERROR
, "Recording already started on that call.\n");
478 static int agent_start_monitoring(struct ast_channel
*ast
, int needlock
)
480 return __agent_start_monitoring(ast
, ast
->tech_pvt
, needlock
);
483 static struct ast_frame
*agent_read(struct ast_channel
*ast
)
485 struct agent_pvt
*p
= ast
->tech_pvt
;
486 struct ast_frame
*f
= NULL
;
487 static struct ast_frame answer_frame
= { AST_FRAME_CONTROL
, AST_CONTROL_ANSWER
};
489 ast_mutex_lock(&p
->lock
);
490 CHECK_FORMATS(ast
, p
);
492 ast_copy_flags(p
->chan
, ast
, AST_FLAG_EXCEPTION
);
493 p
->chan
->fdno
= (ast
->fdno
== AST_AGENT_FD
) ? AST_TIMING_FD
: ast
->fdno
;
494 f
= ast_read(p
->chan
);
498 /* If there's a channel, hang it up (if it's on a callback) make it NULL */
500 p
->chan
->_bridge
= NULL
;
501 /* Note that we don't hangup if it's not a callback because Asterisk will do it
502 for us when the PBX instance that called login finishes */
503 if (!ast_strlen_zero(p
->loginchan
)) {
505 ast_log(LOG_DEBUG
, "Bridge on '%s' being cleared (2)\n", p
->chan
->name
);
506 if (p
->owner
->_state
!= AST_STATE_UP
) {
507 int howlong
= time(NULL
) - p
->start
;
508 if (p
->autologoff
&& howlong
> p
->autologoff
) {
509 long logintime
= time(NULL
) - p
->loginstart
;
511 ast_log(LOG_NOTICE
, "Agent '%s' didn't answer/confirm within %d seconds (waited %d)\n", p
->name
, p
->autologoff
, howlong
);
512 agent_logoff_maintenance(p
, p
->loginchan
, logintime
, ast
->uniqueid
, "Autologoff");
513 if (persistent_agents
)
517 status
= pbx_builtin_getvar_helper(p
->chan
, "CHANLOCALSTATUS");
518 if (autologoffunavail
&& status
&& !strcasecmp(status
, "CHANUNAVAIL")) {
519 long logintime
= time(NULL
) - p
->loginstart
;
521 ast_log(LOG_NOTICE
, "Agent read: '%s' is not available now, auto logoff\n", p
->name
);
522 agent_logoff_maintenance(p
, p
->loginchan
, logintime
, ast
->uniqueid
, "Chanunavail");
525 if (p
->wrapuptime
&& p
->acknowledged
)
526 p
->lastdisc
= ast_tvadd(ast_tvnow(), ast_samp2tv(p
->wrapuptime
, 1000));
529 p
->inherited_devicestate
= -1;
530 ast_device_state_changed("Agent/%s", p
->agent
);
534 /* if acknowledgement is not required, and the channel is up, we may have missed
535 an AST_CONTROL_ANSWER (if there was one), so mark the call acknowledged anyway */
536 if (!p
->ackcall
&& !p
->acknowledged
&& p
->chan
&& (p
->chan
->_state
== AST_STATE_UP
))
538 switch (f
->frametype
) {
539 case AST_FRAME_CONTROL
:
540 if (f
->subclass
== AST_CONTROL_ANSWER
) {
542 if (option_verbose
> 2)
543 ast_verbose(VERBOSE_PREFIX_3
"%s answered, waiting for '#' to acknowledge\n", p
->chan
->name
);
544 /* Don't pass answer along */
549 /* Use the builtin answer frame for the
550 recording start check below. */
556 case AST_FRAME_DTMF_BEGIN
:
557 /*ignore DTMF begin's as it can cause issues with queue announce files*/
558 if((!p
->acknowledged
&& f
->subclass
== '#') || (f
->subclass
== '*' && endcall
)){
563 case AST_FRAME_DTMF_END
:
564 if (!p
->acknowledged
&& (f
->subclass
== '#')) {
565 if (option_verbose
> 2)
566 ast_verbose(VERBOSE_PREFIX_3
"%s acknowledged\n", p
->chan
->name
);
570 } else if (f
->subclass
== '*' && endcall
) {
571 /* terminates call */
576 case AST_FRAME_VOICE
:
577 case AST_FRAME_VIDEO
:
578 /* don't pass voice or video until the call is acknowledged */
579 if (!p
->acknowledged
) {
584 /* pass everything else on through */
590 if (p
->chan
&& !p
->chan
->_bridge
) {
591 if (strcasecmp(p
->chan
->tech
->type
, "Local")) {
592 p
->chan
->_bridge
= ast
;
594 ast_log(LOG_DEBUG
, "Bridge on '%s' being set to '%s' (3)\n", p
->chan
->name
, p
->chan
->_bridge
->name
);
597 ast_mutex_unlock(&p
->lock
);
598 if (recordagentcalls
&& f
== &answer_frame
)
599 agent_start_monitoring(ast
,0);
603 static int agent_sendhtml(struct ast_channel
*ast
, int subclass
, const char *data
, int datalen
)
605 struct agent_pvt
*p
= ast
->tech_pvt
;
607 ast_mutex_lock(&p
->lock
);
609 res
= ast_channel_sendhtml(p
->chan
, subclass
, data
, datalen
);
610 ast_mutex_unlock(&p
->lock
);
614 static int agent_sendtext(struct ast_channel
*ast
, const char *text
)
616 struct agent_pvt
*p
= ast
->tech_pvt
;
618 ast_mutex_lock(&p
->lock
);
620 res
= ast_sendtext(p
->chan
, text
);
621 ast_mutex_unlock(&p
->lock
);
625 static int agent_write(struct ast_channel
*ast
, struct ast_frame
*f
)
627 struct agent_pvt
*p
= ast
->tech_pvt
;
629 CHECK_FORMATS(ast
, p
);
630 ast_mutex_lock(&p
->lock
);
634 if ((f
->frametype
!= AST_FRAME_VOICE
) ||
635 (f
->frametype
!= AST_FRAME_VIDEO
) ||
636 (f
->subclass
== p
->chan
->writeformat
)) {
637 res
= ast_write(p
->chan
, f
);
639 ast_log(LOG_DEBUG
, "Dropping one incompatible %s frame on '%s' to '%s'\n",
640 f
->frametype
== AST_FRAME_VOICE
? "audio" : "video",
641 ast
->name
, p
->chan
->name
);
646 ast_mutex_unlock(&p
->lock
);
650 static int agent_fixup(struct ast_channel
*oldchan
, struct ast_channel
*newchan
)
652 struct agent_pvt
*p
= newchan
->tech_pvt
;
653 ast_mutex_lock(&p
->lock
);
654 if (p
->owner
!= oldchan
) {
655 ast_log(LOG_WARNING
, "old channel wasn't %p but was %p\n", oldchan
, p
->owner
);
656 ast_mutex_unlock(&p
->lock
);
660 ast_mutex_unlock(&p
->lock
);
664 static int agent_indicate(struct ast_channel
*ast
, int condition
, const void *data
, size_t datalen
)
666 struct agent_pvt
*p
= ast
->tech_pvt
;
668 ast_mutex_lock(&p
->lock
);
669 if (p
->chan
&& !ast_check_hangup(p
->chan
))
670 res
= p
->chan
->tech
->indicate
? p
->chan
->tech
->indicate(p
->chan
, condition
, data
, datalen
) : -1;
673 ast_mutex_unlock(&p
->lock
);
677 static int agent_digit_begin(struct ast_channel
*ast
, char digit
)
679 struct agent_pvt
*p
= ast
->tech_pvt
;
680 ast_mutex_lock(&p
->lock
);
682 ast_senddigit_begin(p
->chan
, digit
);
684 ast_mutex_unlock(&p
->lock
);
688 static int agent_digit_end(struct ast_channel
*ast
, char digit
, unsigned int duration
)
690 struct agent_pvt
*p
= ast
->tech_pvt
;
691 ast_mutex_lock(&p
->lock
);
693 ast_senddigit_end(p
->chan
, digit
, duration
);
695 ast_mutex_unlock(&p
->lock
);
699 static int agent_call(struct ast_channel
*ast
, char *dest
, int timeout
)
701 struct agent_pvt
*p
= ast
->tech_pvt
;
704 ast_mutex_lock(&p
->lock
);
708 ast_log(LOG_DEBUG
, "Pretending to dial on pending agent\n");
709 newstate
= AST_STATE_DIALING
;
712 ast_log(LOG_NOTICE
, "Whoa, they hung up between alloc and call... what are the odds of that?\n");
715 ast_mutex_unlock(&p
->lock
);
717 ast_setstate(ast
, newstate
);
719 } else if (!ast_strlen_zero(p
->loginchan
)) {
721 /* Call on this agent */
722 if (option_verbose
> 2)
723 ast_verbose(VERBOSE_PREFIX_3
"outgoing agentcall, to agent '%s', on '%s'\n", p
->agent
, p
->chan
->name
);
724 ast_set_callerid(p
->chan
,
725 ast
->cid
.cid_num
, ast
->cid
.cid_name
, NULL
);
726 ast_channel_inherit_variables(ast
, p
->chan
);
727 res
= ast_call(p
->chan
, p
->loginchan
, 0);
729 ast_mutex_unlock(&p
->lock
);
732 ast_verbose( VERBOSE_PREFIX_3
"agent_call, call to agent '%s' call on '%s'\n", p
->agent
, p
->chan
->name
);
733 if (option_debug
> 2)
734 ast_log(LOG_DEBUG
, "Playing beep, lang '%s'\n", p
->chan
->language
);
735 res
= ast_streamfile(p
->chan
, beep
, p
->chan
->language
);
736 if (option_debug
> 2)
737 ast_log(LOG_DEBUG
, "Played beep, result '%d'\n", res
);
739 res
= ast_waitstream(p
->chan
, "");
740 if (option_debug
> 2)
741 ast_log(LOG_DEBUG
, "Waited for stream, result '%d'\n", res
);
744 res
= ast_set_read_format(p
->chan
, ast_best_codec(p
->chan
->nativeformats
));
745 if (option_debug
> 2)
746 ast_log(LOG_DEBUG
, "Set read format, result '%d'\n", res
);
748 ast_log(LOG_WARNING
, "Unable to set read format to %s\n", ast_getformatname(ast_best_codec(p
->chan
->nativeformats
)));
752 p
->inherited_devicestate
= -1;
753 ast_device_state_changed("Agent/%s", p
->agent
);
757 res
= ast_set_write_format(p
->chan
, ast_best_codec(p
->chan
->nativeformats
));
758 if (option_debug
> 2)
759 ast_log(LOG_DEBUG
, "Set write format, result '%d'\n", res
);
761 ast_log(LOG_WARNING
, "Unable to set write format to %s\n", ast_getformatname(ast_best_codec(p
->chan
->nativeformats
)));
764 /* Call is immediately up, or might need ack */
766 newstate
= AST_STATE_RINGING
;
768 newstate
= AST_STATE_UP
;
769 if (recordagentcalls
)
770 agent_start_monitoring(ast
, 0);
776 ast_mutex_unlock(&p
->lock
);
778 ast_setstate(ast
, newstate
);
782 /*! \brief store/clear the global variable that stores agentid based on the callerid */
783 static void set_agentbycallerid(const char *callerid
, const char *agent
)
785 char buf
[AST_MAX_BUF
];
787 /* if there is no Caller ID, nothing to do */
788 if (ast_strlen_zero(callerid
))
791 snprintf(buf
, sizeof(buf
), "%s_%s", GETAGENTBYCALLERID
, callerid
);
792 pbx_builtin_setvar_helper(NULL
, buf
, agent
);
795 /*! \brief return the channel or base channel if one exists. This function assumes the channel it is called on is already locked */
796 struct ast_channel
* agent_get_base_channel(struct ast_channel
*chan
)
798 struct agent_pvt
*p
= NULL
;
799 struct ast_channel
*base
= chan
;
801 /* chan is locked by the calling function */
802 if (!chan
|| !chan
->tech_pvt
) {
803 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
);
812 int agent_set_base_channel(struct ast_channel
*chan
, struct ast_channel
*base
)
814 struct agent_pvt
*p
= NULL
;
816 if (!chan
|| !base
) {
817 ast_log(LOG_ERROR
, "whoa, you need a channel (0x%ld) and a base channel (0x%ld) for setting.\n", (long)chan
, (long)base
);
822 ast_log(LOG_ERROR
, "whoa, channel %s is missing his tech_pvt structure!!.\n", chan
->name
);
829 static int agent_hangup(struct ast_channel
*ast
)
831 struct agent_pvt
*p
= ast
->tech_pvt
;
834 ast_mutex_lock(&p
->lock
);
836 ast
->tech_pvt
= NULL
;
837 p
->app_sleep_cond
= 1;
840 /* if they really are hung up then set start to 0 so the test
841 * later if we're called on an already downed channel
842 * doesn't cause an agent to be logged out like when
843 * agent_request() is followed immediately by agent_hangup()
844 * as in apps/app_chanisavail.c:chanavail_exec()
848 ast_log(LOG_DEBUG
, "Hangup called for state %s\n", ast_state2str(ast
->_state
));
849 if (p
->start
&& (ast
->_state
!= AST_STATE_UP
)) {
850 howlong
= time(NULL
) - p
->start
;
852 } else if (ast
->_state
== AST_STATE_RESERVED
)
857 p
->chan
->_bridge
= NULL
;
858 /* If they're dead, go ahead and hang up on the agent now */
859 if (!ast_strlen_zero(p
->loginchan
)) {
860 /* Store last disconnect time */
862 p
->lastdisc
= ast_tvadd(ast_tvnow(), ast_samp2tv(p
->wrapuptime
, 1000));
864 p
->lastdisc
= ast_tv(0,0);
866 status
= pbx_builtin_getvar_helper(p
->chan
, "CHANLOCALSTATUS");
867 if (autologoffunavail
&& status
&& !strcasecmp(status
, "CHANUNAVAIL")) {
868 long logintime
= time(NULL
) - p
->loginstart
;
870 ast_log(LOG_NOTICE
, "Agent hangup: '%s' is not available now, auto logoff\n", p
->name
);
871 agent_logoff_maintenance(p
, p
->loginchan
, logintime
, ast
->uniqueid
, "Chanunavail");
873 /* Recognize the hangup and pass it along immediately */
876 p
->inherited_devicestate
= -1;
877 ast_device_state_changed("Agent/%s", p
->agent
);
879 ast_log(LOG_DEBUG
, "Hungup, howlong is %d, autologoff is %d\n", howlong
, p
->autologoff
);
880 if ((p
->deferlogoff
) || (howlong
&& p
->autologoff
&& (howlong
> p
->autologoff
))) {
881 long logintime
= time(NULL
) - p
->loginstart
;
884 ast_log(LOG_NOTICE
, "Agent '%s' didn't answer/confirm within %d seconds (waited %d)\n", p
->name
, p
->autologoff
, howlong
);
886 agent_logoff_maintenance(p
, p
->loginchan
, logintime
, ast
->uniqueid
, "Autologoff");
887 if (persistent_agents
)
890 } else if (p
->dead
) {
891 ast_channel_lock(p
->chan
);
892 ast_softhangup(p
->chan
, AST_SOFTHANGUP_EXPLICIT
);
893 ast_channel_unlock(p
->chan
);
894 } else if (p
->loginstart
) {
895 ast_channel_lock(p
->chan
);
896 ast_indicate_data(p
->chan
, AST_CONTROL_HOLD
,
898 !ast_strlen_zero(p
->moh
) ? strlen(p
->moh
) + 1 : 0);
899 ast_channel_unlock(p
->chan
);
902 ast_mutex_unlock(&p
->lock
);
904 /* Only register a device state change if the agent is still logged in */
905 if (!p
->loginstart
) {
906 p
->loginchan
[0] = '\0';
907 p
->logincallerid
[0] = '\0';
908 if (persistent_agents
)
911 ast_device_state_changed("Agent/%s", p
->agent
);
915 AST_LIST_LOCK(&agents
);
916 AST_LIST_REMOVE(&agents
, p
, list
);
917 AST_LIST_UNLOCK(&agents
);
919 if (p
->abouttograb
) {
920 /* Let the "about to grab" thread know this isn't valid anymore, and let it
923 } else if (p
->dead
) {
924 ast_mutex_destroy(&p
->lock
);
925 ast_mutex_destroy(&p
->app_lock
);
929 /* Not dead -- check availability now */
930 ast_mutex_lock(&p
->lock
);
931 /* Store last disconnect time */
932 p
->lastdisc
= ast_tvadd(ast_tvnow(), ast_samp2tv(p
->wrapuptime
, 1000));
933 ast_mutex_unlock(&p
->lock
);
935 /* Release ownership of the agent to other threads (presumably running the login app). */
936 if (ast_strlen_zero(p
->loginchan
))
937 ast_mutex_unlock(&p
->app_lock
);
942 static int agent_cont_sleep( void *data
)
947 p
= (struct agent_pvt
*)data
;
949 ast_mutex_lock(&p
->lock
);
950 res
= p
->app_sleep_cond
;
951 if (p
->lastdisc
.tv_sec
) {
952 if (ast_tvdiff_ms(ast_tvnow(), p
->lastdisc
) > 0)
955 ast_mutex_unlock(&p
->lock
);
957 if(option_debug
> 4 && !res
)
958 ast_log(LOG_DEBUG
, "agent_cont_sleep() returning %d\n", res
);
963 static int agent_ack_sleep(void *data
)
970 /* Wait a second and look for something */
972 p
= (struct agent_pvt
*) data
;
977 to
= ast_waitfor(p
->chan
, to
);
982 f
= ast_read(p
->chan
);
985 if (f
->frametype
== AST_FRAME_DTMF
)
990 ast_mutex_lock(&p
->lock
);
991 if (!p
->app_sleep_cond
) {
992 ast_mutex_unlock(&p
->lock
);
994 } else if (res
== '#') {
995 ast_mutex_unlock(&p
->lock
);
998 ast_mutex_unlock(&p
->lock
);
1004 static struct ast_channel
*agent_bridgedchannel(struct ast_channel
*chan
, struct ast_channel
*bridge
)
1006 struct agent_pvt
*p
= bridge
->tech_pvt
;
1007 struct ast_channel
*ret
= NULL
;
1010 if (chan
== p
->chan
)
1011 ret
= bridge
->_bridge
;
1012 else if (chan
== bridge
->_bridge
)
1017 ast_log(LOG_DEBUG
, "Asked for bridged channel on '%s'/'%s', returning '%s'\n", chan
->name
, bridge
->name
, ret
? ret
->name
: "<none>");
1021 /*! \brief Create new agent channel */
1022 static struct ast_channel
*agent_new(struct agent_pvt
*p
, int state
)
1024 struct ast_channel
*tmp
;
1027 ast_log(LOG_WARNING
, "No channel? :(\n");
1032 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);
1034 tmp
= ast_channel_alloc(0, state
, 0, 0, "", p
->chan
? p
->chan
->exten
:"", p
->chan
? p
->chan
->context
:"", 0, "Agent/%s", p
->agent
);
1036 ast_log(LOG_WARNING
, "Unable to allocate agent channel structure\n");
1040 tmp
->tech
= &agent_tech
;
1042 tmp
->nativeformats
= p
->chan
->nativeformats
;
1043 tmp
->writeformat
= p
->chan
->writeformat
;
1044 tmp
->rawwriteformat
= p
->chan
->writeformat
;
1045 tmp
->readformat
= p
->chan
->readformat
;
1046 tmp
->rawreadformat
= p
->chan
->readformat
;
1047 ast_string_field_set(tmp
, language
, p
->chan
->language
);
1048 ast_copy_string(tmp
->context
, p
->chan
->context
, sizeof(tmp
->context
));
1049 ast_copy_string(tmp
->exten
, p
->chan
->exten
, sizeof(tmp
->exten
));
1050 /* XXX Is this really all we copy form the originating channel?? */
1052 tmp
->nativeformats
= AST_FORMAT_SLINEAR
;
1053 tmp
->writeformat
= AST_FORMAT_SLINEAR
;
1054 tmp
->rawwriteformat
= AST_FORMAT_SLINEAR
;
1055 tmp
->readformat
= AST_FORMAT_SLINEAR
;
1056 tmp
->rawreadformat
= AST_FORMAT_SLINEAR
;
1058 /* Safe, agentlock already held */
1061 /* XXX: this needs fixing */
1063 ast_atomic_fetchadd_int(&__mod_desc
->usecnt
, +1);
1065 ast_update_use_count();
1067 /* Wake up and wait for other applications (by definition the login app)
1068 * to release this channel). Takes ownership of the agent channel
1069 * to this thread only.
1070 * For signalling the other thread, ast_queue_frame is used until we
1071 * can safely use signals for this purpose. The pselect() needs to be
1072 * implemented in the kernel for this.
1074 p
->app_sleep_cond
= 0;
1075 if(ast_strlen_zero(p
->loginchan
) && ast_mutex_trylock(&p
->app_lock
)) {
1077 ast_queue_frame(p
->chan
, &ast_null_frame
);
1078 ast_mutex_unlock(&p
->lock
); /* For other thread to read the condition. */
1079 ast_mutex_lock(&p
->app_lock
);
1080 ast_mutex_lock(&p
->lock
);
1082 ast_log(LOG_WARNING
, "Agent disconnected while we were connecting the call\n");
1084 tmp
->tech_pvt
= NULL
;
1085 p
->app_sleep_cond
= 1;
1086 ast_channel_free( tmp
);
1087 ast_mutex_unlock(&p
->lock
); /* For other thread to read the condition. */
1088 ast_mutex_unlock(&p
->app_lock
);
1091 } else if (!ast_strlen_zero(p
->loginchan
)) {
1093 ast_queue_frame(p
->chan
, &ast_null_frame
);
1095 ast_log(LOG_WARNING
, "Agent disconnected while we were connecting the call\n");
1097 tmp
->tech_pvt
= NULL
;
1098 p
->app_sleep_cond
= 1;
1099 ast_channel_free( tmp
);
1100 ast_mutex_unlock(&p
->lock
); /* For other thread to read the condition. */
1105 ast_indicate(p
->chan
, AST_CONTROL_UNHOLD
);
1106 p
->owning_app
= pthread_self();
1107 /* After the above step, there should not be any blockers. */
1109 if (ast_test_flag(p
->chan
, AST_FLAG_BLOCKING
)) {
1110 ast_log( LOG_ERROR
, "A blocker exists after agent channel ownership acquired\n" );
1111 ast_assert(ast_test_flag(p
->chan
, AST_FLAG_BLOCKING
) == 0);
1119 * Read configuration data. The file named agents.conf.
1121 * \returns Always 0, or so it seems.
1123 static int read_agent_config(void)
1125 struct ast_config
*cfg
;
1126 struct ast_config
*ucfg
;
1127 struct ast_variable
*v
;
1128 struct agent_pvt
*p
;
1129 const char *general_val
;
1130 const char *catname
;
1131 const char *hasagent
;
1139 cfg
= ast_config_load(config
);
1141 ast_log(LOG_NOTICE
, "No agent configuration found -- agent support disabled\n");
1144 AST_LIST_LOCK(&agents
);
1145 AST_LIST_TRAVERSE(&agents
, p
, list
) {
1148 strcpy(moh
, "default");
1149 /* set the default recording values */
1150 recordagentcalls
= 0;
1151 strcpy(recordformat
, "wav");
1152 strcpy(recordformatext
, "wav");
1153 urlprefix
[0] = '\0';
1154 savecallsin
[0] = '\0';
1156 /* Read in [general] section for persistence */
1157 if ((general_val
= ast_variable_retrieve(cfg
, "general", "persistentagents")))
1158 persistent_agents
= ast_true(general_val
);
1159 multiplelogin
= ast_true(ast_variable_retrieve(cfg
, "general", "multiplelogin"));
1161 /* Read in the [agents] section */
1162 v
= ast_variable_browse(cfg
, "agents");
1164 /* Create the interface list */
1165 if (!strcasecmp(v
->name
, "agent")) {
1166 add_agent(v
->value
, 0);
1167 } else if (!strcasecmp(v
->name
, "group")) {
1168 group
= ast_get_group(v
->value
);
1169 } else if (!strcasecmp(v
->name
, "autologoff")) {
1170 autologoff
= atoi(v
->value
);
1173 } else if (!strcasecmp(v
->name
, "ackcall")) {
1174 if (!strcasecmp(v
->value
, "always"))
1176 else if (ast_true(v
->value
))
1180 } else if (!strcasecmp(v
->name
, "endcall")) {
1181 endcall
= ast_true(v
->value
);
1182 } else if (!strcasecmp(v
->name
, "wrapuptime")) {
1183 wrapuptime
= atoi(v
->value
);
1186 } else if (!strcasecmp(v
->name
, "maxlogintries") && !ast_strlen_zero(v
->value
)) {
1187 maxlogintries
= atoi(v
->value
);
1188 if (maxlogintries
< 0)
1190 } else if (!strcasecmp(v
->name
, "goodbye") && !ast_strlen_zero(v
->value
)) {
1191 strcpy(agentgoodbye
,v
->value
);
1192 } else if (!strcasecmp(v
->name
, "musiconhold")) {
1193 ast_copy_string(moh
, v
->value
, sizeof(moh
));
1194 } else if (!strcasecmp(v
->name
, "updatecdr")) {
1195 if (ast_true(v
->value
))
1199 } else if (!strcasecmp(v
->name
, "autologoffunavail")) {
1200 if (ast_true(v
->value
))
1201 autologoffunavail
= 1;
1203 autologoffunavail
= 0;
1204 } else if (!strcasecmp(v
->name
, "recordagentcalls")) {
1205 recordagentcalls
= ast_true(v
->value
);
1206 } else if (!strcasecmp(v
->name
, "recordformat")) {
1207 ast_copy_string(recordformat
, v
->value
, sizeof(recordformat
));
1208 if (!strcasecmp(v
->value
, "wav49"))
1209 strcpy(recordformatext
, "WAV");
1211 ast_copy_string(recordformatext
, v
->value
, sizeof(recordformatext
));
1212 } else if (!strcasecmp(v
->name
, "urlprefix")) {
1213 ast_copy_string(urlprefix
, v
->value
, sizeof(urlprefix
));
1214 if (urlprefix
[strlen(urlprefix
) - 1] != '/')
1215 strncat(urlprefix
, "/", sizeof(urlprefix
) - strlen(urlprefix
) - 1);
1216 } else if (!strcasecmp(v
->name
, "savecallsin")) {
1217 if (v
->value
[0] == '/')
1218 ast_copy_string(savecallsin
, v
->value
, sizeof(savecallsin
));
1220 snprintf(savecallsin
, sizeof(savecallsin
) - 2, "/%s", v
->value
);
1221 if (savecallsin
[strlen(savecallsin
) - 1] != '/')
1222 strncat(savecallsin
, "/", sizeof(savecallsin
) - strlen(savecallsin
) - 1);
1223 } else if (!strcasecmp(v
->name
, "custom_beep")) {
1224 ast_copy_string(beep
, v
->value
, sizeof(beep
));
1228 if ((ucfg
= ast_config_load("users.conf"))) {
1229 genhasagent
= ast_true(ast_variable_retrieve(ucfg
, "general", "hasagent"));
1230 catname
= ast_category_browse(ucfg
, NULL
);
1232 if (strcasecmp(catname
, "general")) {
1233 hasagent
= ast_variable_retrieve(ucfg
, catname
, "hasagent");
1234 if (ast_true(hasagent
) || (!hasagent
&& genhasagent
)) {
1236 const char *fullname
= ast_variable_retrieve(ucfg
, catname
, "fullname");
1237 const char *secret
= ast_variable_retrieve(ucfg
, catname
, "secret");
1242 snprintf(tmp
, sizeof(tmp
), "%s,%s,%s", catname
, secret
,fullname
);
1246 catname
= ast_category_browse(ucfg
, catname
);
1248 ast_config_destroy(ucfg
);
1250 AST_LIST_TRAVERSE_SAFE_BEGIN(&agents
, p
, list
) {
1252 AST_LIST_REMOVE_CURRENT(&agents
, list
);
1253 /* Destroy if appropriate */
1256 ast_mutex_destroy(&p
->lock
);
1257 ast_mutex_destroy(&p
->app_lock
);
1260 /* Cause them to hang up */
1261 ast_softhangup(p
->chan
, AST_SOFTHANGUP_EXPLICIT
);
1266 AST_LIST_TRAVERSE_SAFE_END
1267 AST_LIST_UNLOCK(&agents
);
1268 ast_config_destroy(cfg
);
1272 static int check_availability(struct agent_pvt
*newlyavailable
, int needlock
)
1274 struct ast_channel
*chan
=NULL
, *parent
=NULL
;
1275 struct agent_pvt
*p
;
1279 ast_log(LOG_DEBUG
, "Checking availability of '%s'\n", newlyavailable
->agent
);
1281 AST_LIST_LOCK(&agents
);
1282 AST_LIST_TRAVERSE(&agents
, p
, list
) {
1283 if (p
== newlyavailable
) {
1286 ast_mutex_lock(&p
->lock
);
1287 if (!p
->abouttograb
&& p
->pending
&& ((p
->group
&& (newlyavailable
->group
& p
->group
)) || !strcmp(p
->agent
, newlyavailable
->agent
))) {
1289 ast_log(LOG_DEBUG
, "Call '%s' looks like a winner for agent '%s'\n", p
->owner
->name
, newlyavailable
->agent
);
1290 /* We found a pending call, time to merge */
1291 chan
= agent_new(newlyavailable
, AST_STATE_DOWN
);
1294 ast_mutex_unlock(&p
->lock
);
1297 ast_mutex_unlock(&p
->lock
);
1300 AST_LIST_UNLOCK(&agents
);
1301 if (parent
&& chan
) {
1302 if (newlyavailable
->ackcall
> 1) {
1303 /* Don't do beep here */
1306 if (option_debug
> 2)
1307 ast_log( LOG_DEBUG
, "Playing beep, lang '%s'\n", newlyavailable
->chan
->language
);
1308 res
= ast_streamfile(newlyavailable
->chan
, beep
, newlyavailable
->chan
->language
);
1309 if (option_debug
> 2)
1310 ast_log( LOG_DEBUG
, "Played beep, result '%d'\n", res
);
1312 res
= ast_waitstream(newlyavailable
->chan
, "");
1313 ast_log( LOG_DEBUG
, "Waited for stream, result '%d'\n", res
);
1317 /* Note -- parent may have disappeared */
1318 if (p
->abouttograb
) {
1319 newlyavailable
->acknowledged
= 1;
1320 /* Safe -- agent lock already held */
1321 ast_setstate(parent
, AST_STATE_UP
);
1322 ast_setstate(chan
, AST_STATE_UP
);
1323 ast_copy_string(parent
->context
, chan
->context
, sizeof(parent
->context
));
1324 /* Go ahead and mark the channel as a zombie so that masquerade will
1325 destroy it for us, and we need not call ast_hangup */
1326 ast_mutex_lock(&parent
->lock
);
1327 ast_set_flag(chan
, AST_FLAG_ZOMBIE
);
1328 ast_channel_masquerade(parent
, chan
);
1329 ast_mutex_unlock(&parent
->lock
);
1333 ast_log(LOG_DEBUG
, "Sneaky, parent disappeared in the mean time...\n");
1334 agent_cleanup(newlyavailable
);
1338 ast_log(LOG_DEBUG
, "Ugh... Agent hung up at exactly the wrong time\n");
1339 agent_cleanup(newlyavailable
);
1345 static int check_beep(struct agent_pvt
*newlyavailable
, int needlock
)
1347 struct agent_pvt
*p
;
1350 ast_log(LOG_DEBUG
, "Checking beep availability of '%s'\n", newlyavailable
->agent
);
1352 AST_LIST_LOCK(&agents
);
1353 AST_LIST_TRAVERSE(&agents
, p
, list
) {
1354 if (p
== newlyavailable
) {
1357 ast_mutex_lock(&p
->lock
);
1358 if (!p
->abouttograb
&& p
->pending
&& ((p
->group
&& (newlyavailable
->group
& p
->group
)) || !strcmp(p
->agent
, newlyavailable
->agent
))) {
1360 ast_log(LOG_DEBUG
, "Call '%s' looks like a would-be winner for agent '%s'\n", p
->owner
->name
, newlyavailable
->agent
);
1361 ast_mutex_unlock(&p
->lock
);
1364 ast_mutex_unlock(&p
->lock
);
1367 AST_LIST_UNLOCK(&agents
);
1369 ast_mutex_unlock(&newlyavailable
->lock
);
1370 if (option_debug
> 2)
1371 ast_log( LOG_DEBUG
, "Playing beep, lang '%s'\n", newlyavailable
->chan
->language
);
1372 res
= ast_streamfile(newlyavailable
->chan
, beep
, newlyavailable
->chan
->language
);
1373 if (option_debug
> 2)
1374 ast_log( LOG_DEBUG
, "Played beep, result '%d'\n", res
);
1376 res
= ast_waitstream(newlyavailable
->chan
, "");
1378 ast_log( LOG_DEBUG
, "Waited for stream, result '%d'\n", res
);
1380 ast_mutex_lock(&newlyavailable
->lock
);
1385 /* return 1 if multiple login is fine, 0 if it is not and we find a match, -1 if multiplelogin is not allowed and we don't find a match. */
1386 static int allow_multiple_login(char *chan
, char *context
)
1388 struct agent_pvt
*p
;
1396 snprintf(loginchan
, sizeof(loginchan
), "%s@%s", chan
, S_OR(context
, "default"));
1398 AST_LIST_TRAVERSE(&agents
, p
, list
) {
1399 if(!strcasecmp(chan
, p
->loginchan
))
1405 /*! \brief Part of the Asterisk PBX interface */
1406 static struct ast_channel
*agent_request(const char *type
, int format
, void *data
, int *cause
)
1408 struct agent_pvt
*p
;
1409 struct ast_channel
*chan
= NULL
;
1411 ast_group_t groupmatch
;
1418 if ((s
[0] == '@') && (sscanf(s
+ 1, "%d", &groupoff
) == 1)) {
1419 groupmatch
= (1 << groupoff
);
1420 } else if ((s
[0] == ':') && (sscanf(s
+ 1, "%d", &groupoff
) == 1)) {
1421 groupmatch
= (1 << groupoff
);
1426 /* Check actual logged in agents first */
1427 AST_LIST_LOCK(&agents
);
1428 AST_LIST_TRAVERSE(&agents
, p
, list
) {
1429 ast_mutex_lock(&p
->lock
);
1430 if (!p
->pending
&& ((groupmatch
&& (p
->group
& groupmatch
)) || !strcmp(data
, p
->agent
)) &&
1431 ast_strlen_zero(p
->loginchan
)) {
1435 if (!p
->lastdisc
.tv_sec
|| (tv
.tv_sec
>= p
->lastdisc
.tv_sec
)) {
1436 p
->lastdisc
= ast_tv(0, 0);
1437 /* Agent must be registered, but not have any active call, and not be in a waiting state */
1438 if (!p
->owner
&& p
->chan
) {
1440 chan
= agent_new(p
, AST_STATE_DOWN
);
1443 ast_mutex_unlock(&p
->lock
);
1448 ast_mutex_unlock(&p
->lock
);
1451 AST_LIST_TRAVERSE(&agents
, p
, list
) {
1452 ast_mutex_lock(&p
->lock
);
1453 if (!p
->pending
&& ((groupmatch
&& (p
->group
& groupmatch
)) || !strcmp(data
, p
->agent
))) {
1454 if (p
->chan
|| !ast_strlen_zero(p
->loginchan
))
1458 ast_log(LOG_NOTICE
, "Time now: %ld, Time of lastdisc: %ld\n", tv
.tv_sec
, p
->lastdisc
.tv_sec
);
1460 if (!p
->lastdisc
.tv_sec
|| (tv
.tv_sec
>= p
->lastdisc
.tv_sec
)) {
1461 p
->lastdisc
= ast_tv(0, 0);
1462 /* Agent must be registered, but not have any active call, and not be in a waiting state */
1463 if (!p
->owner
&& p
->chan
) {
1464 /* Could still get a fixed agent */
1465 chan
= agent_new(p
, AST_STATE_DOWN
);
1466 } else if (!p
->owner
&& !ast_strlen_zero(p
->loginchan
)) {
1467 /* Adjustable agent */
1468 p
->chan
= ast_request("Local", format
, p
->loginchan
, cause
);
1470 chan
= agent_new(p
, AST_STATE_DOWN
);
1473 ast_mutex_unlock(&p
->lock
);
1478 ast_mutex_unlock(&p
->lock
);
1482 if (!chan
&& waitforagent
) {
1483 /* No agent available -- but we're requesting to wait for one.
1484 Allocate a place holder */
1487 ast_log(LOG_DEBUG
, "Creating place holder for '%s'\n", s
);
1488 p
= add_agent(data
, 1);
1489 p
->group
= groupmatch
;
1490 chan
= agent_new(p
, AST_STATE_DOWN
);
1492 ast_log(LOG_WARNING
, "Weird... Fix this to drop the unused pending agent\n");
1494 ast_log(LOG_DEBUG
, "Not creating place holder for '%s' since nobody logged in\n", s
);
1496 *cause
= hasagent
? AST_CAUSE_BUSY
: AST_CAUSE_UNREGISTERED
;
1497 AST_LIST_UNLOCK(&agents
);
1501 static force_inline
int powerof(unsigned int d
)
1512 * Lists agents and their status to the Manager API.
1513 * It is registered on load_module() and it gets called by the manager backend.
1517 * \sa action_agent_logoff(), action_agent_callback_login(), load_module().
1519 static int action_agents(struct mansession
*s
, const struct message
*m
)
1521 const char *id
= astman_get_header(m
,"ActionID");
1522 char idText
[256] = "";
1524 struct agent_pvt
*p
;
1525 char *username
= NULL
;
1526 char *loginChan
= NULL
;
1527 char *talkingtoChan
= NULL
;
1528 char *status
= NULL
;
1530 if (!ast_strlen_zero(id
))
1531 snprintf(idText
, sizeof(idText
) ,"ActionID: %s\r\n", id
);
1532 astman_send_ack(s
, m
, "Agents will follow");
1533 AST_LIST_LOCK(&agents
);
1534 AST_LIST_TRAVERSE(&agents
, p
, list
) {
1535 ast_mutex_lock(&p
->lock
);
1538 AGENT_LOGGEDOFF - Agent isn't logged in
1539 AGENT_IDLE - Agent is logged in, and waiting for call
1540 AGENT_ONCALL - Agent is logged in, and on a call
1541 AGENT_UNKNOWN - Don't know anything about agent. Shouldn't ever get this. */
1543 username
= S_OR(p
->name
, "None");
1545 /* Set a default status. It 'should' get changed. */
1546 status
= "AGENT_UNKNOWN";
1548 if (!ast_strlen_zero(p
->loginchan
) && !p
->chan
) {
1549 loginChan
= p
->loginchan
;
1550 talkingtoChan
= "n/a";
1551 status
= "AGENT_IDLE";
1552 if (p
->acknowledged
) {
1553 snprintf(chanbuf
, sizeof(chanbuf
), " %s (Confirmed)", p
->loginchan
);
1554 loginChan
= chanbuf
;
1556 } else if (p
->chan
) {
1557 loginChan
= ast_strdupa(p
->chan
->name
);
1558 if (p
->owner
&& p
->owner
->_bridge
) {
1559 if (ast_bridged_channel(p
->owner
)) {
1560 talkingtoChan
= ast_strdupa(S_OR(ast_bridged_channel(p
->owner
)->cid
.cid_num
, ""));
1562 talkingtoChan
= "n/a";
1564 status
= "AGENT_ONCALL";
1566 talkingtoChan
= "n/a";
1567 status
= "AGENT_IDLE";
1571 talkingtoChan
= "n/a";
1572 status
= "AGENT_LOGGEDOFF";
1575 astman_append(s
, "Event: Agents\r\n"
1579 "LoggedInChan: %s\r\n"
1580 "LoggedInTime: %d\r\n"
1584 p
->agent
, username
, status
, loginChan
, (int)p
->loginstart
, talkingtoChan
, idText
);
1585 ast_mutex_unlock(&p
->lock
);
1587 AST_LIST_UNLOCK(&agents
);
1588 astman_append(s
, "Event: AgentsComplete\r\n"
1594 static void agent_logoff_maintenance(struct agent_pvt
*p
, char *loginchan
, long logintime
, const char *uniqueid
, char *logcommand
)
1597 char agent
[AST_MAX_AGENT
];
1599 if (!ast_strlen_zero(logcommand
))
1602 tmp
= ast_strdupa("");
1604 snprintf(agent
, sizeof(agent
), "Agent/%s", p
->agent
);
1606 if (!ast_strlen_zero(uniqueid
)) {
1607 manager_event(EVENT_FLAG_AGENT
, "Agentcallbacklogoff",
1611 "Logintime: %ld\r\n"
1613 p
->agent
, tmp
, loginchan
, logintime
, uniqueid
);
1615 manager_event(EVENT_FLAG_AGENT
, "Agentcallbacklogoff",
1619 "Logintime: %ld\r\n",
1620 p
->agent
, tmp
, loginchan
, logintime
);
1623 ast_queue_log("NONE", ast_strlen_zero(uniqueid
) ? "NONE" : uniqueid
, agent
, "AGENTCALLBACKLOGOFF", "%s|%ld|%s", loginchan
, logintime
, tmp
);
1624 set_agentbycallerid(p
->logincallerid
, NULL
);
1625 p
->loginchan
[0] ='\0';
1626 p
->logincallerid
[0] = '\0';
1627 p
->inherited_devicestate
= -1;
1628 ast_device_state_changed("Agent/%s", p
->agent
);
1629 if (persistent_agents
)
1634 static int agent_logoff(const char *agent
, int soft
)
1636 struct agent_pvt
*p
;
1638 int ret
= -1; /* Return -1 if no agent if found */
1640 AST_LIST_LOCK(&agents
);
1641 AST_LIST_TRAVERSE(&agents
, p
, list
) {
1642 if (!strcasecmp(p
->agent
, agent
)) {
1644 if (p
->owner
|| p
->chan
) {
1646 ast_mutex_lock(&p
->lock
);
1648 while (p
->owner
&& ast_channel_trylock(p
->owner
)) {
1649 DEADLOCK_AVOIDANCE(&p
->lock
);
1652 ast_softhangup(p
->owner
, AST_SOFTHANGUP_EXPLICIT
);
1653 ast_channel_unlock(p
->owner
);
1656 while (p
->chan
&& ast_channel_trylock(p
->chan
)) {
1657 DEADLOCK_AVOIDANCE(&p
->lock
);
1660 ast_softhangup(p
->chan
, AST_SOFTHANGUP_EXPLICIT
);
1661 ast_channel_unlock(p
->chan
);
1664 ast_mutex_unlock(&p
->lock
);
1668 logintime
= time(NULL
) - p
->loginstart
;
1670 agent_logoff_maintenance(p
, p
->loginchan
, logintime
, NULL
, "CommandLogoff");
1675 AST_LIST_UNLOCK(&agents
);
1680 static int agent_logoff_cmd(int fd
, int argc
, char **argv
)
1685 if (argc
< 3 || argc
> 4)
1686 return RESULT_SHOWUSAGE
;
1687 if (argc
== 4 && strcasecmp(argv
[3], "soft"))
1688 return RESULT_SHOWUSAGE
;
1690 agent
= argv
[2] + 6;
1691 ret
= agent_logoff(agent
, argc
== 4);
1693 ast_cli(fd
, "Logging out %s\n", agent
);
1695 return RESULT_SUCCESS
;
1699 * Sets an agent as no longer logged in in the Manager API.
1700 * It is registered on load_module() and it gets called by the manager backend.
1704 * \sa action_agents(), action_agent_callback_login(), load_module().
1706 static int action_agent_logoff(struct mansession
*s
, const struct message
*m
)
1708 const char *agent
= astman_get_header(m
, "Agent");
1709 const char *soft_s
= astman_get_header(m
, "Soft"); /* "true" is don't hangup */
1711 int ret
; /* return value of agent_logoff */
1713 if (ast_strlen_zero(agent
)) {
1714 astman_send_error(s
, m
, "No agent specified");
1718 soft
= ast_true(soft_s
) ? 1 : 0;
1719 ret
= agent_logoff(agent
, soft
);
1721 astman_send_ack(s
, m
, "Agent logged out");
1723 astman_send_error(s
, m
, "No such agent");
1728 static char *complete_agent_logoff_cmd(const char *line
, const char *word
, int pos
, int state
)
1733 struct agent_pvt
*p
;
1734 char name
[AST_MAX_AGENT
];
1735 int which
= 0, len
= strlen(word
);
1737 AST_LIST_LOCK(&agents
);
1738 AST_LIST_TRAVERSE(&agents
, p
, list
) {
1739 snprintf(name
, sizeof(name
), "Agent/%s", p
->agent
);
1740 if (!strncasecmp(word
, name
, len
) && ++which
> state
) {
1741 ret
= ast_strdup(name
);
1745 AST_LIST_UNLOCK(&agents
);
1746 } else if (pos
== 3 && state
== 0)
1747 return ast_strdup("soft");
1753 * Show agents in cli.
1755 static int agents_show(int fd
, int argc
, char **argv
)
1757 struct agent_pvt
*p
;
1758 char username
[AST_MAX_BUF
];
1759 char location
[AST_MAX_BUF
] = "";
1760 char talkingto
[AST_MAX_BUF
] = "";
1761 char moh
[AST_MAX_BUF
];
1762 int count_agents
= 0; /*!< Number of agents configured */
1763 int online_agents
= 0; /*!< Number of online agents */
1764 int offline_agents
= 0; /*!< Number of offline agents */
1766 return RESULT_SHOWUSAGE
;
1767 AST_LIST_LOCK(&agents
);
1768 AST_LIST_TRAVERSE(&agents
, p
, list
) {
1769 ast_mutex_lock(&p
->lock
);
1772 ast_cli(fd
, "-- Pending call to group %d\n", powerof(p
->group
));
1774 ast_cli(fd
, "-- Pending call to agent %s\n", p
->agent
);
1776 if (!ast_strlen_zero(p
->name
))
1777 snprintf(username
, sizeof(username
), "(%s) ", p
->name
);
1781 snprintf(location
, sizeof(location
), "logged in on %s", p
->chan
->name
);
1782 if (p
->owner
&& ast_bridged_channel(p
->owner
))
1783 snprintf(talkingto
, sizeof(talkingto
), " talking to %s", ast_bridged_channel(p
->owner
)->name
);
1785 strcpy(talkingto
, " is idle");
1787 } else if (!ast_strlen_zero(p
->loginchan
)) {
1788 if (ast_tvdiff_ms(ast_tvnow(), p
->lastdisc
) > 0 || !(p
->lastdisc
.tv_sec
))
1789 snprintf(location
, sizeof(location
) - 20, "available at '%s'", p
->loginchan
);
1791 snprintf(location
, sizeof(location
) - 20, "wrapping up at '%s'", p
->loginchan
);
1792 talkingto
[0] = '\0';
1794 if (p
->acknowledged
)
1795 strncat(location
, " (Confirmed)", sizeof(location
) - strlen(location
) - 1);
1797 strcpy(location
, "not logged in");
1798 talkingto
[0] = '\0';
1801 if (!ast_strlen_zero(p
->moh
))
1802 snprintf(moh
, sizeof(moh
), " (musiconhold is '%s')", p
->moh
);
1803 ast_cli(fd
, "%-12.12s %s%s%s%s\n", p
->agent
,
1804 username
, location
, talkingto
, moh
);
1807 ast_mutex_unlock(&p
->lock
);
1809 AST_LIST_UNLOCK(&agents
);
1810 if ( !count_agents
)
1811 ast_cli(fd
, "No Agents are configured in %s\n",config
);
1813 ast_cli(fd
, "%d agents configured [%d online , %d offline]\n",count_agents
, online_agents
, offline_agents
);
1816 return RESULT_SUCCESS
;
1820 static int agents_show_online(int fd
, int argc
, char **argv
)
1822 struct agent_pvt
*p
;
1823 char username
[AST_MAX_BUF
];
1824 char location
[AST_MAX_BUF
] = "";
1825 char talkingto
[AST_MAX_BUF
] = "";
1826 char moh
[AST_MAX_BUF
];
1827 int count_agents
= 0; /* Number of agents configured */
1828 int online_agents
= 0; /* Number of online agents */
1829 int agent_status
= 0; /* 0 means offline, 1 means online */
1831 return RESULT_SHOWUSAGE
;
1832 AST_LIST_LOCK(&agents
);
1833 AST_LIST_TRAVERSE(&agents
, p
, list
) {
1834 agent_status
= 0; /* reset it to offline */
1835 ast_mutex_lock(&p
->lock
);
1836 if (!ast_strlen_zero(p
->name
))
1837 snprintf(username
, sizeof(username
), "(%s) ", p
->name
);
1841 snprintf(location
, sizeof(location
), "logged in on %s", p
->chan
->name
);
1842 if (p
->owner
&& ast_bridged_channel(p
->owner
))
1843 snprintf(talkingto
, sizeof(talkingto
), " talking to %s", ast_bridged_channel(p
->owner
)->name
);
1845 strcpy(talkingto
, " is idle");
1848 } else if (!ast_strlen_zero(p
->loginchan
)) {
1849 snprintf(location
, sizeof(location
) - 20, "available at '%s'", p
->loginchan
);
1850 talkingto
[0] = '\0';
1853 if (p
->acknowledged
)
1854 strncat(location
, " (Confirmed)", sizeof(location
) - strlen(location
) - 1);
1856 if (!ast_strlen_zero(p
->moh
))
1857 snprintf(moh
, sizeof(moh
), " (musiconhold is '%s')", p
->moh
);
1859 ast_cli(fd
, "%-12.12s %s%s%s%s\n", p
->agent
, username
, location
, talkingto
, moh
);
1861 ast_mutex_unlock(&p
->lock
);
1863 AST_LIST_UNLOCK(&agents
);
1865 ast_cli(fd
, "No Agents are configured in %s\n", config
);
1867 ast_cli(fd
, "%d agents online\n", online_agents
);
1869 return RESULT_SUCCESS
;
1874 static char show_agents_usage
[] =
1875 "Usage: agent show\n"
1876 " Provides summary information on agents.\n";
1878 static char show_agents_online_usage
[] =
1879 "Usage: agent show online\n"
1880 " Provides a list of all online agents.\n";
1882 static char agent_logoff_usage
[] =
1883 "Usage: agent logoff <channel> [soft]\n"
1884 " Sets an agent as no longer logged in.\n"
1885 " If 'soft' is specified, do not hangup existing calls.\n";
1887 static struct ast_cli_entry cli_show_agents_deprecated
= {
1888 { "show", "agents", NULL
},
1892 static struct ast_cli_entry cli_show_agents_online_deprecated
= {
1893 { "show", "agents", "online" },
1894 agents_show_online
, NULL
,
1897 static struct ast_cli_entry cli_agents
[] = {
1898 { { "agent", "show", NULL
},
1899 agents_show
, "Show status of agents",
1900 show_agents_usage
, NULL
, &cli_show_agents_deprecated
},
1902 { { "agent", "show", "online" },
1903 agents_show_online
, "Show all online agents",
1904 show_agents_online_usage
, NULL
, &cli_show_agents_online_deprecated
},
1906 { { "agent", "logoff", NULL
},
1907 agent_logoff_cmd
, "Sets an agent offline",
1908 agent_logoff_usage
, complete_agent_logoff_cmd
},
1912 * \brief Log in agent application.
1916 * \param callbackmode non-zero for AgentCallbackLogin
1918 static int __login_exec(struct ast_channel
*chan
, void *data
, int callbackmode
)
1922 int max_login_tries
= maxlogintries
;
1923 struct agent_pvt
*p
;
1924 struct ast_module_user
*u
;
1925 int login_state
= 0;
1926 char user
[AST_MAX_AGENT
] = "";
1927 char pass
[AST_MAX_AGENT
];
1928 char agent
[AST_MAX_AGENT
] = "";
1929 char xpass
[AST_MAX_AGENT
] = "";
1932 AST_DECLARE_APP_ARGS(args
,
1933 AST_APP_ARG(agent_id
);
1934 AST_APP_ARG(options
);
1935 AST_APP_ARG(extension
);
1937 const char *tmpoptions
= NULL
;
1938 char *context
= NULL
;
1939 int play_announcement
= 1;
1940 char agent_goodbye
[AST_MAX_FILENAME_LEN
];
1941 int update_cdr
= updatecdr
;
1942 char *filename
= "agent-loginok";
1943 char tmpchan
[AST_MAX_BUF
] = "";
1945 u
= ast_module_user_add(chan
);
1947 parse
= ast_strdupa(data
);
1949 AST_STANDARD_APP_ARGS(args
, parse
);
1951 ast_copy_string(agent_goodbye
, agentgoodbye
, sizeof(agent_goodbye
));
1953 ast_channel_lock(chan
);
1954 /* Set Channel Specific Login Overrides */
1955 if (pbx_builtin_getvar_helper(chan
, "AGENTLMAXLOGINTRIES") && strlen(pbx_builtin_getvar_helper(chan
, "AGENTLMAXLOGINTRIES"))) {
1956 max_login_tries
= atoi(pbx_builtin_getvar_helper(chan
, "AGENTMAXLOGINTRIES"));
1957 if (max_login_tries
< 0)
1958 max_login_tries
= 0;
1959 tmpoptions
=pbx_builtin_getvar_helper(chan
, "AGENTMAXLOGINTRIES");
1960 if (option_verbose
> 2)
1961 ast_verbose(VERBOSE_PREFIX_3
"Saw variable AGENTMAXLOGINTRIES=%s, setting max_login_tries to: %d on Channel '%s'.\n",tmpoptions
,max_login_tries
,chan
->name
);
1963 if (pbx_builtin_getvar_helper(chan
, "AGENTUPDATECDR") && !ast_strlen_zero(pbx_builtin_getvar_helper(chan
, "AGENTUPDATECDR"))) {
1964 if (ast_true(pbx_builtin_getvar_helper(chan
, "AGENTUPDATECDR")))
1968 tmpoptions
=pbx_builtin_getvar_helper(chan
, "AGENTUPDATECDR");
1969 if (option_verbose
> 2)
1970 ast_verbose(VERBOSE_PREFIX_3
"Saw variable AGENTUPDATECDR=%s, setting update_cdr to: %d on Channel '%s'.\n",tmpoptions
,update_cdr
,chan
->name
);
1972 if (pbx_builtin_getvar_helper(chan
, "AGENTGOODBYE") && !ast_strlen_zero(pbx_builtin_getvar_helper(chan
, "AGENTGOODBYE"))) {
1973 strcpy(agent_goodbye
, pbx_builtin_getvar_helper(chan
, "AGENTGOODBYE"));
1974 tmpoptions
=pbx_builtin_getvar_helper(chan
, "AGENTGOODBYE");
1975 if (option_verbose
> 2)
1976 ast_verbose(VERBOSE_PREFIX_3
"Saw variable AGENTGOODBYE=%s, setting agent_goodbye to: %s on Channel '%s'.\n",tmpoptions
,agent_goodbye
,chan
->name
);
1978 ast_channel_unlock(chan
);
1979 /* End Channel Specific Login Overrides */
1981 if (callbackmode
&& args
.extension
) {
1982 parse
= args
.extension
;
1983 args
.extension
= strsep(&parse
, "@");
1987 if (!ast_strlen_zero(args
.options
)) {
1988 if (strchr(args
.options
, 's')) {
1989 play_announcement
= 0;
1993 if (chan
->_state
!= AST_STATE_UP
)
1994 res
= ast_answer(chan
);
1996 if (!ast_strlen_zero(args
.agent_id
))
1997 ast_copy_string(user
, args
.agent_id
, AST_MAX_AGENT
);
1999 res
= ast_app_getdata(chan
, "agent-user", user
, sizeof(user
) - 1, 0);
2001 while (!res
&& (max_login_tries
==0 || tries
< max_login_tries
)) {
2003 /* Check for password */
2004 AST_LIST_LOCK(&agents
);
2005 AST_LIST_TRAVERSE(&agents
, p
, list
) {
2006 if (!strcmp(p
->agent
, user
) && !p
->pending
)
2007 ast_copy_string(xpass
, p
->password
, sizeof(xpass
));
2009 AST_LIST_UNLOCK(&agents
);
2011 if (!ast_strlen_zero(xpass
))
2012 res
= ast_app_getdata(chan
, "agent-pass", pass
, sizeof(pass
) - 1, 0);
2016 errmsg
= "agent-incorrect";
2019 ast_log(LOG_NOTICE
, "user: %s, pass: %s\n", user
, pass
);
2022 /* Check again for accuracy */
2023 AST_LIST_LOCK(&agents
);
2024 AST_LIST_TRAVERSE(&agents
, p
, list
) {
2025 int unlock_channel
= 1;
2026 ast_channel_lock(chan
);
2027 ast_mutex_lock(&p
->lock
);
2028 if (!strcmp(p
->agent
, user
) &&
2029 !strcmp(p
->password
, pass
) && !p
->pending
) {
2030 login_state
= 1; /* Successful Login */
2032 /* Ensure we can't be gotten until we're done */
2033 gettimeofday(&p
->lastdisc
, NULL
);
2034 p
->lastdisc
.tv_sec
++;
2036 /* Set Channel Specific Agent Overrides */
2037 if (pbx_builtin_getvar_helper(chan
, "AGENTACKCALL") && strlen(pbx_builtin_getvar_helper(chan
, "AGENTACKCALL"))) {
2038 if (!strcasecmp(pbx_builtin_getvar_helper(chan
, "AGENTACKCALL"), "always"))
2040 else if (ast_true(pbx_builtin_getvar_helper(chan
, "AGENTACKCALL")))
2044 tmpoptions
=pbx_builtin_getvar_helper(chan
, "AGENTACKCALL");
2045 if (option_verbose
> 2)
2046 ast_verbose(VERBOSE_PREFIX_3
"Saw variable AGENTACKCALL=%s, setting ackcall to: %d for Agent '%s'.\n",tmpoptions
,p
->ackcall
,p
->agent
);
2048 p
->ackcall
= ackcall
;
2050 if (pbx_builtin_getvar_helper(chan
, "AGENTAUTOLOGOFF") && strlen(pbx_builtin_getvar_helper(chan
, "AGENTAUTOLOGOFF"))) {
2051 p
->autologoff
= atoi(pbx_builtin_getvar_helper(chan
, "AGENTAUTOLOGOFF"));
2052 if (p
->autologoff
< 0)
2054 tmpoptions
=pbx_builtin_getvar_helper(chan
, "AGENTAUTOLOGOFF");
2055 if (option_verbose
> 2)
2056 ast_verbose(VERBOSE_PREFIX_3
"Saw variable AGENTAUTOLOGOFF=%s, setting autologff to: %d for Agent '%s'.\n",tmpoptions
,p
->autologoff
,p
->agent
);
2058 p
->autologoff
= autologoff
;
2060 if (pbx_builtin_getvar_helper(chan
, "AGENTWRAPUPTIME") && strlen(pbx_builtin_getvar_helper(chan
, "AGENTWRAPUPTIME"))) {
2061 p
->wrapuptime
= atoi(pbx_builtin_getvar_helper(chan
, "AGENTWRAPUPTIME"));
2062 if (p
->wrapuptime
< 0)
2064 tmpoptions
=pbx_builtin_getvar_helper(chan
, "AGENTWRAPUPTIME");
2065 if (option_verbose
> 2)
2066 ast_verbose(VERBOSE_PREFIX_3
"Saw variable AGENTWRAPUPTIME=%s, setting wrapuptime to: %d for Agent '%s'.\n",tmpoptions
,p
->wrapuptime
,p
->agent
);
2068 p
->wrapuptime
= wrapuptime
;
2070 ast_channel_unlock(chan
);
2072 /* End Channel Specific Agent Overrides */
2074 char last_loginchan
[80] = "";
2076 snprintf(agent
, sizeof(agent
), "Agent/%s", p
->agent
);
2080 /* Retrieve login chan */
2082 if (!ast_strlen_zero(args
.extension
)) {
2083 ast_copy_string(tmpchan
, args
.extension
, sizeof(tmpchan
));
2086 res
= ast_app_getdata(chan
, "agent-newlocation", tmpchan
+pos
, sizeof(tmpchan
) - 2, 0);
2087 if (ast_strlen_zero(tmpchan
) )
2089 if(ast_exists_extension(chan
, S_OR(context
,"default"), tmpchan
,1, NULL
) ) {
2090 if(!allow_multiple_login(tmpchan
,context
) ) {
2091 args
.extension
= NULL
;
2096 if (args
.extension
) {
2097 ast_log(LOG_WARNING
, "Extension '%s' is not valid for automatic login of agent '%s'\n", args
.extension
, p
->agent
);
2098 args
.extension
= NULL
;
2101 ast_log(LOG_WARNING
, "Extension '%s@%s' is not valid for automatic login of agent '%s'\n", tmpchan
, S_OR(context
, "default"), p
->agent
);
2102 res
= ast_streamfile(chan
, "invalid", chan
->language
);
2104 res
= ast_waitstream(chan
, AST_DIGIT_ANY
);
2115 args
.extension
= tmpchan
;
2117 set_agentbycallerid(p
->logincallerid
, NULL
);
2118 if (!ast_strlen_zero(context
) && !ast_strlen_zero(tmpchan
))
2119 snprintf(p
->loginchan
, sizeof(p
->loginchan
), "%s@%s", tmpchan
, context
);
2121 ast_copy_string(last_loginchan
, p
->loginchan
, sizeof(last_loginchan
));
2122 ast_copy_string(p
->loginchan
, tmpchan
, sizeof(p
->loginchan
));
2124 p
->acknowledged
= 0;
2125 if (ast_strlen_zero(p
->loginchan
)) {
2127 filename
= "agent-loggedoff";
2129 if (chan
->cid
.cid_num
) {
2130 ast_copy_string(p
->logincallerid
, chan
->cid
.cid_num
, sizeof(p
->logincallerid
));
2131 set_agentbycallerid(p
->logincallerid
, p
->agent
);
2133 p
->logincallerid
[0] = '\0';
2136 if(update_cdr
&& chan
->cdr
)
2137 snprintf(chan
->cdr
->channel
, sizeof(chan
->cdr
->channel
), "Agent/%s", p
->agent
);
2141 p
->loginchan
[0] = '\0';
2142 p
->logincallerid
[0] = '\0';
2143 p
->acknowledged
= 0;
2145 ast_mutex_unlock(&p
->lock
);
2146 AST_LIST_UNLOCK(&agents
);
2147 if( !res
&& play_announcement
==1 )
2148 res
= ast_streamfile(chan
, filename
, chan
->language
);
2150 ast_waitstream(chan
, "");
2151 AST_LIST_LOCK(&agents
);
2152 ast_mutex_lock(&p
->lock
);
2154 res
= ast_set_read_format(chan
, ast_best_codec(chan
->nativeformats
));
2156 ast_log(LOG_WARNING
, "Unable to set read format to %d\n", ast_best_codec(chan
->nativeformats
));
2159 res
= ast_set_write_format(chan
, ast_best_codec(chan
->nativeformats
));
2161 ast_log(LOG_WARNING
, "Unable to set write format to %d\n", ast_best_codec(chan
->nativeformats
));
2163 /* Check once more just in case */
2166 if (callbackmode
&& !res
) {
2167 /* Just say goodbye and be done with it */
2168 if (!ast_strlen_zero(p
->loginchan
)) {
2169 if (p
->loginstart
== 0)
2170 time(&p
->loginstart
);
2171 manager_event(EVENT_FLAG_AGENT
, "Agentcallbacklogin",
2175 p
->agent
, p
->loginchan
, chan
->uniqueid
);
2176 ast_queue_log("NONE", chan
->uniqueid
, agent
, "AGENTCALLBACKLOGIN", "%s", p
->loginchan
);
2177 if (option_verbose
> 1)
2178 ast_verbose(VERBOSE_PREFIX_2
"Callback Agent '%s' logged in on %s\n", p
->agent
, p
->loginchan
);
2179 ast_device_state_changed("Agent/%s", p
->agent
);
2180 if (persistent_agents
)
2183 logintime
= time(NULL
) - p
->loginstart
;
2186 agent_logoff_maintenance(p
, last_loginchan
, logintime
, chan
->uniqueid
, NULL
);
2187 if (option_verbose
> 1)
2188 ast_verbose(VERBOSE_PREFIX_2
"Callback Agent '%s' logged out\n", p
->agent
);
2190 AST_LIST_UNLOCK(&agents
);
2192 res
= ast_safe_sleep(chan
, 500);
2193 ast_mutex_unlock(&p
->lock
);
2195 ast_indicate_data(chan
, AST_CONTROL_HOLD
,
2197 !ast_strlen_zero(p
->moh
) ? strlen(p
->moh
) + 1 : 0);
2198 if (p
->loginstart
== 0)
2199 time(&p
->loginstart
);
2200 manager_event(EVENT_FLAG_AGENT
, "Agentlogin",
2204 p
->agent
, chan
->name
, chan
->uniqueid
);
2205 if (update_cdr
&& chan
->cdr
)
2206 snprintf(chan
->cdr
->channel
, sizeof(chan
->cdr
->channel
), "Agent/%s", p
->agent
);
2207 ast_queue_log("NONE", chan
->uniqueid
, agent
, "AGENTLOGIN", "%s", chan
->name
);
2208 if (option_verbose
> 1)
2209 ast_verbose(VERBOSE_PREFIX_2
"Agent '%s' logged in (format %s/%s)\n", p
->agent
,
2210 ast_getformatname(chan
->readformat
), ast_getformatname(chan
->writeformat
));
2211 /* Login this channel and wait for it to go away */
2216 check_availability(p
, 0);
2217 ast_mutex_unlock(&p
->lock
);
2218 AST_LIST_UNLOCK(&agents
);
2219 ast_device_state_changed("Agent/%s", p
->agent
);
2221 ast_mutex_lock(&p
->lock
);
2222 if (p
->deferlogoff
&& p
->chan
) {
2223 ast_softhangup(p
->chan
, AST_SOFTHANGUP_EXPLICIT
);
2226 if (p
->chan
!= chan
)
2228 ast_mutex_unlock(&p
->lock
);
2229 /* Yield here so other interested threads can kick in. */
2234 AST_LIST_LOCK(&agents
);
2235 ast_mutex_lock(&p
->lock
);
2236 if (p
->lastdisc
.tv_sec
) {
2237 if (ast_tvdiff_ms(ast_tvnow(), p
->lastdisc
) > 0) {
2239 ast_log(LOG_DEBUG
, "Wrapup time for %s expired!\n", p
->agent
);
2240 p
->lastdisc
= ast_tv(0, 0);
2241 ast_device_state_changed("Agent/%s", p
->agent
);
2245 check_availability(p
, 0);
2248 ast_mutex_unlock(&p
->lock
);
2249 AST_LIST_UNLOCK(&agents
);
2250 /* Synchronize channel ownership between call to agent and itself. */
2251 ast_mutex_lock( &p
->app_lock
);
2252 ast_mutex_lock(&p
->lock
);
2253 p
->owning_app
= pthread_self();
2254 ast_mutex_unlock(&p
->lock
);
2256 res
= agent_ack_sleep(p
);
2258 res
= ast_safe_sleep_conditional( chan
, 1000, agent_cont_sleep
, p
);
2259 ast_mutex_unlock( &p
->app_lock
);
2260 if ((p
->ackcall
> 1) && (res
== 1)) {
2261 AST_LIST_LOCK(&agents
);
2262 ast_mutex_lock(&p
->lock
);
2263 check_availability(p
, 0);
2264 ast_mutex_unlock(&p
->lock
);
2265 AST_LIST_UNLOCK(&agents
);
2270 ast_mutex_lock(&p
->lock
);
2271 if (res
&& p
->owner
)
2272 ast_log(LOG_WARNING
, "Huh? We broke out when there was still an owner?\n");
2273 /* Log us off if appropriate */
2274 if (p
->chan
== chan
) {
2276 p
->inherited_devicestate
= -1;
2278 p
->acknowledged
= 0;
2279 logintime
= time(NULL
) - p
->loginstart
;
2281 ast_mutex_unlock(&p
->lock
);
2282 manager_event(EVENT_FLAG_AGENT
, "Agentlogoff",
2284 "Logintime: %ld\r\n"
2286 p
->agent
, logintime
, chan
->uniqueid
);
2287 ast_queue_log("NONE", chan
->uniqueid
, agent
, "AGENTLOGOFF", "%s|%ld", chan
->name
, logintime
);
2288 if (option_verbose
> 1)
2289 ast_verbose(VERBOSE_PREFIX_2
"Agent '%s' logged out\n", p
->agent
);
2290 /* If there is no owner, go ahead and kill it now */
2291 ast_device_state_changed("Agent/%s", p
->agent
);
2292 if (p
->dead
&& !p
->owner
) {
2293 ast_mutex_destroy(&p
->lock
);
2294 ast_mutex_destroy(&p
->app_lock
);
2299 ast_mutex_unlock(&p
->lock
);
2304 ast_mutex_unlock(&p
->lock
);
2305 errmsg
= "agent-alreadyon";
2310 ast_mutex_unlock(&p
->lock
);
2311 if (unlock_channel
) {
2312 ast_channel_unlock(chan
);
2316 AST_LIST_UNLOCK(&agents
);
2318 if (!res
&& (max_login_tries
==0 || tries
< max_login_tries
))
2319 res
= ast_app_getdata(chan
, errmsg
, user
, sizeof(user
) - 1, 0);
2323 res
= ast_safe_sleep(chan
, 500);
2325 /* AgentLogin() exit */
2326 if (!callbackmode
) {
2327 ast_module_user_remove(u
);
2329 } else { /* AgentCallbackLogin() exit*/
2331 if (login_state
> 0) {
2332 pbx_builtin_setvar_helper(chan
, "AGENTNUMBER", user
);
2333 if (login_state
==1) {
2334 pbx_builtin_setvar_helper(chan
, "AGENTSTATUS", "on");
2335 pbx_builtin_setvar_helper(chan
, "AGENTEXTEN", args
.extension
);
2337 pbx_builtin_setvar_helper(chan
, "AGENTSTATUS", "off");
2339 pbx_builtin_setvar_helper(chan
, "AGENTSTATUS", "fail");
2341 if (ast_exists_extension(chan
, chan
->context
, chan
->exten
, chan
->priority
+ 1, chan
->cid
.cid_num
)) {
2342 ast_module_user_remove(u
);
2345 /* Do we need to play agent-goodbye now that we will be hanging up? */
2346 if (play_announcement
) {
2348 res
= ast_safe_sleep(chan
, 1000);
2349 res
= ast_streamfile(chan
, agent_goodbye
, chan
->language
);
2351 res
= ast_waitstream(chan
, "");
2353 res
= ast_safe_sleep(chan
, 1000);
2357 ast_module_user_remove(u
);
2359 /* We should never get here if next priority exists when in callbackmode */
2364 * Called by the AgentLogin application (from the dial plan).
2369 * \sa callback_login_exec(), agentmonitoroutgoing_exec(), load_module().
2371 static int login_exec(struct ast_channel
*chan
, void *data
)
2373 return __login_exec(chan
, data
, 0);
2376 static void callback_deprecated(void)
2378 static int depwarning
= 0;
2383 ast_log(LOG_WARNING
, "AgentCallbackLogin is deprecated and will be removed in a future release.\n");
2384 ast_log(LOG_WARNING
, "See doc/queues-with-callback-members.txt for an example of how to achieve\n");
2385 ast_log(LOG_WARNING
, "the same functionality using only dialplan logic.\n");
2390 * Called by the AgentCallbackLogin application (from the dial plan).
2395 * \sa login_exec(), agentmonitoroutgoing_exec(), load_module().
2397 static int callback_exec(struct ast_channel
*chan
, void *data
)
2399 callback_deprecated();
2401 return __login_exec(chan
, data
, 1);
2405 * Sets an agent as logged in by callback in the Manager API.
2406 * It is registered on load_module() and it gets called by the manager backend.
2410 * \sa action_agents(), action_agent_logoff(), load_module().
2412 static int action_agent_callback_login(struct mansession
*s
, const struct message
*m
)
2414 const char *agent
= astman_get_header(m
, "Agent");
2415 const char *exten
= astman_get_header(m
, "Exten");
2416 const char *context
= astman_get_header(m
, "Context");
2417 const char *wrapuptime_s
= astman_get_header(m
, "WrapupTime");
2418 const char *ackcall_s
= astman_get_header(m
, "AckCall");
2419 struct agent_pvt
*p
;
2420 int login_state
= 0;
2422 callback_deprecated();
2424 if (ast_strlen_zero(agent
)) {
2425 astman_send_error(s
, m
, "No agent specified");
2429 if (ast_strlen_zero(exten
)) {
2430 astman_send_error(s
, m
, "No extension specified");
2434 AST_LIST_LOCK(&agents
);
2435 AST_LIST_TRAVERSE(&agents
, p
, list
) {
2436 if (strcmp(p
->agent
, agent
) || p
->pending
)
2439 login_state
= 2; /* already logged in (and on the phone)*/
2442 ast_mutex_lock(&p
->lock
);
2443 login_state
= 1; /* Successful Login */
2445 if (ast_strlen_zero(context
))
2446 ast_copy_string(p
->loginchan
, exten
, sizeof(p
->loginchan
));
2448 snprintf(p
->loginchan
, sizeof(p
->loginchan
), "%s@%s", exten
, context
);
2450 if (!ast_strlen_zero(wrapuptime_s
)) {
2451 p
->wrapuptime
= atoi(wrapuptime_s
);
2452 if (p
->wrapuptime
< 0)
2456 if (strcasecmp(ackcall_s
, "always"))
2458 else if (ast_true(ackcall_s
))
2463 if (p
->loginstart
== 0)
2464 time(&p
->loginstart
);
2465 manager_event(EVENT_FLAG_AGENT
, "Agentcallbacklogin",
2467 "Loginchan: %s\r\n",
2468 p
->agent
, p
->loginchan
);
2469 ast_queue_log("NONE", "NONE", agent
, "AGENTCALLBACKLOGIN", "%s", p
->loginchan
);
2470 if (option_verbose
> 1)
2471 ast_verbose(VERBOSE_PREFIX_2
"Callback Agent '%s' logged in on %s\n", p
->agent
, p
->loginchan
);
2472 ast_device_state_changed("Agent/%s", p
->agent
);
2473 ast_mutex_unlock(&p
->lock
);
2474 if (persistent_agents
)
2477 AST_LIST_UNLOCK(&agents
);
2479 if (login_state
== 1)
2480 astman_send_ack(s
, m
, "Agent logged in");
2481 else if (login_state
== 0)
2482 astman_send_error(s
, m
, "No such agent");
2483 else if (login_state
== 2)
2484 astman_send_error(s
, m
, "Agent already logged in");
2490 * \brief Called by the AgentMonitorOutgoing application (from the dial plan).
2495 * \sa login_exec(), callback_login_exec(), load_module().
2497 static int agentmonitoroutgoing_exec(struct ast_channel
*chan
, void *data
)
2499 int exitifnoagentid
= 0;
2501 int changeoutgoing
= 0;
2503 char agent
[AST_MAX_AGENT
];
2506 if (strchr(data
, 'd'))
2507 exitifnoagentid
= 1;
2508 if (strchr(data
, 'n'))
2510 if (strchr(data
, 'c'))
2513 if (chan
->cid
.cid_num
) {
2515 char agentvar
[AST_MAX_BUF
];
2516 snprintf(agentvar
, sizeof(agentvar
), "%s_%s", GETAGENTBYCALLERID
, chan
->cid
.cid_num
);
2517 if ((tmp
= pbx_builtin_getvar_helper(NULL
, agentvar
))) {
2518 struct agent_pvt
*p
;
2519 ast_copy_string(agent
, tmp
, sizeof(agent
));
2520 AST_LIST_LOCK(&agents
);
2521 AST_LIST_TRAVERSE(&agents
, p
, list
) {
2522 if (!strcasecmp(p
->agent
, tmp
)) {
2523 if (changeoutgoing
) snprintf(chan
->cdr
->channel
, sizeof(chan
->cdr
->channel
), "Agent/%s", p
->agent
);
2524 __agent_start_monitoring(chan
, p
, 1);
2528 AST_LIST_UNLOCK(&agents
);
2533 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
);
2538 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");
2540 /* check if there is n + 101 priority */
2541 /*! \todo XXX Needs to check option priorityjump etc etc */
2543 if (ast_exists_extension(chan
, chan
->context
, chan
->exten
, chan
->priority
+ 101, chan
->cid
.cid_num
)) {
2544 chan
->priority
+=100;
2545 if (option_verbose
> 2)
2546 ast_verbose(VERBOSE_PREFIX_3
"Going to %d priority because there is no callerid or the agentid cannot be found.\n",chan
->priority
);
2547 } else if (exitifnoagentid
)
2554 * \brief Dump AgentCallbackLogin agents to the ASTdb database for persistence
2556 static void dump_agents(void)
2558 struct agent_pvt
*cur_agent
= NULL
;
2561 AST_LIST_TRAVERSE(&agents
, cur_agent
, list
) {
2562 if (cur_agent
->chan
)
2565 if (!ast_strlen_zero(cur_agent
->loginchan
)) {
2566 snprintf(buf
, sizeof(buf
), "%s;%s", cur_agent
->loginchan
, cur_agent
->logincallerid
);
2567 if (ast_db_put(pa_family
, cur_agent
->agent
, buf
))
2568 ast_log(LOG_WARNING
, "failed to create persistent entry in ASTdb for %s!\n", buf
);
2569 else if (option_debug
)
2570 ast_log(LOG_DEBUG
, "Saved Agent: %s on %s\n", cur_agent
->agent
, cur_agent
->loginchan
);
2572 /* Delete - no agent or there is an error */
2573 ast_db_del(pa_family
, cur_agent
->agent
);
2579 * \brief Reload the persistent agents from astdb.
2581 static void reload_agents(void)
2584 struct ast_db_entry
*db_tree
;
2585 struct ast_db_entry
*entry
;
2586 struct agent_pvt
*cur_agent
;
2587 char agent_data
[256];
2590 char *agent_callerid
;
2592 db_tree
= ast_db_gettree(pa_family
, NULL
);
2594 AST_LIST_LOCK(&agents
);
2595 for (entry
= db_tree
; entry
; entry
= entry
->next
) {
2596 agent_num
= entry
->key
+ strlen(pa_family
) + 2;
2597 AST_LIST_TRAVERSE(&agents
, cur_agent
, list
) {
2598 ast_mutex_lock(&cur_agent
->lock
);
2599 if (strcmp(agent_num
, cur_agent
->agent
) == 0)
2601 ast_mutex_unlock(&cur_agent
->lock
);
2604 ast_db_del(pa_family
, agent_num
);
2607 ast_mutex_unlock(&cur_agent
->lock
);
2608 if (!ast_db_get(pa_family
, agent_num
, agent_data
, sizeof(agent_data
)-1)) {
2610 ast_log(LOG_DEBUG
, "Reload Agent from AstDB: %s on %s\n", cur_agent
->agent
, agent_data
);
2612 agent_chan
= strsep(&parse
, ";");
2613 agent_callerid
= strsep(&parse
, ";");
2614 ast_copy_string(cur_agent
->loginchan
, agent_chan
, sizeof(cur_agent
->loginchan
));
2615 if (agent_callerid
) {
2616 ast_copy_string(cur_agent
->logincallerid
, agent_callerid
, sizeof(cur_agent
->logincallerid
));
2617 set_agentbycallerid(cur_agent
->logincallerid
, cur_agent
->agent
);
2619 cur_agent
->logincallerid
[0] = '\0';
2620 if (cur_agent
->loginstart
== 0)
2621 time(&cur_agent
->loginstart
);
2622 ast_device_state_changed("Agent/%s", cur_agent
->agent
);
2625 AST_LIST_UNLOCK(&agents
);
2627 ast_log(LOG_NOTICE
, "Agents successfully reloaded from database.\n");
2628 ast_db_freetree(db_tree
);
2632 /*! \brief Part of PBX channel interface */
2633 static int agent_devicestate(void *data
)
2635 struct agent_pvt
*p
;
2637 ast_group_t groupmatch
;
2640 int res
= AST_DEVICE_INVALID
;
2643 if ((s
[0] == '@') && (sscanf(s
+ 1, "%d", &groupoff
) == 1))
2644 groupmatch
= (1 << groupoff
);
2645 else if ((s
[0] == ':') && (sscanf(s
+ 1, "%d", &groupoff
) == 1)) {
2646 groupmatch
= (1 << groupoff
);
2651 /* Check actual logged in agents first */
2652 AST_LIST_LOCK(&agents
);
2653 AST_LIST_TRAVERSE(&agents
, p
, list
) {
2654 ast_mutex_lock(&p
->lock
);
2655 if (!p
->pending
&& ((groupmatch
&& (p
->group
& groupmatch
)) || !strcmp(data
, p
->agent
))) {
2657 if (res
!= AST_DEVICE_INUSE
)
2658 res
= AST_DEVICE_BUSY
;
2659 } else if (p
->inherited_devicestate
> -1) {
2660 res
= p
->inherited_devicestate
;
2662 if (res
== AST_DEVICE_BUSY
)
2663 res
= AST_DEVICE_INUSE
;
2664 if (p
->chan
|| !ast_strlen_zero(p
->loginchan
)) {
2665 if (res
== AST_DEVICE_INVALID
)
2666 res
= AST_DEVICE_UNKNOWN
;
2667 } else if (res
== AST_DEVICE_INVALID
)
2668 res
= AST_DEVICE_UNAVAILABLE
;
2670 if (!strcmp(data
, p
->agent
)) {
2671 ast_mutex_unlock(&p
->lock
);
2675 ast_mutex_unlock(&p
->lock
);
2677 AST_LIST_UNLOCK(&agents
);
2682 * \note This function expects the agent list to be locked
2684 static struct agent_pvt
*find_agent(char *agentid
)
2686 struct agent_pvt
*cur
;
2688 AST_LIST_TRAVERSE(&agents
, cur
, list
) {
2689 if (!strcmp(cur
->agent
, agentid
))
2696 static int function_agent(struct ast_channel
*chan
, char *cmd
, char *data
, char *buf
, size_t len
)
2699 AST_DECLARE_APP_ARGS(args
,
2700 AST_APP_ARG(agentid
);
2704 struct agent_pvt
*agent
;
2708 if (ast_strlen_zero(data
)) {
2709 ast_log(LOG_WARNING
, "The AGENT function requires an argument - agentid!\n");
2713 parse
= ast_strdupa(data
);
2715 AST_NONSTANDARD_APP_ARGS(args
, parse
, ':');
2717 args
.item
= "status";
2719 AST_LIST_LOCK(&agents
);
2721 if (!(agent
= find_agent(args
.agentid
))) {
2722 AST_LIST_UNLOCK(&agents
);
2723 ast_log(LOG_WARNING
, "Agent '%s' not found!\n", args
.agentid
);
2727 if (!strcasecmp(args
.item
, "status")) {
2728 char *status
= "LOGGEDOUT";
2729 if (agent
->chan
|| !ast_strlen_zero(agent
->loginchan
))
2730 status
= "LOGGEDIN";
2731 ast_copy_string(buf
, status
, len
);
2732 } else if (!strcasecmp(args
.item
, "password"))
2733 ast_copy_string(buf
, agent
->password
, len
);
2734 else if (!strcasecmp(args
.item
, "name"))
2735 ast_copy_string(buf
, agent
->name
, len
);
2736 else if (!strcasecmp(args
.item
, "mohclass"))
2737 ast_copy_string(buf
, agent
->moh
, len
);
2738 else if (!strcasecmp(args
.item
, "channel")) {
2740 ast_copy_string(buf
, agent
->chan
->name
, len
);
2741 tmp
= strrchr(buf
, '-');
2745 } else if (!strcasecmp(args
.item
, "exten"))
2746 ast_copy_string(buf
, agent
->loginchan
, len
);
2748 AST_LIST_UNLOCK(&agents
);
2753 struct ast_custom_function agent_function
= {
2755 .synopsis
= "Gets information about an Agent",
2756 .syntax
= "AGENT(<agentid>[:item])",
2757 .read
= function_agent
,
2758 .desc
= "The valid items to retrieve are:\n"
2759 "- status (default) The status of the agent\n"
2760 " LOGGEDIN | LOGGEDOUT\n"
2761 "- password The password of the agent\n"
2762 "- name The name of the agent\n"
2763 "- mohclass MusicOnHold class\n"
2764 "- exten The callback extension for the Agent (AgentCallbackLogin)\n"
2765 "- channel The name of the active channel for the Agent (AgentLogin)\n"
2770 * \brief Initialize the Agents module.
2771 * This function is being called by Asterisk when loading the module.
2772 * Among other things it registers applications, cli commands and reads the cofiguration file.
2774 * \returns int Always 0.
2776 static int load_module(void)
2778 /* Make sure we can register our agent channel type */
2779 if (ast_channel_register(&agent_tech
)) {
2780 ast_log(LOG_ERROR
, "Unable to register channel class 'Agent'\n");
2783 /* Read in the config */
2784 if (!read_agent_config())
2785 return AST_MODULE_LOAD_DECLINE
;
2786 if (persistent_agents
)
2788 /* Dialplan applications */
2789 ast_register_application(app
, login_exec
, synopsis
, descrip
);
2790 ast_register_application(app2
, callback_exec
, synopsis2
, descrip2
);
2791 ast_register_application(app3
, agentmonitoroutgoing_exec
, synopsis3
, descrip3
);
2793 /* Manager commands */
2794 ast_manager_register2("Agents", EVENT_FLAG_AGENT
, action_agents
, "Lists agents and their status", mandescr_agents
);
2795 ast_manager_register2("AgentLogoff", EVENT_FLAG_AGENT
, action_agent_logoff
, "Sets an agent as no longer logged in", mandescr_agent_logoff
);
2796 ast_manager_register2("AgentCallbackLogin", EVENT_FLAG_AGENT
, action_agent_callback_login
, "Sets an agent as logged in by callback", mandescr_agent_callback_login
);
2799 ast_cli_register_multiple(cli_agents
, sizeof(cli_agents
) / sizeof(struct ast_cli_entry
));
2801 /* Dialplan Functions */
2802 ast_custom_function_register(&agent_function
);
2804 ast_devstate_add(agent_devicestate_cb
, NULL
);
2809 static int reload(void)
2811 read_agent_config();
2812 if (persistent_agents
)
2817 static int unload_module(void)
2819 struct agent_pvt
*p
;
2820 /* First, take us out of the channel loop */
2821 ast_channel_unregister(&agent_tech
);
2822 /* Delete devicestate subscription */
2823 ast_devstate_del(agent_devicestate_cb
, NULL
);
2824 /* Unregister dialplan functions */
2825 ast_custom_function_unregister(&agent_function
);
2826 /* Unregister CLI commands */
2827 ast_cli_unregister_multiple(cli_agents
, sizeof(cli_agents
) / sizeof(struct ast_cli_entry
));
2828 /* Unregister dialplan applications */
2829 ast_unregister_application(app
);
2830 ast_unregister_application(app2
);
2831 ast_unregister_application(app3
);
2832 /* Unregister manager command */
2833 ast_manager_unregister("Agents");
2834 ast_manager_unregister("AgentLogoff");
2835 ast_manager_unregister("AgentCallbackLogin");
2836 /* Unregister channel */
2837 AST_LIST_LOCK(&agents
);
2838 /* Hangup all interfaces if they have an owner */
2839 while ((p
= AST_LIST_REMOVE_HEAD(&agents
, list
))) {
2841 ast_softhangup(p
->owner
, AST_SOFTHANGUP_APPUNLOAD
);
2844 AST_LIST_UNLOCK(&agents
);
2848 AST_MODULE_INFO(ASTERISK_GPL_KEY
, AST_MODFLAG_DEFAULT
, "Agent Proxy Channel",
2849 .load
= load_module
,
2850 .unload
= unload_module
,