Let's also include aclocal.m4
[asterisk-bristuff.git] / channels / chan_agent.c
blobefba49e77dce771281cee19ec8cd3100f22c28a5
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 <stdio.h>
42 #include <string.h>
43 #include <errno.h>
44 #include <unistd.h>
45 #include <sys/socket.h>
46 #include <stdlib.h>
47 #include <fcntl.h>
48 #include <netdb.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"
95 "the star key.\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"
103 "context).\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"
114 "\nReturn value:\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"
117 "\nOptions:\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"
127 "Variables: NONE\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;
159 static int ackcall;
160 static int endcall;
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. */
178 struct agent_pvt {
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 { \
210 if (p->chan) {\
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); \
224 } while(0)
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 { \
231 int x; \
232 if (p->chan) { \
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]; \
239 } while(0)
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 = {
263 .type = "Agent",
264 .description = tdesc,
265 .capabilities = -1,
266 .requester = agent_request,
267 .devicestate = agent_devicestate,
268 .send_digit_begin = agent_digit_begin,
269 .send_digit_end = agent_digit_end,
270 .call = agent_call,
271 .hangup = agent_hangup,
272 .answer = agent_answer,
273 .read = agent_read,
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)
288 int res, i;
289 struct agent_pvt *p;
290 char basename[AST_CHANNEL_NAME], *tmp;
292 /* Skip Agent status */
293 if (!strncasecmp(dev, "Agent/", 6)) {
294 return 0;
297 /* Try to be safe, but don't deadlock */
298 for (i = 0; i < 10; i++) {
299 if ((res = AST_LIST_TRYLOCK(&agents)) == 0) {
300 break;
303 if (res) {
304 return -1;
307 AST_LIST_TRAVERSE(&agents, p, list) {
308 ast_mutex_lock(&p->lock);
309 if (p->chan) {
310 ast_copy_string(basename, p->chan->name, sizeof(basename));
311 if ((tmp = strrchr(basename, '-'))) {
312 *tmp = '\0';
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);
322 return 0;
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)
335 char *parse;
336 AST_DECLARE_APP_ARGS(args,
337 AST_APP_ARG(agt);
338 AST_APP_ARG(password);
339 AST_APP_ARG(name);
341 char *password = NULL;
342 char *name = NULL;
343 char *agt = NULL;
344 struct agent_pvt *p;
346 parse = ast_strdupa(agent);
348 /* Extract username (agt), password and name from agent (args). */
349 AST_NONSTANDARD_APP_ARGS(args, parse, ',');
351 if(args.argc == 0) {
352 ast_log(LOG_WARNING, "A blank agent line!\n");
353 return NULL;
356 if(ast_strlen_zero(args.agt) ) {
357 ast_log(LOG_WARNING, "An agent line with no agentid!\n");
358 return NULL;
359 } else
360 agt = args.agt;
362 if(!ast_strlen_zero(args.password)) {
363 password = args.password;
364 while (*password && *password < 33) password++;
366 if(!ast_strlen_zero(args.name)) {
367 name = 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))
374 break;
376 if (!p) {
377 // Build the agent.
378 if (!(p = ast_calloc(1, sizeof(*p))))
379 return NULL;
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;
385 p->group = group;
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;
411 if (pending)
412 p->dead = 1;
413 else
414 p->dead = 0;
415 return p;
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.
422 * \returns Always 0.
424 static int agent_cleanup(struct agent_pvt *p)
426 struct ast_channel *chan = p->owner;
427 p->owner = NULL;
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);
432 if (chan)
433 ast_channel_free(chan);
434 if (p->dead) {
435 ast_mutex_destroy(&p->lock);
436 ast_mutex_destroy(&p->app_lock);
437 free(p);
439 return 0;
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");
447 return -1;
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];
454 int res = -1;
455 if (!p)
456 return -1;
457 if (!ast->monitor) {
458 snprintf(filename, sizeof(filename), "agent-%s-%s",p->agent, ast->uniqueid);
459 /* substitute . for - */
460 if ((pointer = strchr(filename, '.')))
461 *pointer = '-';
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);
466 #if 0
467 ast_verbose("name is %s, link is %s\n",tmp, tmp2);
468 #endif
469 if (!ast->cdr)
470 ast->cdr = ast_cdr_alloc();
471 ast_cdr_setuserfield(ast, tmp2);
472 res = 0;
473 } else
474 ast_log(LOG_ERROR, "Recording already started on that call.\n");
475 return res;
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 };
488 const char *status;
489 ast_mutex_lock(&p->lock);
490 CHECK_FORMATS(ast, p);
491 if (p->chan) {
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);
495 } else
496 f = &ast_null_frame;
497 if (!f) {
498 /* If there's a channel, hang it up (if it's on a callback) make it NULL */
499 if (p->chan) {
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)) {
504 if (p->chan)
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;
510 p->loginstart = 0;
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)
514 dump_agents();
517 status = pbx_builtin_getvar_helper(p->chan, "CHANLOCALSTATUS");
518 if (autologoffunavail && status && !strcasecmp(status, "CHANUNAVAIL")) {
519 long logintime = time(NULL) - p->loginstart;
520 p->loginstart = 0;
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");
524 ast_hangup(p->chan);
525 if (p->wrapuptime && p->acknowledged)
526 p->lastdisc = ast_tvadd(ast_tvnow(), ast_samp2tv(p->wrapuptime, 1000));
528 p->chan = NULL;
529 p->inherited_devicestate = -1;
530 ast_device_state_changed("Agent/%s", p->agent);
531 p->acknowledged = 0;
533 } else {
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))
537 p->acknowledged = 1;
538 switch (f->frametype) {
539 case AST_FRAME_CONTROL:
540 if (f->subclass == AST_CONTROL_ANSWER) {
541 if (p->ackcall) {
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 */
545 ast_frfree(f);
546 f = &ast_null_frame;
547 } else {
548 p->acknowledged = 1;
549 /* Use the builtin answer frame for the
550 recording start check below. */
551 ast_frfree(f);
552 f = &answer_frame;
555 break;
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)){
559 ast_frfree(f);
560 f = &ast_null_frame;
562 break;
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);
567 p->acknowledged = 1;
568 ast_frfree(f);
569 f = &answer_frame;
570 } else if (f->subclass == '*' && endcall) {
571 /* terminates call */
572 ast_frfree(f);
573 f = NULL;
575 break;
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) {
580 ast_frfree(f);
581 f = &ast_null_frame;
583 default:
584 /* pass everything else on through */
585 break;
589 CLEANUP(ast,p);
590 if (p->chan && !p->chan->_bridge) {
591 if (strcasecmp(p->chan->tech->type, "Local")) {
592 p->chan->_bridge = ast;
593 if (p->chan)
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);
600 return f;
603 static int agent_sendhtml(struct ast_channel *ast, int subclass, const char *data, int datalen)
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_channel_sendhtml(p->chan, subclass, data, datalen);
610 ast_mutex_unlock(&p->lock);
611 return res;
614 static int agent_sendtext(struct ast_channel *ast, const char *text)
616 struct agent_pvt *p = ast->tech_pvt;
617 int res = -1;
618 ast_mutex_lock(&p->lock);
619 if (p->chan)
620 res = ast_sendtext(p->chan, text);
621 ast_mutex_unlock(&p->lock);
622 return res;
625 static int agent_write(struct ast_channel *ast, struct ast_frame *f)
627 struct agent_pvt *p = ast->tech_pvt;
628 int res = -1;
629 CHECK_FORMATS(ast, p);
630 ast_mutex_lock(&p->lock);
631 if (!p->chan)
632 res = 0;
633 else {
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);
638 } else {
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);
642 res = 0;
645 CLEANUP(ast, p);
646 ast_mutex_unlock(&p->lock);
647 return res;
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);
657 return -1;
659 p->owner = newchan;
660 ast_mutex_unlock(&p->lock);
661 return 0;
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;
667 int res = -1;
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;
671 else
672 res = 0;
673 ast_mutex_unlock(&p->lock);
674 return res;
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);
681 if (p->chan) {
682 ast_senddigit_begin(p->chan, digit);
684 ast_mutex_unlock(&p->lock);
685 return 0;
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);
692 if (p->chan) {
693 ast_senddigit_end(p->chan, digit, duration);
695 ast_mutex_unlock(&p->lock);
696 return 0;
699 static int agent_call(struct ast_channel *ast, char *dest, int timeout)
701 struct agent_pvt *p = ast->tech_pvt;
702 int res = -1;
703 int newstate=0;
704 ast_mutex_lock(&p->lock);
705 p->acknowledged = 0;
706 if (!p->chan) {
707 if (p->pending) {
708 ast_log(LOG_DEBUG, "Pretending to dial on pending agent\n");
709 newstate = AST_STATE_DIALING;
710 res = 0;
711 } else {
712 ast_log(LOG_NOTICE, "Whoa, they hung up between alloc and call... what are the odds of that?\n");
713 res = -1;
715 ast_mutex_unlock(&p->lock);
716 if (newstate)
717 ast_setstate(ast, newstate);
718 return res;
719 } else if (!ast_strlen_zero(p->loginchan)) {
720 time(&p->start);
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);
728 CLEANUP(ast,p);
729 ast_mutex_unlock(&p->lock);
730 return res;
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);
738 if (!res) {
739 res = ast_waitstream(p->chan, "");
740 if (option_debug > 2)
741 ast_log(LOG_DEBUG, "Waited for stream, result '%d'\n", res);
743 if (!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);
747 if (res)
748 ast_log(LOG_WARNING, "Unable to set read format to %s\n", ast_getformatname(ast_best_codec(p->chan->nativeformats)));
749 } else {
750 /* Agent hung-up */
751 p->chan = NULL;
752 p->inherited_devicestate = -1;
753 ast_device_state_changed("Agent/%s", p->agent);
756 if (!res) {
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);
760 if (res)
761 ast_log(LOG_WARNING, "Unable to set write format to %s\n", ast_getformatname(ast_best_codec(p->chan->nativeformats)));
763 if(!res) {
764 /* Call is immediately up, or might need ack */
765 if (p->ackcall > 1)
766 newstate = AST_STATE_RINGING;
767 else {
768 newstate = AST_STATE_UP;
769 if (recordagentcalls)
770 agent_start_monitoring(ast, 0);
771 p->acknowledged = 1;
773 res = 0;
775 CLEANUP(ast, p);
776 ast_mutex_unlock(&p->lock);
777 if (newstate)
778 ast_setstate(ast, newstate);
779 return res;
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))
789 return;
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);
804 return NULL;
806 p = chan->tech_pvt;
807 if (p->chan)
808 base = p->chan;
809 return base;
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);
818 return -1;
820 p = chan->tech_pvt;
821 if (!p) {
822 ast_log(LOG_ERROR, "whoa, channel %s is missing his tech_pvt structure!!.\n", chan->name);
823 return -1;
825 p->chan = base;
826 return 0;
829 static int agent_hangup(struct ast_channel *ast)
831 struct agent_pvt *p = ast->tech_pvt;
832 int howlong = 0;
833 const char *status;
834 ast_mutex_lock(&p->lock);
835 p->owner = NULL;
836 ast->tech_pvt = NULL;
837 p->app_sleep_cond = 1;
838 p->acknowledged = 0;
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()
847 if (option_debug)
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;
851 p->start = 0;
852 } else if (ast->_state == AST_STATE_RESERVED)
853 howlong = 0;
854 else
855 p->start = 0;
856 if (p->chan) {
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 */
861 if (p->wrapuptime)
862 p->lastdisc = ast_tvadd(ast_tvnow(), ast_samp2tv(p->wrapuptime, 1000));
863 else
864 p->lastdisc = ast_tv(0,0);
865 if (p->chan) {
866 status = pbx_builtin_getvar_helper(p->chan, "CHANLOCALSTATUS");
867 if (autologoffunavail && status && !strcasecmp(status, "CHANUNAVAIL")) {
868 long logintime = time(NULL) - p->loginstart;
869 p->loginstart = 0;
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 */
874 ast_hangup(p->chan);
875 p->chan = NULL;
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;
882 p->loginstart = 0;
883 if (!p->deferlogoff)
884 ast_log(LOG_NOTICE, "Agent '%s' didn't answer/confirm within %d seconds (waited %d)\n", p->name, p->autologoff, howlong);
885 p->deferlogoff = 0;
886 agent_logoff_maintenance(p, p->loginchan, logintime, ast->uniqueid, "Autologoff");
887 if (persistent_agents)
888 dump_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,
897 S_OR(p->moh, NULL),
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)
909 dump_agents();
910 } else {
911 ast_device_state_changed("Agent/%s", p->agent);
914 if (p->pending) {
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
921 kill it later */
922 p->abouttograb = 0;
923 } else if (p->dead) {
924 ast_mutex_destroy(&p->lock);
925 ast_mutex_destroy(&p->app_lock);
926 free(p);
927 } else {
928 if (p->chan) {
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);
939 return 0;
942 static int agent_cont_sleep( void *data )
944 struct agent_pvt *p;
945 int res;
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)
953 res = 1;
955 ast_mutex_unlock(&p->lock);
957 if(option_debug > 4 && !res )
958 ast_log(LOG_DEBUG, "agent_cont_sleep() returning %d\n", res );
960 return res;
963 static int agent_ack_sleep(void *data)
965 struct agent_pvt *p;
966 int res=0;
967 int to = 1000;
968 struct ast_frame *f;
970 /* Wait a second and look for something */
972 p = (struct agent_pvt *) data;
973 if (!p->chan)
974 return -1;
976 for(;;) {
977 to = ast_waitfor(p->chan, to);
978 if (to < 0)
979 return -1;
980 if (!to)
981 return 0;
982 f = ast_read(p->chan);
983 if (!f)
984 return -1;
985 if (f->frametype == AST_FRAME_DTMF)
986 res = f->subclass;
987 else
988 res = 0;
989 ast_frfree(f);
990 ast_mutex_lock(&p->lock);
991 if (!p->app_sleep_cond) {
992 ast_mutex_unlock(&p->lock);
993 return 0;
994 } else if (res == '#') {
995 ast_mutex_unlock(&p->lock);
996 return 1;
998 ast_mutex_unlock(&p->lock);
999 res = 0;
1001 return res;
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;
1009 if (p) {
1010 if (chan == p->chan)
1011 ret = bridge->_bridge;
1012 else if (chan == bridge->_bridge)
1013 ret = p->chan;
1016 if (option_debug)
1017 ast_log(LOG_DEBUG, "Asked for bridged channel on '%s'/'%s', returning '%s'\n", chan->name, bridge->name, ret ? ret->name : "<none>");
1018 return ret;
1021 /*! \brief Create new agent channel */
1022 static struct ast_channel *agent_new(struct agent_pvt *p, int state)
1024 struct ast_channel *tmp;
1025 #if 0
1026 if (!p->chan) {
1027 ast_log(LOG_WARNING, "No channel? :(\n");
1028 return NULL;
1030 #endif
1031 if (p->pending)
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);
1033 else
1034 tmp = ast_channel_alloc(0, state, 0, 0, "", p->chan ? p->chan->exten:"", p->chan ? p->chan->context:"", 0, "Agent/%s", p->agent);
1035 if (!tmp) {
1036 ast_log(LOG_WARNING, "Unable to allocate agent channel structure\n");
1037 return NULL;
1040 tmp->tech = &agent_tech;
1041 if (p->chan) {
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?? */
1051 } else {
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 */
1059 tmp->tech_pvt = p;
1060 p->owner = tmp;
1061 /* XXX: this needs fixing */
1062 #if 0
1063 ast_atomic_fetchadd_int(&__mod_desc->usecnt, +1);
1064 #endif
1065 ast_update_use_count();
1066 tmp->priority = 1;
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)) {
1076 if (p->chan) {
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);
1081 } else {
1082 ast_log(LOG_WARNING, "Agent disconnected while we were connecting the call\n");
1083 p->owner = NULL;
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);
1089 return NULL;
1091 } else if (!ast_strlen_zero(p->loginchan)) {
1092 if (p->chan)
1093 ast_queue_frame(p->chan, &ast_null_frame);
1094 if (!p->chan) {
1095 ast_log(LOG_WARNING, "Agent disconnected while we were connecting the call\n");
1096 p->owner = NULL;
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. */
1101 return NULL;
1104 if (p->chan)
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. */
1108 if (p->chan) {
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);
1114 return tmp;
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;
1132 int genhasagent;
1134 group = 0;
1135 autologoff = 0;
1136 wrapuptime = 0;
1137 ackcall = 0;
1138 endcall = 1;
1139 cfg = ast_config_load(config);
1140 if (!cfg) {
1141 ast_log(LOG_NOTICE, "No agent configuration found -- agent support disabled\n");
1142 return 0;
1144 AST_LIST_LOCK(&agents);
1145 AST_LIST_TRAVERSE(&agents, p, list) {
1146 p->dead = 1;
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");
1163 while(v) {
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);
1171 if (autologoff < 0)
1172 autologoff = 0;
1173 } else if (!strcasecmp(v->name, "ackcall")) {
1174 if (!strcasecmp(v->value, "always"))
1175 ackcall = 2;
1176 else if (ast_true(v->value))
1177 ackcall = 1;
1178 else
1179 ackcall = 0;
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);
1184 if (wrapuptime < 0)
1185 wrapuptime = 0;
1186 } else if (!strcasecmp(v->name, "maxlogintries") && !ast_strlen_zero(v->value)) {
1187 maxlogintries = atoi(v->value);
1188 if (maxlogintries < 0)
1189 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))
1196 updatecdr = 1;
1197 else
1198 updatecdr = 0;
1199 } else if (!strcasecmp(v->name, "autologoffunavail")) {
1200 if (ast_true(v->value))
1201 autologoffunavail = 1;
1202 else
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");
1210 else
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));
1219 else
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));
1226 v = v->next;
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);
1231 while(catname) {
1232 if (strcasecmp(catname, "general")) {
1233 hasagent = ast_variable_retrieve(ucfg, catname, "hasagent");
1234 if (ast_true(hasagent) || (!hasagent && genhasagent)) {
1235 char tmp[256];
1236 const char *fullname = ast_variable_retrieve(ucfg, catname, "fullname");
1237 const char *secret = ast_variable_retrieve(ucfg, catname, "secret");
1238 if (!fullname)
1239 fullname = "";
1240 if (!secret)
1241 secret = "";
1242 snprintf(tmp, sizeof(tmp), "%s,%s,%s", catname, secret,fullname);
1243 add_agent(tmp, 0);
1246 catname = ast_category_browse(ucfg, catname);
1248 ast_config_destroy(ucfg);
1250 AST_LIST_TRAVERSE_SAFE_BEGIN(&agents, p, list) {
1251 if (p->dead) {
1252 AST_LIST_REMOVE_CURRENT(&agents, list);
1253 /* Destroy if appropriate */
1254 if (!p->owner) {
1255 if (!p->chan) {
1256 ast_mutex_destroy(&p->lock);
1257 ast_mutex_destroy(&p->app_lock);
1258 free(p);
1259 } else {
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);
1269 return 1;
1272 static int check_availability(struct agent_pvt *newlyavailable, int needlock)
1274 struct ast_channel *chan=NULL, *parent=NULL;
1275 struct agent_pvt *p;
1276 int res;
1278 if (option_debug)
1279 ast_log(LOG_DEBUG, "Checking availability of '%s'\n", newlyavailable->agent);
1280 if (needlock)
1281 AST_LIST_LOCK(&agents);
1282 AST_LIST_TRAVERSE(&agents, p, list) {
1283 if (p == newlyavailable) {
1284 continue;
1286 ast_mutex_lock(&p->lock);
1287 if (!p->abouttograb && p->pending && ((p->group && (newlyavailable->group & p->group)) || !strcmp(p->agent, newlyavailable->agent))) {
1288 if (option_debug)
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);
1292 parent = p->owner;
1293 p->abouttograb = 1;
1294 ast_mutex_unlock(&p->lock);
1295 break;
1297 ast_mutex_unlock(&p->lock);
1299 if (needlock)
1300 AST_LIST_UNLOCK(&agents);
1301 if (parent && chan) {
1302 if (newlyavailable->ackcall > 1) {
1303 /* Don't do beep here */
1304 res = 0;
1305 } else {
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);
1311 if (!res) {
1312 res = ast_waitstream(newlyavailable->chan, "");
1313 ast_log( LOG_DEBUG, "Waited for stream, result '%d'\n", res);
1316 if (!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);
1330 p->abouttograb = 0;
1331 } else {
1332 if (option_debug)
1333 ast_log(LOG_DEBUG, "Sneaky, parent disappeared in the mean time...\n");
1334 agent_cleanup(newlyavailable);
1336 } else {
1337 if (option_debug)
1338 ast_log(LOG_DEBUG, "Ugh... Agent hung up at exactly the wrong time\n");
1339 agent_cleanup(newlyavailable);
1342 return 0;
1345 static int check_beep(struct agent_pvt *newlyavailable, int needlock)
1347 struct agent_pvt *p;
1348 int res=0;
1350 ast_log(LOG_DEBUG, "Checking beep availability of '%s'\n", newlyavailable->agent);
1351 if (needlock)
1352 AST_LIST_LOCK(&agents);
1353 AST_LIST_TRAVERSE(&agents, p, list) {
1354 if (p == newlyavailable) {
1355 continue;
1357 ast_mutex_lock(&p->lock);
1358 if (!p->abouttograb && p->pending && ((p->group && (newlyavailable->group & p->group)) || !strcmp(p->agent, newlyavailable->agent))) {
1359 if (option_debug)
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);
1362 break;
1364 ast_mutex_unlock(&p->lock);
1366 if (needlock)
1367 AST_LIST_UNLOCK(&agents);
1368 if (p) {
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);
1375 if (!res) {
1376 res = ast_waitstream(newlyavailable->chan, "");
1377 if (option_debug)
1378 ast_log( LOG_DEBUG, "Waited for stream, result '%d'\n", res);
1380 ast_mutex_lock(&newlyavailable->lock);
1382 return res;
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;
1389 char loginchan[80];
1391 if(multiplelogin)
1392 return 1;
1393 if(!chan)
1394 return 0;
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))
1400 return 0;
1402 return -1;
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;
1410 char *s;
1411 ast_group_t groupmatch;
1412 int groupoff;
1413 int waitforagent=0;
1414 int hasagent = 0;
1415 struct timeval tv;
1417 s = data;
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);
1422 waitforagent = 1;
1423 } else
1424 groupmatch = 0;
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)) {
1432 if (p->chan)
1433 hasagent++;
1434 tv = ast_tvnow();
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) {
1439 /* Fixed agent */
1440 chan = agent_new(p, AST_STATE_DOWN);
1442 if (chan) {
1443 ast_mutex_unlock(&p->lock);
1444 break;
1448 ast_mutex_unlock(&p->lock);
1450 if (!p) {
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))
1455 hasagent++;
1456 tv = ast_tvnow();
1457 #if 0
1458 ast_log(LOG_NOTICE, "Time now: %ld, Time of lastdisc: %ld\n", tv.tv_sec, p->lastdisc.tv_sec);
1459 #endif
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);
1469 if (p->chan)
1470 chan = agent_new(p, AST_STATE_DOWN);
1472 if (chan) {
1473 ast_mutex_unlock(&p->lock);
1474 break;
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 */
1485 if (hasagent) {
1486 if (option_debug)
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);
1491 if (!chan)
1492 ast_log(LOG_WARNING, "Weird... Fix this to drop the unused pending agent\n");
1493 } else
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);
1498 return chan;
1501 static force_inline int powerof(unsigned int d)
1503 int x = ffs(d);
1505 if (x)
1506 return x - 1;
1508 return 0;
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.
1514 * \param s
1515 * \param m
1516 * \returns
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] = "";
1523 char chanbuf[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);
1537 /* Status Values:
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, ""));
1561 } else {
1562 talkingtoChan = "n/a";
1564 status = "AGENT_ONCALL";
1565 } else {
1566 talkingtoChan = "n/a";
1567 status = "AGENT_IDLE";
1569 } else {
1570 loginChan = "n/a";
1571 talkingtoChan = "n/a";
1572 status = "AGENT_LOGGEDOFF";
1575 astman_append(s, "Event: Agents\r\n"
1576 "Agent: %s\r\n"
1577 "Name: %s\r\n"
1578 "Status: %s\r\n"
1579 "LoggedInChan: %s\r\n"
1580 "LoggedInTime: %d\r\n"
1581 "TalkingTo: %s\r\n"
1582 "%s"
1583 "\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"
1589 "%s"
1590 "\r\n",idText);
1591 return 0;
1594 static void agent_logoff_maintenance(struct agent_pvt *p, char *loginchan, long logintime, const char *uniqueid, char *logcommand)
1596 char *tmp = NULL;
1597 char agent[AST_MAX_AGENT];
1599 if (!ast_strlen_zero(logcommand))
1600 tmp = logcommand;
1601 else
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",
1608 "Agent: %s\r\n"
1609 "Reason: %s\r\n"
1610 "Loginchan: %s\r\n"
1611 "Logintime: %ld\r\n"
1612 "Uniqueid: %s\r\n",
1613 p->agent, tmp, loginchan, logintime, uniqueid);
1614 } else {
1615 manager_event(EVENT_FLAG_AGENT, "Agentcallbacklogoff",
1616 "Agent: %s\r\n"
1617 "Reason: %s\r\n"
1618 "Loginchan: %s\r\n"
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)
1630 dump_agents();
1634 static int agent_logoff(const char *agent, int soft)
1636 struct agent_pvt *p;
1637 long logintime;
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)) {
1643 ret = 0;
1644 if (p->owner || p->chan) {
1645 if (!soft) {
1646 ast_mutex_lock(&p->lock);
1648 while (p->owner && ast_channel_trylock(p->owner)) {
1649 DEADLOCK_AVOIDANCE(&p->lock);
1651 if (p->owner) {
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);
1659 if (p->chan) {
1660 ast_softhangup(p->chan, AST_SOFTHANGUP_EXPLICIT);
1661 ast_channel_unlock(p->chan);
1664 ast_mutex_unlock(&p->lock);
1665 } else
1666 p->deferlogoff = 1;
1667 } else {
1668 logintime = time(NULL) - p->loginstart;
1669 p->loginstart = 0;
1670 agent_logoff_maintenance(p, p->loginchan, logintime, NULL, "CommandLogoff");
1672 break;
1675 AST_LIST_UNLOCK(&agents);
1677 return ret;
1680 static int agent_logoff_cmd(int fd, int argc, char **argv)
1682 int ret;
1683 char *agent;
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);
1692 if (ret == 0)
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.
1701 * \param s
1702 * \param m
1703 * \returns
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 */
1710 int soft;
1711 int ret; /* return value of agent_logoff */
1713 if (ast_strlen_zero(agent)) {
1714 astman_send_error(s, m, "No agent specified");
1715 return 0;
1718 soft = ast_true(soft_s) ? 1 : 0;
1719 ret = agent_logoff(agent, soft);
1720 if (ret == 0)
1721 astman_send_ack(s, m, "Agent logged out");
1722 else
1723 astman_send_error(s, m, "No such agent");
1725 return 0;
1728 static char *complete_agent_logoff_cmd(const char *line, const char *word, int pos, int state)
1730 char *ret = NULL;
1732 if (pos == 2) {
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);
1742 break;
1745 AST_LIST_UNLOCK(&agents);
1746 } else if (pos == 3 && state == 0)
1747 return ast_strdup("soft");
1749 return ret;
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 */
1765 if (argc != 2)
1766 return RESULT_SHOWUSAGE;
1767 AST_LIST_LOCK(&agents);
1768 AST_LIST_TRAVERSE(&agents, p, list) {
1769 ast_mutex_lock(&p->lock);
1770 if (p->pending) {
1771 if (p->group)
1772 ast_cli(fd, "-- Pending call to group %d\n", powerof(p->group));
1773 else
1774 ast_cli(fd, "-- Pending call to agent %s\n", p->agent);
1775 } else {
1776 if (!ast_strlen_zero(p->name))
1777 snprintf(username, sizeof(username), "(%s) ", p->name);
1778 else
1779 username[0] = '\0';
1780 if (p->chan) {
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);
1784 else
1785 strcpy(talkingto, " is idle");
1786 online_agents++;
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);
1790 else
1791 snprintf(location, sizeof(location) - 20, "wrapping up at '%s'", p->loginchan);
1792 talkingto[0] = '\0';
1793 online_agents++;
1794 if (p->acknowledged)
1795 strncat(location, " (Confirmed)", sizeof(location) - strlen(location) - 1);
1796 } else {
1797 strcpy(location, "not logged in");
1798 talkingto[0] = '\0';
1799 offline_agents++;
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);
1805 count_agents++;
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);
1812 else
1813 ast_cli(fd, "%d agents configured [%d online , %d offline]\n",count_agents, online_agents, offline_agents);
1814 ast_cli(fd, "\n");
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 */
1830 if (argc != 3)
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);
1838 else
1839 username[0] = '\0';
1840 if (p->chan) {
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);
1844 else
1845 strcpy(talkingto, " is idle");
1846 agent_status = 1;
1847 online_agents++;
1848 } else if (!ast_strlen_zero(p->loginchan)) {
1849 snprintf(location, sizeof(location) - 20, "available at '%s'", p->loginchan);
1850 talkingto[0] = '\0';
1851 agent_status = 1;
1852 online_agents++;
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);
1858 if (agent_status)
1859 ast_cli(fd, "%-12.12s %s%s%s%s\n", p->agent, username, location, talkingto, moh);
1860 count_agents++;
1861 ast_mutex_unlock(&p->lock);
1863 AST_LIST_UNLOCK(&agents);
1864 if (!count_agents)
1865 ast_cli(fd, "No Agents are configured in %s\n", config);
1866 else
1867 ast_cli(fd, "%d agents online\n", online_agents);
1868 ast_cli(fd, "\n");
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 },
1889 agents_show, NULL,
1890 NULL, NULL };
1892 static struct ast_cli_entry cli_show_agents_online_deprecated = {
1893 { "show", "agents", "online" },
1894 agents_show_online, NULL,
1895 NULL, 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.
1914 * \param chan
1915 * \param data
1916 * \param callbackmode non-zero for AgentCallbackLogin
1918 static int __login_exec(struct ast_channel *chan, void *data, int callbackmode)
1920 int res=0;
1921 int tries = 0;
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] = "";
1930 char *errmsg;
1931 char *parse;
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")))
1965 update_cdr = 1;
1966 else
1967 update_cdr = 0;
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, "@");
1984 context = 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);
1995 if (!res) {
1996 if (!ast_strlen_zero(args.agent_id))
1997 ast_copy_string(user, args.agent_id, AST_MAX_AGENT);
1998 else
1999 res = ast_app_getdata(chan, "agent-user", user, sizeof(user) - 1, 0);
2001 while (!res && (max_login_tries==0 || tries < max_login_tries)) {
2002 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);
2010 if (!res) {
2011 if (!ast_strlen_zero(xpass))
2012 res = ast_app_getdata(chan, "agent-pass", pass, sizeof(pass) - 1, 0);
2013 else
2014 pass[0] = '\0';
2016 errmsg = "agent-incorrect";
2018 #if 0
2019 ast_log(LOG_NOTICE, "user: %s, pass: %s\n", user, pass);
2020 #endif
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"))
2039 p->ackcall = 2;
2040 else if (ast_true(pbx_builtin_getvar_helper(chan, "AGENTACKCALL")))
2041 p->ackcall = 1;
2042 else
2043 p->ackcall = 0;
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);
2047 } else {
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)
2053 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);
2057 } else {
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)
2063 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);
2067 } else {
2068 p->wrapuptime = wrapuptime;
2070 ast_channel_unlock(chan);
2071 unlock_channel = 0;
2072 /* End Channel Specific Agent Overrides */
2073 if (!p->chan) {
2074 char last_loginchan[80] = "";
2075 long logintime;
2076 snprintf(agent, sizeof(agent), "Agent/%s", p->agent);
2078 if (callbackmode) {
2079 int pos = 0;
2080 /* Retrieve login chan */
2081 for (;;) {
2082 if (!ast_strlen_zero(args.extension)) {
2083 ast_copy_string(tmpchan, args.extension, sizeof(tmpchan));
2084 res = 0;
2085 } else
2086 res = ast_app_getdata(chan, "agent-newlocation", tmpchan+pos, sizeof(tmpchan) - 2, 0);
2087 if (ast_strlen_zero(tmpchan) )
2088 break;
2089 if(ast_exists_extension(chan, S_OR(context,"default"), tmpchan,1, NULL) ) {
2090 if(!allow_multiple_login(tmpchan,context) ) {
2091 args.extension = NULL;
2092 pos = 0;
2093 } else
2094 break;
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;
2099 pos = 0;
2100 } else {
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);
2103 if (!res)
2104 res = ast_waitstream(chan, AST_DIGIT_ANY);
2105 if (res > 0) {
2106 tmpchan[0] = res;
2107 tmpchan[1] = '\0';
2108 pos = 1;
2109 } else {
2110 tmpchan[0] = '\0';
2111 pos = 0;
2115 args.extension = tmpchan;
2116 if (!res) {
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);
2120 else {
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)) {
2126 login_state = 2;
2127 filename = "agent-loggedoff";
2128 } else {
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);
2132 } else
2133 p->logincallerid[0] = '\0';
2136 if(update_cdr && chan->cdr)
2137 snprintf(chan->cdr->channel, sizeof(chan->cdr->channel), "Agent/%s", p->agent);
2140 } else {
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);
2149 if (!res)
2150 ast_waitstream(chan, "");
2151 AST_LIST_LOCK(&agents);
2152 ast_mutex_lock(&p->lock);
2153 if (!res) {
2154 res = ast_set_read_format(chan, ast_best_codec(chan->nativeformats));
2155 if (res)
2156 ast_log(LOG_WARNING, "Unable to set read format to %d\n", ast_best_codec(chan->nativeformats));
2158 if (!res) {
2159 res = ast_set_write_format(chan, ast_best_codec(chan->nativeformats));
2160 if (res)
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 */
2164 if (p->chan)
2165 res = -1;
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",
2172 "Agent: %s\r\n"
2173 "Loginchan: %s\r\n"
2174 "Uniqueid: %s\r\n",
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)
2181 dump_agents();
2182 } else {
2183 logintime = time(NULL) - p->loginstart;
2184 p->loginstart = 0;
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);
2191 if (!res)
2192 res = ast_safe_sleep(chan, 500);
2193 ast_mutex_unlock(&p->lock);
2194 } else if (!res) {
2195 ast_indicate_data(chan, AST_CONTROL_HOLD,
2196 S_OR(p->moh, NULL),
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",
2201 "Agent: %s\r\n"
2202 "Channel: %s\r\n"
2203 "Uniqueid: %s\r\n",
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 */
2212 p->chan = chan;
2213 if (p->ackcall > 1)
2214 check_beep(p, 0);
2215 else
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);
2220 while (res >= 0) {
2221 ast_mutex_lock(&p->lock);
2222 if (p->deferlogoff && p->chan) {
2223 ast_softhangup(p->chan, AST_SOFTHANGUP_EXPLICIT);
2224 p->deferlogoff = 0;
2226 if (p->chan != chan)
2227 res = -1;
2228 ast_mutex_unlock(&p->lock);
2229 /* Yield here so other interested threads can kick in. */
2230 sched_yield();
2231 if (res)
2232 break;
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) {
2238 if (option_debug)
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);
2242 if (p->ackcall > 1)
2243 check_beep(p, 0);
2244 else
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);
2255 if (p->ackcall > 1)
2256 res = agent_ack_sleep(p);
2257 else
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);
2266 res = 0;
2268 sched_yield();
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) {
2275 p->chan = NULL;
2276 p->inherited_devicestate = -1;
2278 p->acknowledged = 0;
2279 logintime = time(NULL) - p->loginstart;
2280 p->loginstart = 0;
2281 ast_mutex_unlock(&p->lock);
2282 manager_event(EVENT_FLAG_AGENT, "Agentlogoff",
2283 "Agent: %s\r\n"
2284 "Logintime: %ld\r\n"
2285 "Uniqueid: %s\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);
2295 free(p);
2298 else {
2299 ast_mutex_unlock(&p->lock);
2300 p = NULL;
2302 res = -1;
2303 } else {
2304 ast_mutex_unlock(&p->lock);
2305 errmsg = "agent-alreadyon";
2306 p = NULL;
2308 break;
2310 ast_mutex_unlock(&p->lock);
2311 if (unlock_channel) {
2312 ast_channel_unlock(chan);
2315 if (!p)
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);
2322 if (!res)
2323 res = ast_safe_sleep(chan, 500);
2325 /* AgentLogin() exit */
2326 if (!callbackmode) {
2327 ast_module_user_remove(u);
2328 return -1;
2329 } else { /* AgentCallbackLogin() exit*/
2330 /* Set variables */
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);
2336 } else
2337 pbx_builtin_setvar_helper(chan, "AGENTSTATUS", "off");
2338 } else {
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);
2343 return 0;
2345 /* Do we need to play agent-goodbye now that we will be hanging up? */
2346 if (play_announcement) {
2347 if (!res)
2348 res = ast_safe_sleep(chan, 1000);
2349 res = ast_streamfile(chan, agent_goodbye, chan->language);
2350 if (!res)
2351 res = ast_waitstream(chan, "");
2352 if (!res)
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 */
2360 return -1;
2364 * Called by the AgentLogin application (from the dial plan).
2366 * \param chan
2367 * \param data
2368 * \returns
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;
2380 if (!depwarning) {
2381 depwarning = 1;
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).
2392 * \param chan
2393 * \param data
2394 * \returns
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.
2407 * \param s
2408 * \param m
2409 * \returns
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");
2426 return 0;
2429 if (ast_strlen_zero(exten)) {
2430 astman_send_error(s, m, "No extension specified");
2431 return 0;
2434 AST_LIST_LOCK(&agents);
2435 AST_LIST_TRAVERSE(&agents, p, list) {
2436 if (strcmp(p->agent, agent) || p->pending)
2437 continue;
2438 if (p->chan) {
2439 login_state = 2; /* already logged in (and on the phone)*/
2440 break;
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));
2447 else
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)
2453 p->wrapuptime = 0;
2456 if (strcasecmp(ackcall_s, "always"))
2457 p->ackcall = 2;
2458 else if (ast_true(ackcall_s))
2459 p->ackcall = 1;
2460 else
2461 p->ackcall = 0;
2463 if (p->loginstart == 0)
2464 time(&p->loginstart);
2465 manager_event(EVENT_FLAG_AGENT, "Agentcallbacklogin",
2466 "Agent: %s\r\n"
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)
2475 dump_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");
2486 return 0;
2490 * \brief Called by the AgentMonitorOutgoing application (from the dial plan).
2492 * \param chan
2493 * \param data
2494 * \returns
2495 * \sa login_exec(), callback_login_exec(), load_module().
2497 static int agentmonitoroutgoing_exec(struct ast_channel *chan, void *data)
2499 int exitifnoagentid = 0;
2500 int nowarnings = 0;
2501 int changeoutgoing = 0;
2502 int res = 0;
2503 char agent[AST_MAX_AGENT];
2505 if (data) {
2506 if (strchr(data, 'd'))
2507 exitifnoagentid = 1;
2508 if (strchr(data, 'n'))
2509 nowarnings = 1;
2510 if (strchr(data, 'c'))
2511 changeoutgoing = 1;
2513 if (chan->cid.cid_num) {
2514 const char *tmp;
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);
2525 break;
2528 AST_LIST_UNLOCK(&agents);
2530 } else {
2531 res = -1;
2532 if (!nowarnings)
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);
2535 } else {
2536 res = -1;
2537 if (!nowarnings)
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 */
2542 if (res) {
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)
2548 return res;
2550 return 0;
2554 * \brief Dump AgentCallbackLogin agents to the ASTdb database for persistence
2556 static void dump_agents(void)
2558 struct agent_pvt *cur_agent = NULL;
2559 char buf[256];
2561 AST_LIST_TRAVERSE(&agents, cur_agent, list) {
2562 if (cur_agent->chan)
2563 continue;
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);
2571 } else {
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)
2583 char *agent_num;
2584 struct ast_db_entry *db_tree;
2585 struct ast_db_entry *entry;
2586 struct agent_pvt *cur_agent;
2587 char agent_data[256];
2588 char *parse;
2589 char *agent_chan;
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)
2600 break;
2601 ast_mutex_unlock(&cur_agent->lock);
2603 if (!cur_agent) {
2604 ast_db_del(pa_family, agent_num);
2605 continue;
2606 } else
2607 ast_mutex_unlock(&cur_agent->lock);
2608 if (!ast_db_get(pa_family, agent_num, agent_data, sizeof(agent_data)-1)) {
2609 if (option_debug)
2610 ast_log(LOG_DEBUG, "Reload Agent from AstDB: %s on %s\n", cur_agent->agent, agent_data);
2611 parse = 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);
2618 } else
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);
2626 if (db_tree) {
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;
2636 char *s;
2637 ast_group_t groupmatch;
2638 int groupoff;
2639 int waitforagent=0;
2640 int res = AST_DEVICE_INVALID;
2642 s = data;
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);
2647 waitforagent = 1;
2648 } else
2649 groupmatch = 0;
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))) {
2656 if (p->owner) {
2657 if (res != AST_DEVICE_INUSE)
2658 res = AST_DEVICE_BUSY;
2659 } else if (p->inherited_devicestate > -1) {
2660 res = p->inherited_devicestate;
2661 } else {
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);
2672 break;
2675 ast_mutex_unlock(&p->lock);
2677 AST_LIST_UNLOCK(&agents);
2678 return res;
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))
2690 break;
2693 return cur;
2696 static int function_agent(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
2698 char *parse;
2699 AST_DECLARE_APP_ARGS(args,
2700 AST_APP_ARG(agentid);
2701 AST_APP_ARG(item);
2703 char *tmp;
2704 struct agent_pvt *agent;
2706 buf[0] = '\0';
2708 if (ast_strlen_zero(data)) {
2709 ast_log(LOG_WARNING, "The AGENT function requires an argument - agentid!\n");
2710 return -1;
2713 parse = ast_strdupa(data);
2715 AST_NONSTANDARD_APP_ARGS(args, parse, ':');
2716 if (!args.item)
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);
2724 return -1;
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")) {
2739 if (agent->chan) {
2740 ast_copy_string(buf, agent->chan->name, len);
2741 tmp = strrchr(buf, '-');
2742 if (tmp)
2743 *tmp = '\0';
2745 } else if (!strcasecmp(args.item, "exten"))
2746 ast_copy_string(buf, agent->loginchan, len);
2748 AST_LIST_UNLOCK(&agents);
2750 return 0;
2753 struct ast_custom_function agent_function = {
2754 .name = "AGENT",
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");
2781 return -1;
2783 /* Read in the config */
2784 if (!read_agent_config())
2785 return AST_MODULE_LOAD_DECLINE;
2786 if (persistent_agents)
2787 reload_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);
2798 /* CLI Commands */
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);
2806 return 0;
2809 static int reload(void)
2811 read_agent_config();
2812 if (persistent_agents)
2813 reload_agents();
2814 return 0;
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))) {
2840 if (p->owner)
2841 ast_softhangup(p->owner, AST_SOFTHANGUP_APPUNLOAD);
2842 free(p);
2844 AST_LIST_UNLOCK(&agents);
2845 return 0;
2848 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Agent Proxy Channel",
2849 .load = load_module,
2850 .unload = unload_module,
2851 .reload = reload,