Merged revisions 140566 via svnmerge from
[asterisk-bristuff.git] / channels / chan_agent.c
blobee02f9021889f5a928befbea2bde4c02289ca1fd
1 /*
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.
20 /*! \file
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.
28 * \par See also
29 * \arg \ref Config_agent
31 * \ingroup channel_drivers
33 /*** MODULEINFO
34 <depend>chan_local</depend>
35 ***/
37 #include "asterisk.h"
39 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
41 #include <sys/socket.h>
42 #include <fcntl.h>
43 #include <netdb.h>
44 #include <netinet/in.h>
45 #include <arpa/inet.h>
46 #include <sys/signal.h>
48 #include "asterisk/lock.h"
49 #include "asterisk/channel.h"
50 #include "asterisk/config.h"
51 #include "asterisk/module.h"
52 #include "asterisk/pbx.h"
53 #include "asterisk/sched.h"
54 #include "asterisk/io.h"
55 #include "asterisk/rtp.h"
56 #include "asterisk/acl.h"
57 #include "asterisk/callerid.h"
58 #include "asterisk/file.h"
59 #include "asterisk/cli.h"
60 #include "asterisk/app.h"
61 #include "asterisk/musiconhold.h"
62 #include "asterisk/manager.h"
63 #include "asterisk/features.h"
64 #include "asterisk/utils.h"
65 #include "asterisk/causes.h"
66 #include "asterisk/astdb.h"
67 #include "asterisk/devicestate.h"
68 #include "asterisk/monitor.h"
69 #include "asterisk/stringfields.h"
70 #include "asterisk/event.h"
72 static const char tdesc[] = "Call Agent Proxy Channel";
73 static const char config[] = "agents.conf";
75 static const char app[] = "AgentLogin";
76 static const char app3[] = "AgentMonitorOutgoing";
78 static const char synopsis[] = "Call agent login";
79 static const char synopsis3[] = "Record agent's outgoing call";
81 static const char descrip[] =
82 " AgentLogin([AgentNo][,options]):\n"
83 "Asks the agent to login to the system. Always returns -1. While\n"
84 "logged in, the agent can receive calls and will hear a 'beep'\n"
85 "when a new call comes in. The agent can dump the call by pressing\n"
86 "the star key.\n"
87 "The option string may contain zero or more of the following characters:\n"
88 " 's' -- silent login - do not announce the login ok segment after agent logged on/off\n";
90 static const char descrip3[] =
91 " AgentMonitorOutgoing([options]):\n"
92 "Tries to figure out the id of the agent who is placing outgoing call based on\n"
93 "comparison of the callerid of the current interface and the global variable \n"
94 "placed by the AgentCallbackLogin application. That's why it should be used only\n"
95 "with the AgentCallbackLogin app. Uses the monitoring functions in chan_agent \n"
96 "instead of Monitor application. That has to be configured in the agents.conf file.\n"
97 "\nReturn value:\n"
98 "Normally the app returns 0 unless the options are passed.\n"
99 "\nOptions:\n"
100 " 'd' - make the app return -1 if there is an error condition\n"
101 " 'c' - change the CDR so that the source of the call is 'Agent/agent_id'\n"
102 " 'n' - don't generate the warnings when there is no callerid or the\n"
103 " agentid is not known.\n"
104 " It's handy if you want to have one context for agent and non-agent calls.\n";
106 static const char mandescr_agents[] =
107 "Description: Will list info about all possible agents.\n"
108 "Variables: NONE\n";
110 static const char mandescr_agent_logoff[] =
111 "Description: Sets an agent as no longer logged in.\n"
112 "Variables: (Names marked with * are required)\n"
113 " *Agent: Agent ID of the agent to log off\n"
114 " Soft: Set to 'true' to not hangup existing calls\n";
116 static char moh[80] = "default";
118 #define AST_MAX_AGENT 80 /*!< Agent ID or Password max length */
119 #define AST_MAX_BUF 256
120 #define AST_MAX_FILENAME_LEN 256
122 static const char pa_family[] = "Agents"; /*!< Persistent Agents astdb family */
123 #define PA_MAX_LEN 2048 /*!< The maximum length of each persistent member agent database entry */
125 static int persistent_agents = 0; /*!< queues.conf [general] option */
126 static void dump_agents(void);
128 #define DEFAULT_ACCEPTDTMF '#'
129 #define DEFAULT_ENDDTMF '*'
131 static ast_group_t group;
132 static int autologoff;
133 static int wrapuptime;
134 static int ackcall;
135 static int endcall;
136 static int multiplelogin = 1;
137 static int autologoffunavail = 0;
138 static char acceptdtmf = DEFAULT_ACCEPTDTMF;
139 static char enddtmf = DEFAULT_ENDDTMF;
141 static int maxlogintries = 3;
142 static char agentgoodbye[AST_MAX_FILENAME_LEN] = "vm-goodbye";
144 static int recordagentcalls = 0;
145 static char recordformat[AST_MAX_BUF] = "";
146 static char recordformatext[AST_MAX_BUF] = "";
147 static char urlprefix[AST_MAX_BUF] = "";
148 static char savecallsin[AST_MAX_BUF] = "";
149 static int updatecdr = 0;
150 static char beep[AST_MAX_BUF] = "beep";
151 struct ast_event_sub *agent_devicestate_sub = NULL;
153 #define GETAGENTBYCALLERID "AGENTBYCALLERID"
155 /*! \brief Structure representing an agent. */
156 struct agent_pvt {
157 ast_mutex_t lock; /*!< Channel private lock */
158 int dead; /*!< Poised for destruction? */
159 int pending; /*!< Not a real agent -- just pending a match */
160 int abouttograb; /*!< About to grab */
161 int autologoff; /*!< Auto timeout time */
162 int ackcall; /*!< ackcall */
163 int deferlogoff; /*!< Defer logoff to hangup */
164 char acceptdtmf;
165 char enddtmf;
166 time_t loginstart; /*!< When agent first logged in (0 when logged off) */
167 time_t start; /*!< When call started */
168 struct timeval lastdisc; /*!< When last disconnected */
169 int wrapuptime; /*!< Wrapup time in ms */
170 ast_group_t group; /*!< Group memberships */
171 int acknowledged; /*!< Acknowledged */
172 char moh[80]; /*!< Which music on hold */
173 char agent[AST_MAX_AGENT]; /*!< Agent ID */
174 char password[AST_MAX_AGENT]; /*!< Password for Agent login */
175 char name[AST_MAX_AGENT];
176 int inherited_devicestate; /*!< Does the underlying channel have a devicestate to pass? */
177 ast_mutex_t app_lock; /**< Synchronization between owning applications */
178 volatile pthread_t owning_app; /**< Owning application thread id */
179 volatile int app_sleep_cond; /**< Sleep condition for the login app */
180 struct ast_channel *owner; /**< Agent */
181 char loginchan[80]; /**< channel they logged in from */
182 char logincallerid[80]; /**< Caller ID they had when they logged in */
183 struct ast_channel *chan; /**< Channel we use */
184 AST_LIST_ENTRY(agent_pvt) list; /**< Next Agent in the linked list. */
187 static AST_LIST_HEAD_STATIC(agents, agent_pvt); /*!< Holds the list of agents (loaded form agents.conf). */
189 #define CHECK_FORMATS(ast, p) do { \
190 if (p->chan) {\
191 if (ast->nativeformats != p->chan->nativeformats) { \
192 ast_debug(1, "Native formats changing from %d to %d\n", ast->nativeformats, p->chan->nativeformats); \
193 /* Native formats changed, reset things */ \
194 ast->nativeformats = p->chan->nativeformats; \
195 ast_debug(1, "Resetting read to %d and write to %d\n", ast->readformat, ast->writeformat);\
196 ast_set_read_format(ast, ast->readformat); \
197 ast_set_write_format(ast, ast->writeformat); \
199 if (p->chan->readformat != ast->rawreadformat && !p->chan->generator) \
200 ast_set_read_format(p->chan, ast->rawreadformat); \
201 if (p->chan->writeformat != ast->rawwriteformat && !p->chan->generator) \
202 ast_set_write_format(p->chan, ast->rawwriteformat); \
204 } while(0)
206 /*! \brief Cleanup moves all the relevant FD's from the 2nd to the first, but retains things
207 properly for a timingfd XXX This might need more work if agents were logged in as agents or other
208 totally impractical combinations XXX */
210 #define CLEANUP(ast, p) do { \
211 int x; \
212 if (p->chan) { \
213 for (x=0;x<AST_MAX_FDS;x++) {\
214 if (x != AST_TIMING_FD) \
215 ast_channel_set_fd(ast, x, p->chan->fds[x]); \
217 ast_channel_set_fd(ast, AST_AGENT_FD, p->chan->fds[AST_TIMING_FD]); \
219 } while(0)
221 /*--- Forward declarations */
222 static struct ast_channel *agent_request(const char *type, int format, void *data, int *cause);
223 static int agent_devicestate(void *data);
224 static void agent_logoff_maintenance(struct agent_pvt *p, char *loginchan, long logintime, const char *uniqueid, char *logcommand);
225 static int agent_digit_begin(struct ast_channel *ast, char digit);
226 static int agent_digit_end(struct ast_channel *ast, char digit, unsigned int duration);
227 static int agent_call(struct ast_channel *ast, char *dest, int timeout);
228 static int agent_hangup(struct ast_channel *ast);
229 static int agent_answer(struct ast_channel *ast);
230 static struct ast_frame *agent_read(struct ast_channel *ast);
231 static int agent_write(struct ast_channel *ast, struct ast_frame *f);
232 static int agent_sendhtml(struct ast_channel *ast, int subclass, const char *data, int datalen);
233 static int agent_sendtext(struct ast_channel *ast, const char *text);
234 static int agent_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen);
235 static int agent_fixup(struct ast_channel *oldchan, struct ast_channel *newchan);
236 static struct ast_channel *agent_bridgedchannel(struct ast_channel *chan, struct ast_channel *bridge);
237 static void set_agentbycallerid(const char *callerid, const char *agent);
238 static char *complete_agent_logoff_cmd(const char *line, const char *word, int pos, int state);
239 static struct ast_channel* agent_get_base_channel(struct ast_channel *chan);
240 static int agent_set_base_channel(struct ast_channel *chan, struct ast_channel *base);
242 /*! \brief Channel interface description for PBX integration */
243 static const struct ast_channel_tech agent_tech = {
244 .type = "Agent",
245 .description = tdesc,
246 .capabilities = -1,
247 .requester = agent_request,
248 .devicestate = agent_devicestate,
249 .send_digit_begin = agent_digit_begin,
250 .send_digit_end = agent_digit_end,
251 .call = agent_call,
252 .hangup = agent_hangup,
253 .answer = agent_answer,
254 .read = agent_read,
255 .write = agent_write,
256 .write_video = agent_write,
257 .send_html = agent_sendhtml,
258 .send_text = agent_sendtext,
259 .exception = agent_read,
260 .indicate = agent_indicate,
261 .fixup = agent_fixup,
262 .bridged_channel = agent_bridgedchannel,
263 .get_base_channel = agent_get_base_channel,
264 .set_base_channel = agent_set_base_channel,
267 static void agent_devicestate_cb(const struct ast_event *event, void *unused)
269 int res, i;
270 struct agent_pvt *p;
271 char base[AST_CHANNEL_NAME], *tmp;
272 const char *device;
273 enum ast_device_state state;
275 state = ast_event_get_ie_uint(event, AST_EVENT_IE_STATE);
276 device = ast_event_get_ie_str(event, AST_EVENT_IE_DEVICE);
278 if (ast_strlen_zero(device)) {
279 return;
282 /* Skip Agent status */
283 if (!strncasecmp(device, "Agent/", 6)) {
284 return;
287 /* Try to be safe, but don't deadlock */
288 for (i = 0; i < 10; i++) {
289 if ((res = AST_LIST_TRYLOCK(&agents)) == 0) {
290 break;
293 if (res) {
294 return;
297 AST_LIST_TRAVERSE(&agents, p, list) {
298 ast_mutex_lock(&p->lock);
299 if (p->chan) {
300 ast_copy_string(base, p->chan->name, sizeof(base));
301 if ((tmp = strrchr(base, '-'))) {
302 *tmp = '\0';
304 if (strcasecmp(p->chan->name, device) == 0 || strcasecmp(base, device) == 0) {
305 p->inherited_devicestate = state;
306 ast_devstate_changed(state, "Agent/%s", p->agent);
309 ast_mutex_unlock(&p->lock);
311 AST_LIST_UNLOCK(&agents);
315 * Adds an agent to the global list of agents.
317 * \param agent A string with the username, password and real name of an agent. As defined in agents.conf. Example: "13,169,John Smith"
318 * \param pending If it is pending or not.
319 * @return The just created agent.
320 * \sa agent_pvt, agents.
322 static struct agent_pvt *add_agent(const char *agent, int pending)
324 char *parse;
325 AST_DECLARE_APP_ARGS(args,
326 AST_APP_ARG(agt);
327 AST_APP_ARG(password);
328 AST_APP_ARG(name);
330 char *password = NULL;
331 char *name = NULL;
332 char *agt = NULL;
333 struct agent_pvt *p;
335 parse = ast_strdupa(agent);
337 /* Extract username (agt), password and name from agent (args). */
338 AST_STANDARD_APP_ARGS(args, parse);
340 if(args.argc == 0) {
341 ast_log(LOG_WARNING, "A blank agent line!\n");
342 return NULL;
345 if(ast_strlen_zero(args.agt) ) {
346 ast_log(LOG_WARNING, "An agent line with no agentid!\n");
347 return NULL;
348 } else
349 agt = args.agt;
351 if(!ast_strlen_zero(args.password)) {
352 password = args.password;
353 while (*password && *password < 33) password++;
355 if(!ast_strlen_zero(args.name)) {
356 name = args.name;
357 while (*name && *name < 33) name++;
360 /* Are we searching for the agent here ? To see if it exists already ? */
361 AST_LIST_TRAVERSE(&agents, p, list) {
362 if (!pending && !strcmp(p->agent, agt))
363 break;
365 if (!p) {
366 // Build the agent.
367 if (!(p = ast_calloc(1, sizeof(*p))))
368 return NULL;
369 ast_copy_string(p->agent, agt, sizeof(p->agent));
370 ast_mutex_init(&p->lock);
371 ast_mutex_init(&p->app_lock);
372 p->owning_app = (pthread_t) -1;
373 p->app_sleep_cond = 1;
374 p->group = group;
375 p->pending = pending;
376 p->inherited_devicestate = -1;
377 AST_LIST_INSERT_TAIL(&agents, p, list);
380 ast_copy_string(p->password, password ? password : "", sizeof(p->password));
381 ast_copy_string(p->name, name ? name : "", sizeof(p->name));
382 ast_copy_string(p->moh, moh, sizeof(p->moh));
383 p->ackcall = ackcall;
384 p->autologoff = autologoff;
385 p->acceptdtmf = acceptdtmf;
386 p->enddtmf = enddtmf;
388 /* If someone reduces the wrapuptime and reloads, we want it
389 * to change the wrapuptime immediately on all calls */
390 if (p->wrapuptime > wrapuptime) {
391 struct timeval now = ast_tvnow();
392 /* XXX check what is this exactly */
394 /* We won't be pedantic and check the tv_usec val */
395 if (p->lastdisc.tv_sec > (now.tv_sec + wrapuptime/1000)) {
396 p->lastdisc.tv_sec = now.tv_sec + wrapuptime/1000;
397 p->lastdisc.tv_usec = now.tv_usec;
400 p->wrapuptime = wrapuptime;
402 if (pending)
403 p->dead = 1;
404 else
405 p->dead = 0;
406 return p;
410 * Deletes an agent after doing some clean up.
411 * Further documentation: How safe is this function ? What state should the agent be to be cleaned.
412 * \param p Agent to be deleted.
413 * \returns Always 0.
415 static int agent_cleanup(struct agent_pvt *p)
417 struct ast_channel *chan = p->owner;
418 p->owner = NULL;
419 chan->tech_pvt = NULL;
420 p->app_sleep_cond = 1;
421 /* Release ownership of the agent to other threads (presumably running the login app). */
422 ast_mutex_unlock(&p->app_lock);
423 if (chan)
424 ast_channel_free(chan);
425 if (p->dead) {
426 ast_mutex_destroy(&p->lock);
427 ast_mutex_destroy(&p->app_lock);
428 ast_free(p);
430 return 0;
433 static int check_availability(struct agent_pvt *newlyavailable, int needlock);
435 static int agent_answer(struct ast_channel *ast)
437 ast_log(LOG_WARNING, "Huh? Agent is being asked to answer?\n");
438 return -1;
441 static int __agent_start_monitoring(struct ast_channel *ast, struct agent_pvt *p, int needlock)
443 char tmp[AST_MAX_BUF],tmp2[AST_MAX_BUF], *pointer;
444 char filename[AST_MAX_BUF];
445 int res = -1;
446 if (!p)
447 return -1;
448 if (!ast->monitor) {
449 snprintf(filename, sizeof(filename), "agent-%s-%s",p->agent, ast->uniqueid);
450 /* substitute . for - */
451 if ((pointer = strchr(filename, '.')))
452 *pointer = '-';
453 snprintf(tmp, sizeof(tmp), "%s%s", savecallsin, filename);
454 ast_monitor_start(ast, recordformat, tmp, needlock, X_REC_IN | X_REC_OUT);
455 ast_monitor_setjoinfiles(ast, 1);
456 snprintf(tmp2, sizeof(tmp2), "%s%s.%s", urlprefix, filename, recordformatext);
457 #if 0
458 ast_verbose("name is %s, link is %s\n",tmp, tmp2);
459 #endif
460 if (!ast->cdr)
461 ast->cdr = ast_cdr_alloc();
462 ast_cdr_setuserfield(ast, tmp2);
463 res = 0;
464 } else
465 ast_log(LOG_ERROR, "Recording already started on that call.\n");
466 return res;
469 static int agent_start_monitoring(struct ast_channel *ast, int needlock)
471 return __agent_start_monitoring(ast, ast->tech_pvt, needlock);
474 static struct ast_frame *agent_read(struct ast_channel *ast)
476 struct agent_pvt *p = ast->tech_pvt;
477 struct ast_frame *f = NULL;
478 static struct ast_frame answer_frame = { AST_FRAME_CONTROL, AST_CONTROL_ANSWER };
479 const char *status;
480 ast_mutex_lock(&p->lock);
481 CHECK_FORMATS(ast, p);
482 if (p->chan) {
483 ast_copy_flags(p->chan, ast, AST_FLAG_EXCEPTION);
484 p->chan->fdno = (ast->fdno == AST_AGENT_FD) ? AST_TIMING_FD : ast->fdno;
485 f = ast_read(p->chan);
486 } else
487 f = &ast_null_frame;
488 if (!f) {
489 /* If there's a channel, hang it up (if it's on a callback) make it NULL */
490 if (p->chan) {
491 p->chan->_bridge = NULL;
492 /* Note that we don't hangup if it's not a callback because Asterisk will do it
493 for us when the PBX instance that called login finishes */
494 if (!ast_strlen_zero(p->loginchan)) {
495 if (p->chan)
496 ast_debug(1, "Bridge on '%s' being cleared (2)\n", p->chan->name);
497 if (p->owner->_state != AST_STATE_UP) {
498 int howlong = time(NULL) - p->start;
499 if (p->autologoff && howlong > p->autologoff) {
500 long logintime = time(NULL) - p->loginstart;
501 p->loginstart = 0;
502 ast_log(LOG_NOTICE, "Agent '%s' didn't answer/confirm within %d seconds (waited %d)\n", p->name, p->autologoff, howlong);
503 agent_logoff_maintenance(p, p->loginchan, logintime, ast->uniqueid, "Autologoff");
504 if (persistent_agents)
505 dump_agents();
508 status = pbx_builtin_getvar_helper(p->chan, "CHANLOCALSTATUS");
509 if (autologoffunavail && status && !strcasecmp(status, "CHANUNAVAIL")) {
510 long logintime = time(NULL) - p->loginstart;
511 p->loginstart = 0;
512 ast_log(LOG_NOTICE, "Agent read: '%s' is not available now, auto logoff\n", p->name);
513 agent_logoff_maintenance(p, p->loginchan, logintime, ast->uniqueid, "Chanunavail");
515 ast_hangup(p->chan);
516 if (p->wrapuptime && p->acknowledged)
517 p->lastdisc = ast_tvadd(ast_tvnow(), ast_samp2tv(p->wrapuptime, 1000));
519 p->chan = NULL;
520 p->inherited_devicestate = -1;
521 ast_devstate_changed(AST_DEVICE_UNAVAILABLE, "Agent/%s", p->agent);
522 p->acknowledged = 0;
524 } else {
525 /* if acknowledgement is not required, and the channel is up, we may have missed
526 an AST_CONTROL_ANSWER (if there was one), so mark the call acknowledged anyway */
527 if (!p->ackcall && !p->acknowledged && p->chan && (p->chan->_state == AST_STATE_UP))
528 p->acknowledged = 1;
529 switch (f->frametype) {
530 case AST_FRAME_CONTROL:
531 if (f->subclass == AST_CONTROL_ANSWER) {
532 if (p->ackcall) {
533 ast_verb(3, "%s answered, waiting for '%c' to acknowledge\n", p->chan->name, p->acceptdtmf);
534 /* Don't pass answer along */
535 ast_frfree(f);
536 f = &ast_null_frame;
537 } else {
538 p->acknowledged = 1;
539 /* Use the builtin answer frame for the
540 recording start check below. */
541 ast_frfree(f);
542 f = &answer_frame;
545 break;
546 case AST_FRAME_DTMF_BEGIN:
547 /*ignore DTMF begin's as it can cause issues with queue announce files*/
548 if((!p->acknowledged && f->subclass == p->acceptdtmf) || (f->subclass == p->enddtmf && endcall)){
549 ast_frfree(f);
550 f = &ast_null_frame;
552 break;
553 case AST_FRAME_DTMF_END:
554 if (!p->acknowledged && (f->subclass == p->acceptdtmf)) {
555 ast_verb(3, "%s acknowledged\n", p->chan->name);
556 p->acknowledged = 1;
557 ast_frfree(f);
558 f = &answer_frame;
559 } else if (f->subclass == p->enddtmf && endcall) {
560 /* terminates call */
561 ast_frfree(f);
562 f = NULL;
564 break;
565 case AST_FRAME_VOICE:
566 case AST_FRAME_VIDEO:
567 /* don't pass voice or video until the call is acknowledged */
568 if (!p->acknowledged) {
569 ast_frfree(f);
570 f = &ast_null_frame;
572 default:
573 /* pass everything else on through */
574 break;
578 CLEANUP(ast,p);
579 if (p->chan && !p->chan->_bridge) {
580 if (strcasecmp(p->chan->tech->type, "Local")) {
581 p->chan->_bridge = ast;
582 if (p->chan)
583 ast_debug(1, "Bridge on '%s' being set to '%s' (3)\n", p->chan->name, p->chan->_bridge->name);
586 ast_mutex_unlock(&p->lock);
587 if (recordagentcalls && f == &answer_frame)
588 agent_start_monitoring(ast,0);
589 return f;
592 static int agent_sendhtml(struct ast_channel *ast, int subclass, const char *data, int datalen)
594 struct agent_pvt *p = ast->tech_pvt;
595 int res = -1;
596 ast_mutex_lock(&p->lock);
597 if (p->chan)
598 res = ast_channel_sendhtml(p->chan, subclass, data, datalen);
599 ast_mutex_unlock(&p->lock);
600 return res;
603 static int agent_sendtext(struct ast_channel *ast, const char *text)
605 struct agent_pvt *p = ast->tech_pvt;
606 int res = -1;
607 ast_mutex_lock(&p->lock);
608 if (p->chan)
609 res = ast_sendtext(p->chan, text);
610 ast_mutex_unlock(&p->lock);
611 return res;
614 static int agent_write(struct ast_channel *ast, struct ast_frame *f)
616 struct agent_pvt *p = ast->tech_pvt;
617 int res = -1;
618 CHECK_FORMATS(ast, p);
619 ast_mutex_lock(&p->lock);
620 if (!p->chan)
621 res = 0;
622 else {
623 if ((f->frametype != AST_FRAME_VOICE) ||
624 (f->frametype != AST_FRAME_VIDEO) ||
625 (f->subclass == p->chan->writeformat)) {
626 res = ast_write(p->chan, f);
627 } else {
628 ast_debug(1, "Dropping one incompatible %s frame on '%s' to '%s'\n",
629 f->frametype == AST_FRAME_VOICE ? "audio" : "video",
630 ast->name, p->chan->name);
631 res = 0;
634 CLEANUP(ast, p);
635 ast_mutex_unlock(&p->lock);
636 return res;
639 static int agent_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
641 struct agent_pvt *p = newchan->tech_pvt;
642 ast_mutex_lock(&p->lock);
643 if (p->owner != oldchan) {
644 ast_log(LOG_WARNING, "old channel wasn't %p but was %p\n", oldchan, p->owner);
645 ast_mutex_unlock(&p->lock);
646 return -1;
648 p->owner = newchan;
649 ast_mutex_unlock(&p->lock);
650 return 0;
653 static int agent_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen)
655 struct agent_pvt *p = ast->tech_pvt;
656 int res = -1;
657 ast_mutex_lock(&p->lock);
658 if (p->chan)
659 res = p->chan->tech->indicate ? p->chan->tech->indicate(p->chan, condition, data, datalen) : -1;
660 else
661 res = 0;
662 ast_mutex_unlock(&p->lock);
663 return res;
666 static int agent_digit_begin(struct ast_channel *ast, char digit)
668 struct agent_pvt *p = ast->tech_pvt;
669 ast_mutex_lock(&p->lock);
670 if (p->chan) {
671 ast_senddigit_begin(p->chan, digit);
673 ast_mutex_unlock(&p->lock);
674 return 0;
677 static int agent_digit_end(struct ast_channel *ast, char digit, unsigned int duration)
679 struct agent_pvt *p = ast->tech_pvt;
680 ast_mutex_lock(&p->lock);
681 if (p->chan) {
682 ast_senddigit_end(p->chan, digit, duration);
684 ast_mutex_unlock(&p->lock);
685 return 0;
688 static int agent_call(struct ast_channel *ast, char *dest, int timeout)
690 struct agent_pvt *p = ast->tech_pvt;
691 int res = -1;
692 int newstate=0;
693 ast_mutex_lock(&p->lock);
694 p->acknowledged = 0;
695 if (!p->chan) {
696 if (p->pending) {
697 ast_debug(1, "Pretending to dial on pending agent\n");
698 newstate = AST_STATE_DIALING;
699 res = 0;
700 } else {
701 ast_log(LOG_NOTICE, "Whoa, they hung up between alloc and call... what are the odds of that?\n");
702 res = -1;
704 ast_mutex_unlock(&p->lock);
705 if (newstate)
706 ast_setstate(ast, newstate);
707 return res;
708 } else if (!ast_strlen_zero(p->loginchan)) {
709 time(&p->start);
710 /* Call on this agent */
711 ast_verb(3, "outgoing agentcall, to agent '%s', on '%s'\n", p->agent, p->chan->name);
712 ast_set_callerid(p->chan,
713 ast->cid.cid_num, ast->cid.cid_name, NULL);
714 ast_channel_inherit_variables(ast, p->chan);
715 res = ast_call(p->chan, p->loginchan, 0);
716 CLEANUP(ast,p);
717 ast_mutex_unlock(&p->lock);
718 return res;
720 ast_verb(3, "agent_call, call to agent '%s' call on '%s'\n", p->agent, p->chan->name);
721 ast_debug(3, "Playing beep, lang '%s'\n", p->chan->language);
722 res = ast_streamfile(p->chan, beep, p->chan->language);
723 ast_debug(3, "Played beep, result '%d'\n", res);
724 if (!res) {
725 res = ast_waitstream(p->chan, "");
726 ast_debug(3, "Waited for stream, result '%d'\n", res);
728 if (!res) {
729 res = ast_set_read_format(p->chan, ast_best_codec(p->chan->nativeformats));
730 ast_debug(3, "Set read format, result '%d'\n", res);
731 if (res)
732 ast_log(LOG_WARNING, "Unable to set read format to %s\n", ast_getformatname(ast_best_codec(p->chan->nativeformats)));
733 } else {
734 /* Agent hung-up */
735 p->chan = NULL;
736 p->inherited_devicestate = -1;
737 ast_devstate_changed(AST_DEVICE_UNAVAILABLE, "Agent/%s", p->agent);
740 if (!res) {
741 res = ast_set_write_format(p->chan, ast_best_codec(p->chan->nativeformats));
742 ast_debug(3, "Set write format, result '%d'\n", res);
743 if (res)
744 ast_log(LOG_WARNING, "Unable to set write format to %s\n", ast_getformatname(ast_best_codec(p->chan->nativeformats)));
746 if(!res) {
747 /* Call is immediately up, or might need ack */
748 if (p->ackcall > 1)
749 newstate = AST_STATE_RINGING;
750 else {
751 newstate = AST_STATE_UP;
752 if (recordagentcalls)
753 agent_start_monitoring(ast, 0);
754 p->acknowledged = 1;
756 res = 0;
758 CLEANUP(ast, p);
759 ast_mutex_unlock(&p->lock);
760 if (newstate)
761 ast_setstate(ast, newstate);
762 return res;
765 /*! \brief store/clear the global variable that stores agentid based on the callerid */
766 static void set_agentbycallerid(const char *callerid, const char *agent)
768 char buf[AST_MAX_BUF];
770 /* if there is no Caller ID, nothing to do */
771 if (ast_strlen_zero(callerid))
772 return;
774 snprintf(buf, sizeof(buf), "%s_%s", GETAGENTBYCALLERID, callerid);
775 pbx_builtin_setvar_helper(NULL, buf, agent);
778 /*! \brief return the channel or base channel if one exists. This function assumes the channel it is called on is already locked */
779 struct ast_channel* agent_get_base_channel(struct ast_channel *chan)
781 struct agent_pvt *p = NULL;
782 struct ast_channel *base = chan;
784 /* chan is locked by the calling function */
785 if (!chan || !chan->tech_pvt) {
786 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);
787 return NULL;
789 p = chan->tech_pvt;
790 if (p->chan)
791 base = p->chan;
792 return base;
795 int agent_set_base_channel(struct ast_channel *chan, struct ast_channel *base)
797 struct agent_pvt *p = NULL;
799 if (!chan || !base) {
800 ast_log(LOG_ERROR, "whoa, you need a channel (0x%ld) and a base channel (0x%ld) for setting.\n", (long)chan, (long)base);
801 return -1;
803 p = chan->tech_pvt;
804 if (!p) {
805 ast_log(LOG_ERROR, "whoa, channel %s is missing his tech_pvt structure!!.\n", chan->name);
806 return -1;
808 p->chan = base;
809 return 0;
812 static int agent_hangup(struct ast_channel *ast)
814 struct agent_pvt *p = ast->tech_pvt;
815 int howlong = 0;
816 const char *status;
817 ast_mutex_lock(&p->lock);
818 p->owner = NULL;
819 ast->tech_pvt = NULL;
820 p->app_sleep_cond = 1;
821 p->acknowledged = 0;
823 /* if they really are hung up then set start to 0 so the test
824 * later if we're called on an already downed channel
825 * doesn't cause an agent to be logged out like when
826 * agent_request() is followed immediately by agent_hangup()
827 * as in apps/app_chanisavail.c:chanavail_exec()
830 ast_debug(1, "Hangup called for state %s\n", ast_state2str(ast->_state));
831 if (p->start && (ast->_state != AST_STATE_UP)) {
832 howlong = time(NULL) - p->start;
833 p->start = 0;
834 } else if (ast->_state == AST_STATE_RESERVED)
835 howlong = 0;
836 else
837 p->start = 0;
838 if (p->chan) {
839 p->chan->_bridge = NULL;
840 /* If they're dead, go ahead and hang up on the agent now */
841 if (!ast_strlen_zero(p->loginchan)) {
842 /* Store last disconnect time */
843 if (p->wrapuptime)
844 p->lastdisc = ast_tvadd(ast_tvnow(), ast_samp2tv(p->wrapuptime, 1000));
845 else
846 p->lastdisc = ast_tv(0,0);
847 if (p->chan) {
848 status = pbx_builtin_getvar_helper(p->chan, "CHANLOCALSTATUS");
849 if (autologoffunavail && status && !strcasecmp(status, "CHANUNAVAIL")) {
850 long logintime = time(NULL) - p->loginstart;
851 p->loginstart = 0;
852 ast_log(LOG_NOTICE, "Agent hangup: '%s' is not available now, auto logoff\n", p->name);
853 agent_logoff_maintenance(p, p->loginchan, logintime, ast->uniqueid, "Chanunavail");
855 /* Recognize the hangup and pass it along immediately */
856 ast_hangup(p->chan);
857 p->chan = NULL;
858 p->inherited_devicestate = -1;
859 ast_devstate_changed(AST_DEVICE_UNAVAILABLE, "Agent/%s", p->agent);
861 ast_debug(1, "Hungup, howlong is %d, autologoff is %d\n", howlong, p->autologoff);
862 if ((p->deferlogoff) || (howlong && p->autologoff && (howlong > p->autologoff))) {
863 long logintime = time(NULL) - p->loginstart;
864 p->loginstart = 0;
865 if (!p->deferlogoff)
866 ast_log(LOG_NOTICE, "Agent '%s' didn't answer/confirm within %d seconds (waited %d)\n", p->name, p->autologoff, howlong);
867 p->deferlogoff = 0;
868 agent_logoff_maintenance(p, p->loginchan, logintime, ast->uniqueid, "Autologoff");
869 if (persistent_agents)
870 dump_agents();
872 } else if (p->dead) {
873 ast_channel_lock(p->chan);
874 ast_softhangup(p->chan, AST_SOFTHANGUP_EXPLICIT);
875 ast_channel_unlock(p->chan);
876 } else if (p->loginstart) {
877 ast_channel_lock(p->chan);
878 ast_indicate_data(p->chan, AST_CONTROL_HOLD,
879 S_OR(p->moh, NULL),
880 !ast_strlen_zero(p->moh) ? strlen(p->moh) + 1 : 0);
881 ast_channel_unlock(p->chan);
884 ast_mutex_unlock(&p->lock);
886 /* Only register a device state change if the agent is still logged in */
887 if (!p->loginstart) {
888 p->loginchan[0] = '\0';
889 p->logincallerid[0] = '\0';
890 if (persistent_agents)
891 dump_agents();
892 } else {
893 ast_devstate_changed(AST_DEVICE_NOT_INUSE, "Agent/%s", p->agent);
896 if (p->pending) {
897 AST_LIST_LOCK(&agents);
898 AST_LIST_REMOVE(&agents, p, list);
899 AST_LIST_UNLOCK(&agents);
901 if (p->abouttograb) {
902 /* Let the "about to grab" thread know this isn't valid anymore, and let it
903 kill it later */
904 p->abouttograb = 0;
905 } else if (p->dead) {
906 ast_mutex_destroy(&p->lock);
907 ast_mutex_destroy(&p->app_lock);
908 ast_free(p);
909 } else {
910 if (p->chan) {
911 /* Not dead -- check availability now */
912 ast_mutex_lock(&p->lock);
913 /* Store last disconnect time */
914 p->lastdisc = ast_tvadd(ast_tvnow(), ast_samp2tv(p->wrapuptime, 1000));
915 ast_mutex_unlock(&p->lock);
917 /* Release ownership of the agent to other threads (presumably running the login app). */
918 if (ast_strlen_zero(p->loginchan))
919 ast_mutex_unlock(&p->app_lock);
921 return 0;
924 static int agent_cont_sleep( void *data )
926 struct agent_pvt *p;
927 int res;
929 p = (struct agent_pvt *)data;
931 ast_mutex_lock(&p->lock);
932 res = p->app_sleep_cond;
933 if (p->lastdisc.tv_sec) {
934 if (ast_tvdiff_ms(ast_tvnow(), p->lastdisc) > 0)
935 res = 1;
937 ast_mutex_unlock(&p->lock);
939 if (!res)
940 ast_debug(5, "agent_cont_sleep() returning %d\n", res );
942 return res;
945 static int agent_ack_sleep(void *data)
947 struct agent_pvt *p;
948 int res=0;
949 int to = 1000;
950 struct ast_frame *f;
952 /* Wait a second and look for something */
954 p = (struct agent_pvt *) data;
955 if (!p->chan)
956 return -1;
958 for(;;) {
959 to = ast_waitfor(p->chan, to);
960 if (to < 0)
961 return -1;
962 if (!to)
963 return 0;
964 f = ast_read(p->chan);
965 if (!f)
966 return -1;
967 if (f->frametype == AST_FRAME_DTMF)
968 res = f->subclass;
969 else
970 res = 0;
971 ast_frfree(f);
972 ast_mutex_lock(&p->lock);
973 if (!p->app_sleep_cond) {
974 ast_mutex_unlock(&p->lock);
975 return 0;
976 } else if (res == p->acceptdtmf) {
977 ast_mutex_unlock(&p->lock);
978 return 1;
980 ast_mutex_unlock(&p->lock);
981 res = 0;
983 return res;
986 static struct ast_channel *agent_bridgedchannel(struct ast_channel *chan, struct ast_channel *bridge)
988 struct agent_pvt *p = bridge->tech_pvt;
989 struct ast_channel *ret = NULL;
991 if (p) {
992 if (chan == p->chan)
993 ret = bridge->_bridge;
994 else if (chan == bridge->_bridge)
995 ret = p->chan;
998 ast_debug(1, "Asked for bridged channel on '%s'/'%s', returning '%s'\n", chan->name, bridge->name, ret ? ret->name : "<none>");
999 return ret;
1002 /*! \brief Create new agent channel */
1003 static struct ast_channel *agent_new(struct agent_pvt *p, int state)
1005 struct ast_channel *tmp;
1006 #if 0
1007 if (!p->chan) {
1008 ast_log(LOG_WARNING, "No channel? :(\n");
1009 return NULL;
1011 #endif
1012 if (p->pending)
1013 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);
1014 else
1015 tmp = ast_channel_alloc(0, state, 0, 0, "", p->chan ? p->chan->exten:"", p->chan ? p->chan->context:"", 0, "Agent/%s", p->agent);
1016 if (!tmp) {
1017 ast_log(LOG_WARNING, "Unable to allocate agent channel structure\n");
1018 return NULL;
1021 tmp->tech = &agent_tech;
1022 if (p->chan) {
1023 tmp->nativeformats = p->chan->nativeformats;
1024 tmp->writeformat = p->chan->writeformat;
1025 tmp->rawwriteformat = p->chan->writeformat;
1026 tmp->readformat = p->chan->readformat;
1027 tmp->rawreadformat = p->chan->readformat;
1028 ast_string_field_set(tmp, language, p->chan->language);
1029 ast_copy_string(tmp->context, p->chan->context, sizeof(tmp->context));
1030 ast_copy_string(tmp->exten, p->chan->exten, sizeof(tmp->exten));
1031 /* XXX Is this really all we copy form the originating channel?? */
1032 } else {
1033 tmp->nativeformats = AST_FORMAT_SLINEAR;
1034 tmp->writeformat = AST_FORMAT_SLINEAR;
1035 tmp->rawwriteformat = AST_FORMAT_SLINEAR;
1036 tmp->readformat = AST_FORMAT_SLINEAR;
1037 tmp->rawreadformat = AST_FORMAT_SLINEAR;
1039 /* Safe, agentlock already held */
1040 tmp->tech_pvt = p;
1041 p->owner = tmp;
1042 tmp->priority = 1;
1043 /* Wake up and wait for other applications (by definition the login app)
1044 * to release this channel). Takes ownership of the agent channel
1045 * to this thread only.
1046 * For signalling the other thread, ast_queue_frame is used until we
1047 * can safely use signals for this purpose. The pselect() needs to be
1048 * implemented in the kernel for this.
1050 p->app_sleep_cond = 0;
1051 if(ast_strlen_zero(p->loginchan) && ast_mutex_trylock(&p->app_lock)) {
1052 if (p->chan) {
1053 ast_queue_frame(p->chan, &ast_null_frame);
1054 ast_mutex_unlock(&p->lock); /* For other thread to read the condition. */
1055 ast_mutex_lock(&p->app_lock);
1056 ast_mutex_lock(&p->lock);
1057 } else {
1058 ast_log(LOG_WARNING, "Agent disconnected while we were connecting the call\n");
1059 p->owner = NULL;
1060 tmp->tech_pvt = NULL;
1061 p->app_sleep_cond = 1;
1062 ast_channel_free( tmp );
1063 ast_mutex_unlock(&p->lock); /* For other thread to read the condition. */
1064 ast_mutex_unlock(&p->app_lock);
1065 return NULL;
1067 } else if (!ast_strlen_zero(p->loginchan)) {
1068 if (p->chan)
1069 ast_queue_frame(p->chan, &ast_null_frame);
1070 if (!p->chan) {
1071 ast_log(LOG_WARNING, "Agent disconnected while we were connecting the call\n");
1072 p->owner = NULL;
1073 tmp->tech_pvt = NULL;
1074 p->app_sleep_cond = 1;
1075 ast_channel_free( tmp );
1076 ast_mutex_unlock(&p->lock); /* For other thread to read the condition. */
1077 return NULL;
1080 if (p->chan)
1081 ast_indicate(p->chan, AST_CONTROL_UNHOLD);
1082 p->owning_app = pthread_self();
1083 /* After the above step, there should not be any blockers. */
1084 if (p->chan) {
1085 if (ast_test_flag(p->chan, AST_FLAG_BLOCKING)) {
1086 ast_log( LOG_ERROR, "A blocker exists after agent channel ownership acquired\n" );
1087 ast_assert(ast_test_flag(p->chan, AST_FLAG_BLOCKING) == 0);
1090 return tmp;
1095 * Read configuration data. The file named agents.conf.
1097 * \returns Always 0, or so it seems.
1099 static int read_agent_config(int reload)
1101 struct ast_config *cfg;
1102 struct ast_config *ucfg;
1103 struct ast_variable *v;
1104 struct agent_pvt *p;
1105 const char *general_val;
1106 const char *catname;
1107 const char *hasagent;
1108 int genhasagent;
1109 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
1111 group = 0;
1112 autologoff = 0;
1113 wrapuptime = 0;
1114 ackcall = 0;
1115 endcall = 1;
1116 cfg = ast_config_load(config, config_flags);
1117 if (!cfg) {
1118 ast_log(LOG_NOTICE, "No agent configuration found -- agent support disabled\n");
1119 return 0;
1120 } else if (cfg == CONFIG_STATUS_FILEUNCHANGED)
1121 return -1;
1122 AST_LIST_LOCK(&agents);
1123 AST_LIST_TRAVERSE(&agents, p, list) {
1124 p->dead = 1;
1126 strcpy(moh, "default");
1127 /* set the default recording values */
1128 recordagentcalls = 0;
1129 strcpy(recordformat, "wav");
1130 strcpy(recordformatext, "wav");
1131 urlprefix[0] = '\0';
1132 savecallsin[0] = '\0';
1134 /* Read in [general] section for persistence */
1135 if ((general_val = ast_variable_retrieve(cfg, "general", "persistentagents")))
1136 persistent_agents = ast_true(general_val);
1137 multiplelogin = ast_true(ast_variable_retrieve(cfg, "general", "multiplelogin"));
1139 /* Read in the [agents] section */
1140 v = ast_variable_browse(cfg, "agents");
1141 while(v) {
1142 /* Create the interface list */
1143 if (!strcasecmp(v->name, "agent")) {
1144 add_agent(v->value, 0);
1145 } else if (!strcasecmp(v->name, "group")) {
1146 group = ast_get_group(v->value);
1147 } else if (!strcasecmp(v->name, "autologoff")) {
1148 autologoff = atoi(v->value);
1149 if (autologoff < 0)
1150 autologoff = 0;
1151 } else if (!strcasecmp(v->name, "ackcall")) {
1152 if (!strcasecmp(v->value, "always"))
1153 ackcall = 2;
1154 else if (ast_true(v->value))
1155 ackcall = 1;
1156 else
1157 ackcall = 0;
1158 } else if (!strcasecmp(v->name, "endcall")) {
1159 endcall = ast_true(v->value);
1160 } else if (!strcasecmp(v->name, "acceptdtmf")) {
1161 acceptdtmf = *(v->value);
1162 ast_log(LOG_NOTICE, "Set acceptdtmf to %c\n", acceptdtmf);
1163 } else if (!strcasecmp(v->name, "enddtmf")) {
1164 enddtmf = *(v->value);
1165 } else if (!strcasecmp(v->name, "wrapuptime")) {
1166 wrapuptime = atoi(v->value);
1167 if (wrapuptime < 0)
1168 wrapuptime = 0;
1169 } else if (!strcasecmp(v->name, "maxlogintries") && !ast_strlen_zero(v->value)) {
1170 maxlogintries = atoi(v->value);
1171 if (maxlogintries < 0)
1172 maxlogintries = 0;
1173 } else if (!strcasecmp(v->name, "goodbye") && !ast_strlen_zero(v->value)) {
1174 strcpy(agentgoodbye,v->value);
1175 } else if (!strcasecmp(v->name, "musiconhold")) {
1176 ast_copy_string(moh, v->value, sizeof(moh));
1177 } else if (!strcasecmp(v->name, "updatecdr")) {
1178 if (ast_true(v->value))
1179 updatecdr = 1;
1180 else
1181 updatecdr = 0;
1182 } else if (!strcasecmp(v->name, "autologoffunavail")) {
1183 if (ast_true(v->value))
1184 autologoffunavail = 1;
1185 else
1186 autologoffunavail = 0;
1187 } else if (!strcasecmp(v->name, "recordagentcalls")) {
1188 recordagentcalls = ast_true(v->value);
1189 } else if (!strcasecmp(v->name, "recordformat")) {
1190 ast_copy_string(recordformat, v->value, sizeof(recordformat));
1191 if (!strcasecmp(v->value, "wav49"))
1192 strcpy(recordformatext, "WAV");
1193 else
1194 ast_copy_string(recordformatext, v->value, sizeof(recordformatext));
1195 } else if (!strcasecmp(v->name, "urlprefix")) {
1196 ast_copy_string(urlprefix, v->value, sizeof(urlprefix));
1197 if (urlprefix[strlen(urlprefix) - 1] != '/')
1198 strncat(urlprefix, "/", sizeof(urlprefix) - strlen(urlprefix) - 1);
1199 } else if (!strcasecmp(v->name, "savecallsin")) {
1200 if (v->value[0] == '/')
1201 ast_copy_string(savecallsin, v->value, sizeof(savecallsin));
1202 else
1203 snprintf(savecallsin, sizeof(savecallsin) - 2, "/%s", v->value);
1204 if (savecallsin[strlen(savecallsin) - 1] != '/')
1205 strncat(savecallsin, "/", sizeof(savecallsin) - strlen(savecallsin) - 1);
1206 } else if (!strcasecmp(v->name, "custom_beep")) {
1207 ast_copy_string(beep, v->value, sizeof(beep));
1209 v = v->next;
1211 if ((ucfg = ast_config_load("users.conf", config_flags)) && ucfg != CONFIG_STATUS_FILEUNCHANGED) {
1212 genhasagent = ast_true(ast_variable_retrieve(ucfg, "general", "hasagent"));
1213 catname = ast_category_browse(ucfg, NULL);
1214 while(catname) {
1215 if (strcasecmp(catname, "general")) {
1216 hasagent = ast_variable_retrieve(ucfg, catname, "hasagent");
1217 if (ast_true(hasagent) || (!hasagent && genhasagent)) {
1218 char tmp[256];
1219 const char *fullname = ast_variable_retrieve(ucfg, catname, "fullname");
1220 const char *secret = ast_variable_retrieve(ucfg, catname, "secret");
1221 if (!fullname)
1222 fullname = "";
1223 if (!secret)
1224 secret = "";
1225 snprintf(tmp, sizeof(tmp), "%s,%s,%s", catname, secret,fullname);
1226 add_agent(tmp, 0);
1229 catname = ast_category_browse(ucfg, catname);
1231 ast_config_destroy(ucfg);
1233 AST_LIST_TRAVERSE_SAFE_BEGIN(&agents, p, list) {
1234 if (p->dead) {
1235 AST_LIST_REMOVE_CURRENT(list);
1236 /* Destroy if appropriate */
1237 if (!p->owner) {
1238 if (!p->chan) {
1239 ast_mutex_destroy(&p->lock);
1240 ast_mutex_destroy(&p->app_lock);
1241 ast_free(p);
1242 } else {
1243 /* Cause them to hang up */
1244 ast_softhangup(p->chan, AST_SOFTHANGUP_EXPLICIT);
1249 AST_LIST_TRAVERSE_SAFE_END;
1250 AST_LIST_UNLOCK(&agents);
1251 ast_config_destroy(cfg);
1252 return 1;
1255 static int check_availability(struct agent_pvt *newlyavailable, int needlock)
1257 struct ast_channel *chan=NULL, *parent=NULL;
1258 struct agent_pvt *p;
1259 int res;
1261 ast_debug(1, "Checking availability of '%s'\n", newlyavailable->agent);
1262 if (needlock)
1263 AST_LIST_LOCK(&agents);
1264 AST_LIST_TRAVERSE(&agents, p, list) {
1265 if (p == newlyavailable) {
1266 continue;
1268 ast_mutex_lock(&p->lock);
1269 if (!p->abouttograb && p->pending && ((p->group && (newlyavailable->group & p->group)) || !strcmp(p->agent, newlyavailable->agent))) {
1270 ast_debug(1, "Call '%s' looks like a winner for agent '%s'\n", p->owner->name, newlyavailable->agent);
1271 /* We found a pending call, time to merge */
1272 chan = agent_new(newlyavailable, AST_STATE_DOWN);
1273 parent = p->owner;
1274 p->abouttograb = 1;
1275 ast_mutex_unlock(&p->lock);
1276 break;
1278 ast_mutex_unlock(&p->lock);
1280 if (needlock)
1281 AST_LIST_UNLOCK(&agents);
1282 if (parent && chan) {
1283 if (newlyavailable->ackcall > 1) {
1284 /* Don't do beep here */
1285 res = 0;
1286 } else {
1287 ast_debug(3, "Playing beep, lang '%s'\n", newlyavailable->chan->language);
1288 res = ast_streamfile(newlyavailable->chan, beep, newlyavailable->chan->language);
1289 ast_debug(3, "Played beep, result '%d'\n", res);
1290 if (!res) {
1291 res = ast_waitstream(newlyavailable->chan, "");
1292 ast_debug(1, "Waited for stream, result '%d'\n", res);
1295 if (!res) {
1296 /* Note -- parent may have disappeared */
1297 if (p->abouttograb) {
1298 newlyavailable->acknowledged = 1;
1299 /* Safe -- agent lock already held */
1300 ast_setstate(parent, AST_STATE_UP);
1301 ast_setstate(chan, AST_STATE_UP);
1302 ast_copy_string(parent->context, chan->context, sizeof(parent->context));
1303 /* Go ahead and mark the channel as a zombie so that masquerade will
1304 destroy it for us, and we need not call ast_hangup */
1305 ast_set_flag(chan, AST_FLAG_ZOMBIE);
1306 ast_channel_masquerade(parent, chan);
1307 p->abouttograb = 0;
1308 } else {
1309 ast_debug(1, "Sneaky, parent disappeared in the mean time...\n");
1310 agent_cleanup(newlyavailable);
1312 } else {
1313 ast_debug(1, "Ugh... Agent hung up at exactly the wrong time\n");
1314 agent_cleanup(newlyavailable);
1317 return 0;
1320 static int check_beep(struct agent_pvt *newlyavailable, int needlock)
1322 struct agent_pvt *p;
1323 int res=0;
1325 ast_debug(1, "Checking beep availability of '%s'\n", newlyavailable->agent);
1326 if (needlock)
1327 AST_LIST_LOCK(&agents);
1328 AST_LIST_TRAVERSE(&agents, p, list) {
1329 if (p == newlyavailable) {
1330 continue;
1332 ast_mutex_lock(&p->lock);
1333 if (!p->abouttograb && p->pending && ((p->group && (newlyavailable->group & p->group)) || !strcmp(p->agent, newlyavailable->agent))) {
1334 ast_debug(1, "Call '%s' looks like a would-be winner for agent '%s'\n", p->owner->name, newlyavailable->agent);
1335 ast_mutex_unlock(&p->lock);
1336 break;
1338 ast_mutex_unlock(&p->lock);
1340 if (needlock)
1341 AST_LIST_UNLOCK(&agents);
1342 if (p) {
1343 ast_mutex_unlock(&newlyavailable->lock);
1344 ast_debug(3, "Playing beep, lang '%s'\n", newlyavailable->chan->language);
1345 res = ast_streamfile(newlyavailable->chan, beep, newlyavailable->chan->language);
1346 ast_debug(1, "Played beep, result '%d'\n", res);
1347 if (!res) {
1348 res = ast_waitstream(newlyavailable->chan, "");
1349 ast_debug(1, "Waited for stream, result '%d'\n", res);
1351 ast_mutex_lock(&newlyavailable->lock);
1353 return res;
1356 /*! \brief Part of the Asterisk PBX interface */
1357 static struct ast_channel *agent_request(const char *type, int format, void *data, int *cause)
1359 struct agent_pvt *p;
1360 struct ast_channel *chan = NULL;
1361 char *s;
1362 ast_group_t groupmatch;
1363 int groupoff;
1364 int waitforagent=0;
1365 int hasagent = 0;
1366 struct timeval now;
1368 s = data;
1369 if ((s[0] == '@') && (sscanf(s + 1, "%d", &groupoff) == 1)) {
1370 groupmatch = (1 << groupoff);
1371 } else if ((s[0] == ':') && (sscanf(s + 1, "%d", &groupoff) == 1)) {
1372 groupmatch = (1 << groupoff);
1373 waitforagent = 1;
1374 } else
1375 groupmatch = 0;
1377 /* Check actual logged in agents first */
1378 AST_LIST_LOCK(&agents);
1379 AST_LIST_TRAVERSE(&agents, p, list) {
1380 ast_mutex_lock(&p->lock);
1381 if (!p->pending && ((groupmatch && (p->group & groupmatch)) || !strcmp(data, p->agent)) &&
1382 ast_strlen_zero(p->loginchan)) {
1383 if (p->chan)
1384 hasagent++;
1385 now = ast_tvnow();
1386 if (!p->lastdisc.tv_sec || (now.tv_sec >= p->lastdisc.tv_sec)) {
1387 p->lastdisc = ast_tv(0, 0);
1388 /* Agent must be registered, but not have any active call, and not be in a waiting state */
1389 if (!p->owner && p->chan) {
1390 /* Fixed agent */
1391 chan = agent_new(p, AST_STATE_DOWN);
1393 if (chan) {
1394 ast_mutex_unlock(&p->lock);
1395 break;
1399 ast_mutex_unlock(&p->lock);
1401 if (!p) {
1402 AST_LIST_TRAVERSE(&agents, p, list) {
1403 ast_mutex_lock(&p->lock);
1404 if (!p->pending && ((groupmatch && (p->group & groupmatch)) || !strcmp(data, p->agent))) {
1405 if (p->chan || !ast_strlen_zero(p->loginchan))
1406 hasagent++;
1407 now = ast_tvnow();
1408 #if 0
1409 ast_log(LOG_NOTICE, "Time now: %ld, Time of lastdisc: %ld\n", now.tv_sec, p->lastdisc.tv_sec);
1410 #endif
1411 if (!p->lastdisc.tv_sec || (now.tv_sec >= p->lastdisc.tv_sec)) {
1412 p->lastdisc = ast_tv(0, 0);
1413 /* Agent must be registered, but not have any active call, and not be in a waiting state */
1414 if (!p->owner && p->chan) {
1415 /* Could still get a fixed agent */
1416 chan = agent_new(p, AST_STATE_DOWN);
1417 } else if (!p->owner && !ast_strlen_zero(p->loginchan)) {
1418 /* Adjustable agent */
1419 p->chan = ast_request("Local", format, p->loginchan, cause);
1420 if (p->chan)
1421 chan = agent_new(p, AST_STATE_DOWN);
1423 if (chan) {
1424 ast_mutex_unlock(&p->lock);
1425 break;
1429 ast_mutex_unlock(&p->lock);
1433 if (!chan && waitforagent) {
1434 /* No agent available -- but we're requesting to wait for one.
1435 Allocate a place holder */
1436 if (hasagent) {
1437 ast_debug(1, "Creating place holder for '%s'\n", s);
1438 p = add_agent(data, 1);
1439 p->group = groupmatch;
1440 chan = agent_new(p, AST_STATE_DOWN);
1441 if (!chan)
1442 ast_log(LOG_WARNING, "Weird... Fix this to drop the unused pending agent\n");
1443 } else {
1444 ast_debug(1, "Not creating place holder for '%s' since nobody logged in\n", s);
1447 *cause = hasagent ? AST_CAUSE_BUSY : AST_CAUSE_UNREGISTERED;
1448 AST_LIST_UNLOCK(&agents);
1449 return chan;
1452 static force_inline int powerof(unsigned int d)
1454 int x = ffs(d);
1456 if (x)
1457 return x - 1;
1459 return 0;
1463 * Lists agents and their status to the Manager API.
1464 * It is registered on load_module() and it gets called by the manager backend.
1465 * \param s
1466 * \param m
1467 * \returns
1468 * \sa action_agent_logoff(), load_module().
1470 static int action_agents(struct mansession *s, const struct message *m)
1472 const char *id = astman_get_header(m,"ActionID");
1473 char idText[256] = "";
1474 char chanbuf[256];
1475 struct agent_pvt *p;
1476 char *username = NULL;
1477 char *loginChan = NULL;
1478 char *talkingto = NULL;
1479 char *talkingtoChan = NULL;
1480 char *status = NULL;
1482 if (!ast_strlen_zero(id))
1483 snprintf(idText, sizeof(idText) ,"ActionID: %s\r\n", id);
1484 astman_send_ack(s, m, "Agents will follow");
1485 AST_LIST_LOCK(&agents);
1486 AST_LIST_TRAVERSE(&agents, p, list) {
1487 ast_mutex_lock(&p->lock);
1489 /* Status Values:
1490 AGENT_LOGGEDOFF - Agent isn't logged in
1491 AGENT_IDLE - Agent is logged in, and waiting for call
1492 AGENT_ONCALL - Agent is logged in, and on a call
1493 AGENT_UNKNOWN - Don't know anything about agent. Shouldn't ever get this. */
1495 username = S_OR(p->name, "None");
1497 /* Set a default status. It 'should' get changed. */
1498 status = "AGENT_UNKNOWN";
1500 if (!ast_strlen_zero(p->loginchan) && !p->chan) {
1501 loginChan = p->loginchan;
1502 talkingto = "n/a";
1503 talkingtoChan = "n/a";
1504 status = "AGENT_IDLE";
1505 if (p->acknowledged) {
1506 snprintf(chanbuf, sizeof(chanbuf), " %s (Confirmed)", p->loginchan);
1507 loginChan = chanbuf;
1509 } else if (p->chan) {
1510 loginChan = ast_strdupa(p->chan->name);
1511 if (p->owner && p->owner->_bridge) {
1512 talkingto = p->chan->cid.cid_num;
1513 if (ast_bridged_channel(p->owner))
1514 talkingtoChan = ast_strdupa(ast_bridged_channel(p->owner)->name);
1515 else
1516 talkingtoChan = "n/a";
1517 status = "AGENT_ONCALL";
1518 } else {
1519 talkingto = "n/a";
1520 talkingtoChan = "n/a";
1521 status = "AGENT_IDLE";
1523 } else {
1524 loginChan = "n/a";
1525 talkingto = "n/a";
1526 talkingtoChan = "n/a";
1527 status = "AGENT_LOGGEDOFF";
1530 astman_append(s, "Event: Agents\r\n"
1531 "Agent: %s\r\n"
1532 "Name: %s\r\n"
1533 "Status: %s\r\n"
1534 "LoggedInChan: %s\r\n"
1535 "LoggedInTime: %d\r\n"
1536 "TalkingTo: %s\r\n"
1537 "TalkingToChan: %s\r\n"
1538 "%s"
1539 "\r\n",
1540 p->agent, username, status, loginChan, (int)p->loginstart, talkingto, talkingtoChan, idText);
1541 ast_mutex_unlock(&p->lock);
1543 AST_LIST_UNLOCK(&agents);
1544 astman_append(s, "Event: AgentsComplete\r\n"
1545 "%s"
1546 "\r\n",idText);
1547 return 0;
1550 static void agent_logoff_maintenance(struct agent_pvt *p, char *loginchan, long logintime, const char *uniqueid, char *logcommand)
1552 char *tmp = NULL;
1553 char agent[AST_MAX_AGENT];
1555 if (!ast_strlen_zero(logcommand))
1556 tmp = logcommand;
1557 else
1558 tmp = ast_strdupa("");
1560 snprintf(agent, sizeof(agent), "Agent/%s", p->agent);
1562 if (!ast_strlen_zero(uniqueid)) {
1563 manager_event(EVENT_FLAG_AGENT, "Agentcallbacklogoff",
1564 "Agent: %s\r\n"
1565 "Reason: %s\r\n"
1566 "Loginchan: %s\r\n"
1567 "Logintime: %ld\r\n"
1568 "Uniqueid: %s\r\n",
1569 p->agent, tmp, loginchan, logintime, uniqueid);
1570 } else {
1571 manager_event(EVENT_FLAG_AGENT, "Agentcallbacklogoff",
1572 "Agent: %s\r\n"
1573 "Reason: %s\r\n"
1574 "Loginchan: %s\r\n"
1575 "Logintime: %ld\r\n",
1576 p->agent, tmp, loginchan, logintime);
1579 ast_queue_log("NONE", ast_strlen_zero(uniqueid) ? "NONE" : uniqueid, agent, "AGENTCALLBACKLOGOFF", "%s|%ld|%s", loginchan, logintime, tmp);
1580 set_agentbycallerid(p->logincallerid, NULL);
1581 p->loginchan[0] ='\0';
1582 p->logincallerid[0] = '\0';
1583 p->inherited_devicestate = -1;
1584 ast_devstate_changed(AST_DEVICE_UNAVAILABLE, "Agent/%s", p->agent);
1585 if (persistent_agents)
1586 dump_agents();
1590 static int agent_logoff(const char *agent, int soft)
1592 struct agent_pvt *p;
1593 long logintime;
1594 int ret = -1; /* Return -1 if no agent if found */
1596 AST_LIST_LOCK(&agents);
1597 AST_LIST_TRAVERSE(&agents, p, list) {
1598 if (!strcasecmp(p->agent, agent)) {
1599 ret = 0;
1600 if (p->owner || p->chan) {
1601 if (!soft) {
1602 ast_mutex_lock(&p->lock);
1604 while (p->owner && ast_channel_trylock(p->owner)) {
1605 DEADLOCK_AVOIDANCE(&p->lock);
1607 if (p->owner) {
1608 ast_softhangup(p->owner, AST_SOFTHANGUP_EXPLICIT);
1609 ast_channel_unlock(p->owner);
1612 while (p->chan && ast_channel_trylock(p->chan)) {
1613 DEADLOCK_AVOIDANCE(&p->lock);
1615 if (p->chan) {
1616 ast_softhangup(p->chan, AST_SOFTHANGUP_EXPLICIT);
1617 ast_channel_unlock(p->chan);
1620 ast_mutex_unlock(&p->lock);
1621 } else
1622 p->deferlogoff = 1;
1623 } else {
1624 logintime = time(NULL) - p->loginstart;
1625 p->loginstart = 0;
1626 agent_logoff_maintenance(p, p->loginchan, logintime, NULL, "CommandLogoff");
1628 break;
1631 AST_LIST_UNLOCK(&agents);
1633 return ret;
1636 static char *agent_logoff_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1638 int ret;
1639 char *agent;
1641 switch (cmd) {
1642 case CLI_INIT:
1643 e->command = "agent logoff";
1644 e->usage =
1645 "Usage: agent logoff <channel> [soft]\n"
1646 " Sets an agent as no longer logged in.\n"
1647 " If 'soft' is specified, do not hangup existing calls.\n";
1648 return NULL;
1649 case CLI_GENERATE:
1650 return complete_agent_logoff_cmd(a->line, a->word, a->pos, a->n);
1653 if (a->argc < 3 || a->argc > 4)
1654 return CLI_SHOWUSAGE;
1655 if (a->argc == 4 && strcasecmp(a->argv[3], "soft"))
1656 return CLI_SHOWUSAGE;
1658 agent = a->argv[2] + 6;
1659 ret = agent_logoff(agent, a->argc == 4);
1660 if (ret == 0)
1661 ast_cli(a->fd, "Logging out %s\n", agent);
1663 return CLI_SUCCESS;
1667 * Sets an agent as no longer logged in in the Manager API.
1668 * It is registered on load_module() and it gets called by the manager backend.
1669 * \param s
1670 * \param m
1671 * \returns
1672 * \sa action_agents(), load_module().
1674 static int action_agent_logoff(struct mansession *s, const struct message *m)
1676 const char *agent = astman_get_header(m, "Agent");
1677 const char *soft_s = astman_get_header(m, "Soft"); /* "true" is don't hangup */
1678 int soft;
1679 int ret; /* return value of agent_logoff */
1681 if (ast_strlen_zero(agent)) {
1682 astman_send_error(s, m, "No agent specified");
1683 return 0;
1686 soft = ast_true(soft_s) ? 1 : 0;
1687 ret = agent_logoff(agent, soft);
1688 if (ret == 0)
1689 astman_send_ack(s, m, "Agent logged out");
1690 else
1691 astman_send_error(s, m, "No such agent");
1693 return 0;
1696 static char *complete_agent_logoff_cmd(const char *line, const char *word, int pos, int state)
1698 char *ret = NULL;
1700 if (pos == 2) {
1701 struct agent_pvt *p;
1702 char name[AST_MAX_AGENT];
1703 int which = 0, len = strlen(word);
1705 AST_LIST_LOCK(&agents);
1706 AST_LIST_TRAVERSE(&agents, p, list) {
1707 snprintf(name, sizeof(name), "Agent/%s", p->agent);
1708 if (!strncasecmp(word, name, len) && p->loginstart && ++which > state) {
1709 ret = ast_strdup(name);
1710 break;
1713 AST_LIST_UNLOCK(&agents);
1714 } else if (pos == 3 && state == 0)
1715 return ast_strdup("soft");
1717 return ret;
1721 * Show agents in cli.
1723 static char *agents_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1725 struct agent_pvt *p;
1726 char username[AST_MAX_BUF];
1727 char location[AST_MAX_BUF] = "";
1728 char talkingto[AST_MAX_BUF] = "";
1729 char music[AST_MAX_BUF];
1730 int count_agents = 0; /*!< Number of agents configured */
1731 int online_agents = 0; /*!< Number of online agents */
1732 int offline_agents = 0; /*!< Number of offline agents */
1734 switch (cmd) {
1735 case CLI_INIT:
1736 e->command = "agent show";
1737 e->usage =
1738 "Usage: agent show\n"
1739 " Provides summary information on agents.\n";
1740 return NULL;
1741 case CLI_GENERATE:
1742 return NULL;
1745 if (a->argc != 2)
1746 return CLI_SHOWUSAGE;
1748 AST_LIST_LOCK(&agents);
1749 AST_LIST_TRAVERSE(&agents, p, list) {
1750 ast_mutex_lock(&p->lock);
1751 if (p->pending) {
1752 if (p->group)
1753 ast_cli(a->fd, "-- Pending call to group %d\n", powerof(p->group));
1754 else
1755 ast_cli(a->fd, "-- Pending call to agent %s\n", p->agent);
1756 } else {
1757 if (!ast_strlen_zero(p->name))
1758 snprintf(username, sizeof(username), "(%s) ", p->name);
1759 else
1760 username[0] = '\0';
1761 if (p->chan) {
1762 snprintf(location, sizeof(location), "logged in on %s", p->chan->name);
1763 if (p->owner && ast_bridged_channel(p->owner))
1764 snprintf(talkingto, sizeof(talkingto), " talking to %s", ast_bridged_channel(p->owner)->name);
1765 else
1766 strcpy(talkingto, " is idle");
1767 online_agents++;
1768 } else if (!ast_strlen_zero(p->loginchan)) {
1769 if (ast_tvdiff_ms(ast_tvnow(), p->lastdisc) > 0 || !(p->lastdisc.tv_sec))
1770 snprintf(location, sizeof(location) - 20, "available at '%s'", p->loginchan);
1771 else
1772 snprintf(location, sizeof(location) - 20, "wrapping up at '%s'", p->loginchan);
1773 talkingto[0] = '\0';
1774 online_agents++;
1775 if (p->acknowledged)
1776 strncat(location, " (Confirmed)", sizeof(location) - strlen(location) - 1);
1777 } else {
1778 strcpy(location, "not logged in");
1779 talkingto[0] = '\0';
1780 offline_agents++;
1782 if (!ast_strlen_zero(p->moh))
1783 snprintf(music, sizeof(music), " (musiconhold is '%s')", p->moh);
1784 ast_cli(a->fd, "%-12.12s %s%s%s%s\n", p->agent,
1785 username, location, talkingto, music);
1786 count_agents++;
1788 ast_mutex_unlock(&p->lock);
1790 AST_LIST_UNLOCK(&agents);
1791 if ( !count_agents )
1792 ast_cli(a->fd, "No Agents are configured in %s\n",config);
1793 else
1794 ast_cli(a->fd, "%d agents configured [%d online , %d offline]\n",count_agents, online_agents, offline_agents);
1795 ast_cli(a->fd, "\n");
1797 return CLI_SUCCESS;
1801 static char *agents_show_online(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1803 struct agent_pvt *p;
1804 char username[AST_MAX_BUF];
1805 char location[AST_MAX_BUF] = "";
1806 char talkingto[AST_MAX_BUF] = "";
1807 char music[AST_MAX_BUF];
1808 int count_agents = 0; /* Number of agents configured */
1809 int online_agents = 0; /* Number of online agents */
1810 int agent_status = 0; /* 0 means offline, 1 means online */
1812 switch (cmd) {
1813 case CLI_INIT:
1814 e->command = "agent show online";
1815 e->usage =
1816 "Usage: agent show online\n"
1817 " Provides a list of all online agents.\n";
1818 return NULL;
1819 case CLI_GENERATE:
1820 return NULL;
1823 if (a->argc != 3)
1824 return CLI_SHOWUSAGE;
1826 AST_LIST_LOCK(&agents);
1827 AST_LIST_TRAVERSE(&agents, p, list) {
1828 agent_status = 0; /* reset it to offline */
1829 ast_mutex_lock(&p->lock);
1830 if (!ast_strlen_zero(p->name))
1831 snprintf(username, sizeof(username), "(%s) ", p->name);
1832 else
1833 username[0] = '\0';
1834 if (p->chan) {
1835 snprintf(location, sizeof(location), "logged in on %s", p->chan->name);
1836 if (p->owner && ast_bridged_channel(p->owner))
1837 snprintf(talkingto, sizeof(talkingto), " talking to %s", ast_bridged_channel(p->owner)->name);
1838 else
1839 strcpy(talkingto, " is idle");
1840 agent_status = 1;
1841 online_agents++;
1842 } else if (!ast_strlen_zero(p->loginchan)) {
1843 snprintf(location, sizeof(location) - 20, "available at '%s'", p->loginchan);
1844 talkingto[0] = '\0';
1845 agent_status = 1;
1846 online_agents++;
1847 if (p->acknowledged)
1848 strncat(location, " (Confirmed)", sizeof(location) - strlen(location) - 1);
1850 if (!ast_strlen_zero(p->moh))
1851 snprintf(music, sizeof(music), " (musiconhold is '%s')", p->moh);
1852 if (agent_status)
1853 ast_cli(a->fd, "%-12.12s %s%s%s%s\n", p->agent, username, location, talkingto, music);
1854 count_agents++;
1855 ast_mutex_unlock(&p->lock);
1857 AST_LIST_UNLOCK(&agents);
1858 if (!count_agents)
1859 ast_cli(a->fd, "No Agents are configured in %s\n", config);
1860 else
1861 ast_cli(a->fd, "%d agents online\n", online_agents);
1862 ast_cli(a->fd, "\n");
1863 return CLI_SUCCESS;
1866 static const char agent_logoff_usage[] =
1867 "Usage: agent logoff <channel> [soft]\n"
1868 " Sets an agent as no longer logged in.\n"
1869 " If 'soft' is specified, do not hangup existing calls.\n";
1871 static struct ast_cli_entry cli_agents[] = {
1872 AST_CLI_DEFINE(agents_show, "Show status of agents"),
1873 AST_CLI_DEFINE(agents_show_online, "Show all online agents"),
1874 AST_CLI_DEFINE(agent_logoff_cmd, "Sets an agent offline"),
1878 * Called by the AgentLogin application (from the dial plan).
1880 * \brief Log in agent application.
1882 * \param chan
1883 * \param data
1884 * \returns
1885 * \sa agentmonitoroutgoing_exec(), load_module().
1887 static int login_exec(struct ast_channel *chan, void *data)
1889 int res=0;
1890 int tries = 0;
1891 int max_login_tries = maxlogintries;
1892 struct agent_pvt *p;
1893 struct ast_module_user *u;
1894 int login_state = 0;
1895 char user[AST_MAX_AGENT] = "";
1896 char pass[AST_MAX_AGENT];
1897 char agent[AST_MAX_AGENT] = "";
1898 char xpass[AST_MAX_AGENT] = "";
1899 char *errmsg;
1900 char *parse;
1901 AST_DECLARE_APP_ARGS(args,
1902 AST_APP_ARG(agent_id);
1903 AST_APP_ARG(options);
1904 AST_APP_ARG(extension);
1906 const char *tmpoptions = NULL;
1907 int play_announcement = 1;
1908 char agent_goodbye[AST_MAX_FILENAME_LEN];
1909 int update_cdr = updatecdr;
1910 char *filename = "agent-loginok";
1912 u = ast_module_user_add(chan);
1914 parse = ast_strdupa(data);
1916 AST_STANDARD_APP_ARGS(args, parse);
1918 ast_copy_string(agent_goodbye, agentgoodbye, sizeof(agent_goodbye));
1920 ast_channel_lock(chan);
1921 /* Set Channel Specific Login Overrides */
1922 if (!ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENTLMAXLOGINTRIES"))) {
1923 max_login_tries = atoi(pbx_builtin_getvar_helper(chan, "AGENTMAXLOGINTRIES"));
1924 if (max_login_tries < 0)
1925 max_login_tries = 0;
1926 tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTMAXLOGINTRIES");
1927 ast_verb(3, "Saw variable AGENTMAXLOGINTRIES=%s, setting max_login_tries to: %d on Channel '%s'.\n",tmpoptions,max_login_tries,chan->name);
1929 if (!ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENTUPDATECDR"))) {
1930 if (ast_true(pbx_builtin_getvar_helper(chan, "AGENTUPDATECDR")))
1931 update_cdr = 1;
1932 else
1933 update_cdr = 0;
1934 tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTUPDATECDR");
1935 ast_verb(3, "Saw variable AGENTUPDATECDR=%s, setting update_cdr to: %d on Channel '%s'.\n",tmpoptions,update_cdr,chan->name);
1937 if (!ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENTGOODBYE"))) {
1938 strcpy(agent_goodbye, pbx_builtin_getvar_helper(chan, "AGENTGOODBYE"));
1939 tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTGOODBYE");
1940 ast_verb(3, "Saw variable AGENTGOODBYE=%s, setting agent_goodbye to: %s on Channel '%s'.\n",tmpoptions,agent_goodbye,chan->name);
1942 ast_channel_unlock(chan);
1943 /* End Channel Specific Login Overrides */
1945 if (!ast_strlen_zero(args.options)) {
1946 if (strchr(args.options, 's')) {
1947 play_announcement = 0;
1951 if (chan->_state != AST_STATE_UP)
1952 res = ast_answer(chan);
1953 if (!res) {
1954 if (!ast_strlen_zero(args.agent_id))
1955 ast_copy_string(user, args.agent_id, AST_MAX_AGENT);
1956 else
1957 res = ast_app_getdata(chan, "agent-user", user, sizeof(user) - 1, 0);
1959 while (!res && (max_login_tries==0 || tries < max_login_tries)) {
1960 tries++;
1961 /* Check for password */
1962 AST_LIST_LOCK(&agents);
1963 AST_LIST_TRAVERSE(&agents, p, list) {
1964 if (!strcmp(p->agent, user) && !p->pending)
1965 ast_copy_string(xpass, p->password, sizeof(xpass));
1967 AST_LIST_UNLOCK(&agents);
1968 if (!res) {
1969 if (!ast_strlen_zero(xpass))
1970 res = ast_app_getdata(chan, "agent-pass", pass, sizeof(pass) - 1, 0);
1971 else
1972 pass[0] = '\0';
1974 errmsg = "agent-incorrect";
1976 #if 0
1977 ast_log(LOG_NOTICE, "user: %s, pass: %s\n", user, pass);
1978 #endif
1980 /* Check again for accuracy */
1981 AST_LIST_LOCK(&agents);
1982 AST_LIST_TRAVERSE(&agents, p, list) {
1983 int unlock_channel = 1;
1984 ast_channel_lock(chan);
1985 ast_mutex_lock(&p->lock);
1986 if (!strcmp(p->agent, user) &&
1987 !strcmp(p->password, pass) && !p->pending) {
1988 login_state = 1; /* Successful Login */
1990 /* Ensure we can't be gotten until we're done */
1991 p->lastdisc = ast_tvnow();
1992 p->lastdisc.tv_sec++;
1994 /* Set Channel Specific Agent Overrides */
1995 if (!ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENTACKCALL"))) {
1996 if (!strcasecmp(pbx_builtin_getvar_helper(chan, "AGENTACKCALL"), "always"))
1997 p->ackcall = 2;
1998 else if (ast_true(pbx_builtin_getvar_helper(chan, "AGENTACKCALL")))
1999 p->ackcall = 1;
2000 else
2001 p->ackcall = 0;
2002 tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTACKCALL");
2003 ast_verb(3, "Saw variable AGENTACKCALL=%s, setting ackcall to: %d for Agent '%s'.\n", tmpoptions, p->ackcall, p->agent);
2004 } else {
2005 p->ackcall = ackcall;
2007 if (!ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF"))) {
2008 p->autologoff = atoi(pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF"));
2009 if (p->autologoff < 0)
2010 p->autologoff = 0;
2011 tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF");
2012 ast_verb(3, "Saw variable AGENTAUTOLOGOFF=%s, setting autologff to: %d for Agent '%s'.\n", tmpoptions, p->autologoff, p->agent);
2013 } else {
2014 p->autologoff = autologoff;
2016 if (!ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME"))) {
2017 p->wrapuptime = atoi(pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME"));
2018 if (p->wrapuptime < 0)
2019 p->wrapuptime = 0;
2020 tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME");
2021 ast_verb(3, "Saw variable AGENTWRAPUPTIME=%s, setting wrapuptime to: %d for Agent '%s'.\n", tmpoptions, p->wrapuptime, p->agent);
2022 } else {
2023 p->wrapuptime = wrapuptime;
2025 tmpoptions = pbx_builtin_getvar_helper(chan, "AGENTACCEPTDMTF");
2026 if (!ast_strlen_zero(tmpoptions)) {
2027 p->acceptdtmf = *tmpoptions;
2028 ast_verb(3, "Saw variable AGENTACCEPTDTMF=%s, setting acceptdtmf to: %c for Agent '%s'.\n", tmpoptions, p->acceptdtmf, p->agent);
2030 tmpoptions = pbx_builtin_getvar_helper(chan, "AGENTENDDTMF");
2031 if (!ast_strlen_zero(tmpoptions)) {
2032 p->enddtmf = *tmpoptions;
2033 ast_verb(3, "Saw variable AGENTENDDTMF=%s, setting enddtmf to: %c for Agent '%s'.\n", tmpoptions, p->enddtmf, p->agent);
2035 ast_channel_unlock(chan);
2036 unlock_channel = 0;
2037 /* End Channel Specific Agent Overrides */
2038 if (!p->chan) {
2039 long logintime;
2040 snprintf(agent, sizeof(agent), "Agent/%s", p->agent);
2042 p->loginchan[0] = '\0';
2043 p->logincallerid[0] = '\0';
2044 p->acknowledged = 0;
2046 ast_mutex_unlock(&p->lock);
2047 AST_LIST_UNLOCK(&agents);
2048 if( !res && play_announcement==1 )
2049 res = ast_streamfile(chan, filename, chan->language);
2050 if (!res)
2051 ast_waitstream(chan, "");
2052 AST_LIST_LOCK(&agents);
2053 ast_mutex_lock(&p->lock);
2054 if (!res) {
2055 res = ast_set_read_format(chan, ast_best_codec(chan->nativeformats));
2056 if (res)
2057 ast_log(LOG_WARNING, "Unable to set read format to %d\n", ast_best_codec(chan->nativeformats));
2059 if (!res) {
2060 res = ast_set_write_format(chan, ast_best_codec(chan->nativeformats));
2061 if (res)
2062 ast_log(LOG_WARNING, "Unable to set write format to %d\n", ast_best_codec(chan->nativeformats));
2064 /* Check once more just in case */
2065 if (p->chan)
2066 res = -1;
2067 if (!res) {
2068 ast_indicate_data(chan, AST_CONTROL_HOLD,
2069 S_OR(p->moh, NULL),
2070 !ast_strlen_zero(p->moh) ? strlen(p->moh) + 1 : 0);
2071 if (p->loginstart == 0)
2072 time(&p->loginstart);
2073 manager_event(EVENT_FLAG_AGENT, "Agentlogin",
2074 "Agent: %s\r\n"
2075 "Channel: %s\r\n"
2076 "Uniqueid: %s\r\n",
2077 p->agent, chan->name, chan->uniqueid);
2078 if (update_cdr && chan->cdr)
2079 snprintf(chan->cdr->channel, sizeof(chan->cdr->channel), "Agent/%s", p->agent);
2080 ast_queue_log("NONE", chan->uniqueid, agent, "AGENTLOGIN", "%s", chan->name);
2081 ast_verb(2, "Agent '%s' logged in (format %s/%s)\n", p->agent,
2082 ast_getformatname(chan->readformat), ast_getformatname(chan->writeformat));
2083 /* Login this channel and wait for it to go away */
2084 p->chan = chan;
2085 if (p->ackcall > 1)
2086 check_beep(p, 0);
2087 else
2088 check_availability(p, 0);
2089 ast_mutex_unlock(&p->lock);
2090 AST_LIST_UNLOCK(&agents);
2091 ast_devstate_changed(AST_DEVICE_NOT_INUSE, "Agent/%s", p->agent);
2092 while (res >= 0) {
2093 ast_mutex_lock(&p->lock);
2094 if (p->deferlogoff && p->chan) {
2095 ast_softhangup(p->chan, AST_SOFTHANGUP_EXPLICIT);
2096 p->deferlogoff = 0;
2098 if (p->chan != chan)
2099 res = -1;
2100 ast_mutex_unlock(&p->lock);
2101 /* Yield here so other interested threads can kick in. */
2102 sched_yield();
2103 if (res)
2104 break;
2106 AST_LIST_LOCK(&agents);
2107 ast_mutex_lock(&p->lock);
2108 if (p->lastdisc.tv_sec) {
2109 if (ast_tvdiff_ms(ast_tvnow(), p->lastdisc) > 0) {
2110 ast_debug(1, "Wrapup time for %s expired!\n", p->agent);
2111 p->lastdisc = ast_tv(0, 0);
2112 ast_devstate_changed(AST_DEVICE_NOT_INUSE, "Agent/%s", p->agent);
2113 if (p->ackcall > 1)
2114 check_beep(p, 0);
2115 else
2116 check_availability(p, 0);
2119 ast_mutex_unlock(&p->lock);
2120 AST_LIST_UNLOCK(&agents);
2121 /* Synchronize channel ownership between call to agent and itself. */
2122 ast_mutex_lock( &p->app_lock );
2123 ast_mutex_lock(&p->lock);
2124 p->owning_app = pthread_self();
2125 ast_mutex_unlock(&p->lock);
2126 if (p->ackcall > 1)
2127 res = agent_ack_sleep(p);
2128 else
2129 res = ast_safe_sleep_conditional( chan, 1000, agent_cont_sleep, p );
2130 ast_mutex_unlock( &p->app_lock );
2131 if ((p->ackcall > 1) && (res == 1)) {
2132 AST_LIST_LOCK(&agents);
2133 ast_mutex_lock(&p->lock);
2134 check_availability(p, 0);
2135 ast_mutex_unlock(&p->lock);
2136 AST_LIST_UNLOCK(&agents);
2137 res = 0;
2139 sched_yield();
2141 ast_mutex_lock(&p->lock);
2142 if (res && p->owner)
2143 ast_log(LOG_WARNING, "Huh? We broke out when there was still an owner?\n");
2144 /* Log us off if appropriate */
2145 if (p->chan == chan) {
2146 p->chan = NULL;
2147 p->inherited_devicestate = -1;
2149 p->acknowledged = 0;
2150 logintime = time(NULL) - p->loginstart;
2151 p->loginstart = 0;
2152 ast_mutex_unlock(&p->lock);
2153 manager_event(EVENT_FLAG_AGENT, "Agentlogoff",
2154 "Agent: %s\r\n"
2155 "Logintime: %ld\r\n"
2156 "Uniqueid: %s\r\n",
2157 p->agent, logintime, chan->uniqueid);
2158 ast_queue_log("NONE", chan->uniqueid, agent, "AGENTLOGOFF", "%s|%ld", chan->name, logintime);
2159 ast_verb(2, "Agent '%s' logged out\n", p->agent);
2160 /* If there is no owner, go ahead and kill it now */
2161 ast_devstate_changed(AST_DEVICE_UNAVAILABLE, "Agent/%s", p->agent);
2162 if (p->dead && !p->owner) {
2163 ast_mutex_destroy(&p->lock);
2164 ast_mutex_destroy(&p->app_lock);
2165 ast_free(p);
2168 else {
2169 ast_mutex_unlock(&p->lock);
2170 p = NULL;
2172 res = -1;
2173 } else {
2174 ast_mutex_unlock(&p->lock);
2175 errmsg = "agent-alreadyon";
2176 p = NULL;
2178 break;
2180 ast_mutex_unlock(&p->lock);
2181 if (unlock_channel) {
2182 ast_channel_unlock(chan);
2185 if (!p)
2186 AST_LIST_UNLOCK(&agents);
2188 if (!res && (max_login_tries==0 || tries < max_login_tries))
2189 res = ast_app_getdata(chan, errmsg, user, sizeof(user) - 1, 0);
2192 if (!res)
2193 res = ast_safe_sleep(chan, 500);
2195 ast_module_user_remove(u);
2197 return -1;
2201 * \brief Called by the AgentMonitorOutgoing application (from the dial plan).
2203 * \param chan
2204 * \param data
2205 * \returns
2206 * \sa login_exec(), load_module().
2208 static int agentmonitoroutgoing_exec(struct ast_channel *chan, void *data)
2210 int exitifnoagentid = 0;
2211 int nowarnings = 0;
2212 int changeoutgoing = 0;
2213 int res = 0;
2214 char agent[AST_MAX_AGENT];
2216 if (data) {
2217 if (strchr(data, 'd'))
2218 exitifnoagentid = 1;
2219 if (strchr(data, 'n'))
2220 nowarnings = 1;
2221 if (strchr(data, 'c'))
2222 changeoutgoing = 1;
2224 if (chan->cid.cid_num) {
2225 const char *tmp;
2226 char agentvar[AST_MAX_BUF];
2227 snprintf(agentvar, sizeof(agentvar), "%s_%s", GETAGENTBYCALLERID, chan->cid.cid_num);
2228 if ((tmp = pbx_builtin_getvar_helper(NULL, agentvar))) {
2229 struct agent_pvt *p;
2230 ast_copy_string(agent, tmp, sizeof(agent));
2231 AST_LIST_LOCK(&agents);
2232 AST_LIST_TRAVERSE(&agents, p, list) {
2233 if (!strcasecmp(p->agent, tmp)) {
2234 if (changeoutgoing) snprintf(chan->cdr->channel, sizeof(chan->cdr->channel), "Agent/%s", p->agent);
2235 __agent_start_monitoring(chan, p, 1);
2236 break;
2239 AST_LIST_UNLOCK(&agents);
2241 } else {
2242 res = -1;
2243 if (!nowarnings)
2244 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);
2246 } else {
2247 res = -1;
2248 if (!nowarnings)
2249 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");
2251 if (res) {
2252 if (exitifnoagentid)
2253 return res;
2255 return 0;
2259 * \brief Dump AgentCallbackLogin agents to the ASTdb database for persistence
2261 static void dump_agents(void)
2263 struct agent_pvt *cur_agent = NULL;
2264 char buf[256];
2266 AST_LIST_TRAVERSE(&agents, cur_agent, list) {
2267 if (cur_agent->chan)
2268 continue;
2270 if (!ast_strlen_zero(cur_agent->loginchan)) {
2271 snprintf(buf, sizeof(buf), "%s;%s", cur_agent->loginchan, cur_agent->logincallerid);
2272 if (ast_db_put(pa_family, cur_agent->agent, buf))
2273 ast_log(LOG_WARNING, "failed to create persistent entry in ASTdb for %s!\n", buf);
2274 else
2275 ast_debug(1, "Saved Agent: %s on %s\n", cur_agent->agent, cur_agent->loginchan);
2276 } else {
2277 /* Delete - no agent or there is an error */
2278 ast_db_del(pa_family, cur_agent->agent);
2284 * \brief Reload the persistent agents from astdb.
2286 static void reload_agents(void)
2288 char *agent_num;
2289 struct ast_db_entry *db_tree;
2290 struct ast_db_entry *entry;
2291 struct agent_pvt *cur_agent;
2292 char agent_data[256];
2293 char *parse;
2294 char *agent_chan;
2295 char *agent_callerid;
2297 db_tree = ast_db_gettree(pa_family, NULL);
2299 AST_LIST_LOCK(&agents);
2300 for (entry = db_tree; entry; entry = entry->next) {
2301 agent_num = entry->key + strlen(pa_family) + 2;
2302 AST_LIST_TRAVERSE(&agents, cur_agent, list) {
2303 ast_mutex_lock(&cur_agent->lock);
2304 if (strcmp(agent_num, cur_agent->agent) == 0)
2305 break;
2306 ast_mutex_unlock(&cur_agent->lock);
2308 if (!cur_agent) {
2309 ast_db_del(pa_family, agent_num);
2310 continue;
2311 } else
2312 ast_mutex_unlock(&cur_agent->lock);
2313 if (!ast_db_get(pa_family, agent_num, agent_data, sizeof(agent_data)-1)) {
2314 ast_debug(1, "Reload Agent from AstDB: %s on %s\n", cur_agent->agent, agent_data);
2315 parse = agent_data;
2316 agent_chan = strsep(&parse, ";");
2317 agent_callerid = strsep(&parse, ";");
2318 ast_copy_string(cur_agent->loginchan, agent_chan, sizeof(cur_agent->loginchan));
2319 if (agent_callerid) {
2320 ast_copy_string(cur_agent->logincallerid, agent_callerid, sizeof(cur_agent->logincallerid));
2321 set_agentbycallerid(cur_agent->logincallerid, cur_agent->agent);
2322 } else
2323 cur_agent->logincallerid[0] = '\0';
2324 if (cur_agent->loginstart == 0)
2325 time(&cur_agent->loginstart);
2326 ast_devstate_changed(AST_DEVICE_UNKNOWN, "Agent/%s", cur_agent->agent);
2329 AST_LIST_UNLOCK(&agents);
2330 if (db_tree) {
2331 ast_log(LOG_NOTICE, "Agents successfully reloaded from database.\n");
2332 ast_db_freetree(db_tree);
2336 /*! \brief Part of PBX channel interface */
2337 static int agent_devicestate(void *data)
2339 struct agent_pvt *p;
2340 char *s;
2341 ast_group_t groupmatch;
2342 int groupoff;
2343 int res = AST_DEVICE_INVALID;
2345 s = data;
2346 if ((s[0] == '@') && (sscanf(s + 1, "%d", &groupoff) == 1))
2347 groupmatch = (1 << groupoff);
2348 else if ((s[0] == ':') && (sscanf(s + 1, "%d", &groupoff) == 1)) {
2349 groupmatch = (1 << groupoff);
2350 } else
2351 groupmatch = 0;
2353 /* Check actual logged in agents first */
2354 AST_LIST_LOCK(&agents);
2355 AST_LIST_TRAVERSE(&agents, p, list) {
2356 ast_mutex_lock(&p->lock);
2357 if (!p->pending && ((groupmatch && (p->group & groupmatch)) || !strcmp(data, p->agent))) {
2358 if (p->owner) {
2359 if (res != AST_DEVICE_INUSE)
2360 res = AST_DEVICE_BUSY;
2361 } else if (p->inherited_devicestate > -1) {
2362 res = p->inherited_devicestate;
2363 } else {
2364 if (res == AST_DEVICE_BUSY)
2365 res = AST_DEVICE_INUSE;
2366 if (p->chan || !ast_strlen_zero(p->loginchan)) {
2367 if (res == AST_DEVICE_INVALID)
2368 res = AST_DEVICE_UNKNOWN;
2369 } else if (res == AST_DEVICE_INVALID)
2370 res = AST_DEVICE_UNAVAILABLE;
2372 if (!strcmp(data, p->agent)) {
2373 ast_mutex_unlock(&p->lock);
2374 break;
2377 ast_mutex_unlock(&p->lock);
2379 AST_LIST_UNLOCK(&agents);
2380 return res;
2384 * \note This function expects the agent list to be locked
2386 static struct agent_pvt *find_agent(char *agentid)
2388 struct agent_pvt *cur;
2390 AST_LIST_TRAVERSE(&agents, cur, list) {
2391 if (!strcmp(cur->agent, agentid))
2392 break;
2395 return cur;
2398 static int function_agent(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
2400 char *parse;
2401 AST_DECLARE_APP_ARGS(args,
2402 AST_APP_ARG(agentid);
2403 AST_APP_ARG(item);
2405 char *tmp;
2406 struct agent_pvt *agent;
2408 buf[0] = '\0';
2410 if (ast_strlen_zero(data)) {
2411 ast_log(LOG_WARNING, "The AGENT function requires an argument - agentid!\n");
2412 return -1;
2415 parse = ast_strdupa(data);
2417 AST_NONSTANDARD_APP_ARGS(args, parse, ':');
2418 if (!args.item)
2419 args.item = "status";
2421 AST_LIST_LOCK(&agents);
2423 if (!(agent = find_agent(args.agentid))) {
2424 AST_LIST_UNLOCK(&agents);
2425 ast_log(LOG_WARNING, "Agent '%s' not found!\n", args.agentid);
2426 return -1;
2429 if (!strcasecmp(args.item, "status")) {
2430 char *status = "LOGGEDOUT";
2431 if (agent->chan || !ast_strlen_zero(agent->loginchan))
2432 status = "LOGGEDIN";
2433 ast_copy_string(buf, status, len);
2434 } else if (!strcasecmp(args.item, "password"))
2435 ast_copy_string(buf, agent->password, len);
2436 else if (!strcasecmp(args.item, "name"))
2437 ast_copy_string(buf, agent->name, len);
2438 else if (!strcasecmp(args.item, "mohclass"))
2439 ast_copy_string(buf, agent->moh, len);
2440 else if (!strcasecmp(args.item, "channel")) {
2441 if (agent->chan) {
2442 ast_copy_string(buf, agent->chan->name, len);
2443 tmp = strrchr(buf, '-');
2444 if (tmp)
2445 *tmp = '\0';
2447 } else if (!strcasecmp(args.item, "exten"))
2448 ast_copy_string(buf, agent->loginchan, len);
2450 AST_LIST_UNLOCK(&agents);
2452 return 0;
2455 struct ast_custom_function agent_function = {
2456 .name = "AGENT",
2457 .synopsis = "Gets information about an Agent",
2458 .syntax = "AGENT(<agentid>[:item])",
2459 .read = function_agent,
2460 .desc = "The valid items to retrieve are:\n"
2461 "- status (default) The status of the agent\n"
2462 " LOGGEDIN | LOGGEDOUT\n"
2463 "- password The password of the agent\n"
2464 "- name The name of the agent\n"
2465 "- mohclass MusicOnHold class\n"
2466 "- exten The callback extension for the Agent (AgentCallbackLogin)\n"
2467 "- channel The name of the active channel for the Agent (AgentLogin)\n"
2472 * \brief Initialize the Agents module.
2473 * This function is being called by Asterisk when loading the module.
2474 * Among other things it registers applications, cli commands and reads the cofiguration file.
2476 * \returns int Always 0.
2478 static int load_module(void)
2480 /* Make sure we can register our agent channel type */
2481 if (ast_channel_register(&agent_tech)) {
2482 ast_log(LOG_ERROR, "Unable to register channel class 'Agent'\n");
2483 return AST_MODULE_LOAD_FAILURE;
2485 /* Read in the config */
2486 if (!read_agent_config(0))
2487 return AST_MODULE_LOAD_DECLINE;
2488 if (persistent_agents)
2489 reload_agents();
2490 /* Dialplan applications */
2491 ast_register_application(app, login_exec, synopsis, descrip);
2492 ast_register_application(app3, agentmonitoroutgoing_exec, synopsis3, descrip3);
2494 /* Manager commands */
2495 ast_manager_register2("Agents", EVENT_FLAG_AGENT, action_agents, "Lists agents and their status", mandescr_agents);
2496 ast_manager_register2("AgentLogoff", EVENT_FLAG_AGENT, action_agent_logoff, "Sets an agent as no longer logged in", mandescr_agent_logoff);
2498 /* CLI Commands */
2499 ast_cli_register_multiple(cli_agents, sizeof(cli_agents) / sizeof(struct ast_cli_entry));
2501 /* Dialplan Functions */
2502 ast_custom_function_register(&agent_function);
2504 agent_devicestate_sub = ast_event_subscribe(AST_EVENT_DEVICE_STATE,
2505 agent_devicestate_cb, NULL, AST_EVENT_IE_END);
2507 return AST_MODULE_LOAD_SUCCESS;
2510 static int reload(void)
2512 if (!read_agent_config(1)) {
2513 if (persistent_agents)
2514 reload_agents();
2516 return 0;
2519 static int unload_module(void)
2521 struct agent_pvt *p;
2522 /* First, take us out of the channel loop */
2523 ast_channel_unregister(&agent_tech);
2524 /* Delete devicestate subscription */
2525 agent_devicestate_sub = ast_event_unsubscribe(agent_devicestate_sub);
2526 /* Unregister dialplan functions */
2527 ast_custom_function_unregister(&agent_function);
2528 /* Unregister CLI commands */
2529 ast_cli_unregister_multiple(cli_agents, sizeof(cli_agents) / sizeof(struct ast_cli_entry));
2530 /* Unregister dialplan applications */
2531 ast_unregister_application(app);
2532 ast_unregister_application(app3);
2533 /* Unregister manager command */
2534 ast_manager_unregister("Agents");
2535 ast_manager_unregister("AgentLogoff");
2536 /* Unregister channel */
2537 AST_LIST_LOCK(&agents);
2538 /* Hangup all interfaces if they have an owner */
2539 while ((p = AST_LIST_REMOVE_HEAD(&agents, list))) {
2540 if (p->owner)
2541 ast_softhangup(p->owner, AST_SOFTHANGUP_APPUNLOAD);
2542 ast_free(p);
2544 AST_LIST_UNLOCK(&agents);
2545 return 0;
2548 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Agent Proxy Channel",
2549 .load = load_module,
2550 .unload = unload_module,
2551 .reload = reload,