don't allow string input to overrun the buffer to hold it (ASA-2007-010)
[asterisk-bristuff.git] / channels / chan_agent.c
blob4ec79a098d34833fb15c76097489cb3fab5c58f7
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
34 #include "asterisk.h"
36 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
38 #include <stdio.h>
39 #include <string.h>
40 #include <errno.h>
41 #include <unistd.h>
42 #include <sys/socket.h>
43 #include <stdlib.h>
44 #include <fcntl.h>
45 #include <netdb.h>
46 #include <netinet/in.h>
47 #include <arpa/inet.h>
48 #include <sys/signal.h>
50 #include "asterisk/lock.h"
51 #include "asterisk/channel.h"
52 #include "asterisk/config.h"
53 #include "asterisk/logger.h"
54 #include "asterisk/module.h"
55 #include "asterisk/pbx.h"
56 #include "asterisk/options.h"
57 #include "asterisk/lock.h"
58 #include "asterisk/sched.h"
59 #include "asterisk/io.h"
60 #include "asterisk/rtp.h"
61 #include "asterisk/acl.h"
62 #include "asterisk/callerid.h"
63 #include "asterisk/file.h"
64 #include "asterisk/cli.h"
65 #include "asterisk/app.h"
66 #include "asterisk/musiconhold.h"
67 #include "asterisk/manager.h"
68 #include "asterisk/features.h"
69 #include "asterisk/utils.h"
70 #include "asterisk/causes.h"
71 #include "asterisk/astdb.h"
72 #include "asterisk/devicestate.h"
73 #include "asterisk/monitor.h"
74 #include "asterisk/stringfields.h"
76 static const char tdesc[] = "Call Agent Proxy Channel";
77 static const char config[] = "agents.conf";
79 static const char app[] = "AgentLogin";
80 static const char app2[] = "AgentCallbackLogin";
81 static const char app3[] = "AgentMonitorOutgoing";
83 static const char synopsis[] = "Call agent login";
84 static const char synopsis2[] = "Call agent callback login";
85 static const char synopsis3[] = "Record agent's outgoing call";
87 static const char descrip[] =
88 " AgentLogin([AgentNo][|options]):\n"
89 "Asks the agent to login to the system. Always returns -1. While\n"
90 "logged in, the agent can receive calls and will hear a 'beep'\n"
91 "when a new call comes in. The agent can dump the call by pressing\n"
92 "the star key.\n"
93 "The option string may contain zero or more of the following characters:\n"
94 " 's' -- silent login - do not announce the login ok segment after agent logged in/off\n";
96 static const char descrip2[] =
97 " AgentCallbackLogin([AgentNo][|[options][|[exten]@context]]):\n"
98 "Asks the agent to login to the system with callback.\n"
99 "The agent's callback extension is called (optionally with the specified\n"
100 "context).\n"
101 "The option string may contain zero or more of the following characters:\n"
102 " 's' -- silent login - do not announce the login ok segment agent logged in/off\n";
104 static const char descrip3[] =
105 " AgentMonitorOutgoing([options]):\n"
106 "Tries to figure out the id of the agent who is placing outgoing call based on\n"
107 "comparison of the callerid of the current interface and the global variable \n"
108 "placed by the AgentCallbackLogin application. That's why it should be used only\n"
109 "with the AgentCallbackLogin app. Uses the monitoring functions in chan_agent \n"
110 "instead of Monitor application. That have to be configured in the agents.conf file.\n"
111 "\nReturn value:\n"
112 "Normally the app returns 0 unless the options are passed. Also if the callerid or\n"
113 "the agentid are not specified it'll look for n+101 priority.\n"
114 "\nOptions:\n"
115 " 'd' - make the app return -1 if there is an error condition and there is\n"
116 " no extension n+101\n"
117 " 'c' - change the CDR so that the source of the call is 'Agent/agent_id'\n"
118 " 'n' - don't generate the warnings when there is no callerid or the\n"
119 " agentid is not known.\n"
120 " It's handy if you want to have one context for agent and non-agent calls.\n";
122 static const char mandescr_agents[] =
123 "Description: Will list info about all possible agents.\n"
124 "Variables: NONE\n";
126 static const char mandescr_agent_logoff[] =
127 "Description: Sets an agent as no longer logged in.\n"
128 "Variables: (Names marked with * are required)\n"
129 " *Agent: Agent ID of the agent to log off\n"
130 " Soft: Set to 'true' to not hangup existing calls\n";
132 static const char mandescr_agent_callback_login[] =
133 "Description: Sets an agent as logged in with callback.\n"
134 "Variables: (Names marked with * are required)\n"
135 " *Agent: Agent ID of the agent to login\n"
136 " *Exten: Extension to use for callback\n"
137 " Context: Context to use for callback\n"
138 " AckCall: Set to 'true' to require an acknowledgement by '#' when agent is called back\n"
139 " WrapupTime: the minimum amount of time after disconnecting before the caller can receive a new call\n";
141 static char moh[80] = "default";
143 #define AST_MAX_AGENT 80 /*!< Agent ID or Password max length */
144 #define AST_MAX_BUF 256
145 #define AST_MAX_FILENAME_LEN 256
147 static const char pa_family[] = "/Agents"; /*!< Persistent Agents astdb family */
148 #define PA_MAX_LEN 2048 /*!< The maximum length of each persistent member agent database entry */
150 static int persistent_agents = 0; /*!< queues.conf [general] option */
151 static void dump_agents(void);
153 static ast_group_t group;
154 static int autologoff;
155 static int wrapuptime;
156 static int ackcall;
157 static int endcall;
158 static int multiplelogin = 1;
159 static int autologoffunavail = 0;
161 static int maxlogintries = 3;
162 static char agentgoodbye[AST_MAX_FILENAME_LEN] = "vm-goodbye";
164 static int recordagentcalls = 0;
165 static char recordformat[AST_MAX_BUF] = "";
166 static char recordformatext[AST_MAX_BUF] = "";
167 static char urlprefix[AST_MAX_BUF] = "";
168 static char savecallsin[AST_MAX_BUF] = "";
169 static int updatecdr = 0;
170 static char beep[AST_MAX_BUF] = "beep";
172 #define GETAGENTBYCALLERID "AGENTBYCALLERID"
174 /*! \brief Structure representing an agent. */
175 struct agent_pvt {
176 ast_mutex_t lock; /*!< Channel private lock */
177 int dead; /*!< Poised for destruction? */
178 int pending; /*!< Not a real agent -- just pending a match */
179 int abouttograb; /*!< About to grab */
180 int autologoff; /*!< Auto timeout time */
181 int ackcall; /*!< ackcall */
182 int deferlogoff; /*!< Defer logoff to hangup */
183 time_t loginstart; /*!< When agent first logged in (0 when logged off) */
184 time_t start; /*!< When call started */
185 struct timeval lastdisc; /*!< When last disconnected */
186 int wrapuptime; /*!< Wrapup time in ms */
187 ast_group_t group; /*!< Group memberships */
188 int acknowledged; /*!< Acknowledged */
189 char moh[80]; /*!< Which music on hold */
190 char agent[AST_MAX_AGENT]; /*!< Agent ID */
191 char password[AST_MAX_AGENT]; /*!< Password for Agent login */
192 char name[AST_MAX_AGENT];
193 ast_mutex_t app_lock; /**< Synchronization between owning applications */
194 volatile pthread_t owning_app; /**< Owning application thread id */
195 volatile int app_sleep_cond; /**< Sleep condition for the login app */
196 struct ast_channel *owner; /**< Agent */
197 char loginchan[80]; /**< channel they logged in from */
198 char logincallerid[80]; /**< Caller ID they had when they logged in */
199 struct ast_channel *chan; /**< Channel we use */
200 AST_LIST_ENTRY(agent_pvt) list; /**< Next Agent in the linked list. */
203 static AST_LIST_HEAD_STATIC(agents, agent_pvt); /*!< Holds the list of agents (loaded form agents.conf). */
205 #define CHECK_FORMATS(ast, p) do { \
206 if (p->chan) {\
207 if (ast->nativeformats != p->chan->nativeformats) { \
208 ast_log(LOG_DEBUG, "Native formats changing from %d to %d\n", ast->nativeformats, p->chan->nativeformats); \
209 /* Native formats changed, reset things */ \
210 ast->nativeformats = p->chan->nativeformats; \
211 ast_log(LOG_DEBUG, "Resetting read to %d and write to %d\n", ast->readformat, ast->writeformat);\
212 ast_set_read_format(ast, ast->readformat); \
213 ast_set_write_format(ast, ast->writeformat); \
215 if (p->chan->readformat != ast->rawreadformat) \
216 ast_set_read_format(p->chan, ast->rawreadformat); \
217 if (p->chan->writeformat != ast->rawwriteformat) \
218 ast_set_write_format(p->chan, ast->rawwriteformat); \
220 } while(0)
222 /*! \brief Cleanup moves all the relevant FD's from the 2nd to the first, but retains things
223 properly for a timingfd XXX This might need more work if agents were logged in as agents or other
224 totally impractical combinations XXX */
226 #define CLEANUP(ast, p) do { \
227 int x; \
228 if (p->chan) { \
229 for (x=0;x<AST_MAX_FDS;x++) {\
230 if (x != AST_TIMING_FD) \
231 ast->fds[x] = p->chan->fds[x]; \
233 ast->fds[AST_AGENT_FD] = p->chan->fds[AST_TIMING_FD]; \
235 } while(0)
237 /*--- Forward declarations */
238 static struct ast_channel *agent_request(const char *type, int format, void *data, int *cause);
239 static int agent_devicestate(void *data);
240 static void agent_logoff_maintenance(struct agent_pvt *p, char *loginchan, long logintime, const char *uniqueid, char *logcommand);
241 static int agent_digit_begin(struct ast_channel *ast, char digit);
242 static int agent_digit_end(struct ast_channel *ast, char digit, unsigned int duration);
243 static int agent_call(struct ast_channel *ast, char *dest, int timeout);
244 static int agent_hangup(struct ast_channel *ast);
245 static int agent_answer(struct ast_channel *ast);
246 static struct ast_frame *agent_read(struct ast_channel *ast);
247 static int agent_write(struct ast_channel *ast, struct ast_frame *f);
248 static int agent_sendhtml(struct ast_channel *ast, int subclass, const char *data, int datalen);
249 static int agent_sendtext(struct ast_channel *ast, const char *text);
250 static int agent_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen);
251 static int agent_fixup(struct ast_channel *oldchan, struct ast_channel *newchan);
252 static struct ast_channel *agent_bridgedchannel(struct ast_channel *chan, struct ast_channel *bridge);
253 static void set_agentbycallerid(const char *callerid, const char *agent);
255 /*! \brief Channel interface description for PBX integration */
256 static const struct ast_channel_tech agent_tech = {
257 .type = "Agent",
258 .description = tdesc,
259 .capabilities = -1,
260 .requester = agent_request,
261 .devicestate = agent_devicestate,
262 .send_digit_begin = agent_digit_begin,
263 .send_digit_end = agent_digit_end,
264 .call = agent_call,
265 .hangup = agent_hangup,
266 .answer = agent_answer,
267 .read = agent_read,
268 .write = agent_write,
269 .write_video = agent_write,
270 .send_html = agent_sendhtml,
271 .send_text = agent_sendtext,
272 .exception = agent_read,
273 .indicate = agent_indicate,
274 .fixup = agent_fixup,
275 .bridged_channel = agent_bridgedchannel,
279 * Adds an agent to the global list of agents.
281 * \param agent A string with the username, password and real name of an agent. As defined in agents.conf. Example: "13,169,John Smith"
282 * \param pending If it is pending or not.
283 * @return The just created agent.
284 * \sa agent_pvt, agents.
286 static struct agent_pvt *add_agent(char *agent, int pending)
288 char *parse;
289 AST_DECLARE_APP_ARGS(args,
290 AST_APP_ARG(agt);
291 AST_APP_ARG(password);
292 AST_APP_ARG(name);
294 char *password = NULL;
295 char *name = NULL;
296 char *agt = NULL;
297 struct agent_pvt *p;
299 parse = ast_strdupa(agent);
301 /* Extract username (agt), password and name from agent (args). */
302 AST_NONSTANDARD_APP_ARGS(args, parse, ',');
304 if(args.argc == 0) {
305 ast_log(LOG_WARNING, "A blank agent line!\n");
306 return NULL;
309 if(ast_strlen_zero(args.agt) ) {
310 ast_log(LOG_WARNING, "An agent line with no agentid!\n");
311 return NULL;
312 } else
313 agt = args.agt;
315 if(!ast_strlen_zero(args.password)) {
316 password = args.password;
317 while (*password && *password < 33) password++;
319 if(!ast_strlen_zero(args.name)) {
320 name = args.name;
321 while (*name && *name < 33) name++;
324 /* Are we searching for the agent here ? To see if it exists already ? */
325 AST_LIST_TRAVERSE(&agents, p, list) {
326 if (!pending && !strcmp(p->agent, agt))
327 break;
329 if (!p) {
330 // Build the agent.
331 if (!(p = ast_calloc(1, sizeof(*p))))
332 return NULL;
333 ast_copy_string(p->agent, agt, sizeof(p->agent));
334 ast_mutex_init(&p->lock);
335 ast_mutex_init(&p->app_lock);
336 p->owning_app = (pthread_t) -1;
337 p->app_sleep_cond = 1;
338 p->group = group;
339 p->pending = pending;
340 AST_LIST_INSERT_TAIL(&agents, p, list);
343 ast_copy_string(p->password, password ? password : "", sizeof(p->password));
344 ast_copy_string(p->name, name ? name : "", sizeof(p->name));
345 ast_copy_string(p->moh, moh, sizeof(p->moh));
346 p->ackcall = ackcall;
347 p->autologoff = autologoff;
349 /* If someone reduces the wrapuptime and reloads, we want it
350 * to change the wrapuptime immediately on all calls */
351 if (p->wrapuptime > wrapuptime) {
352 struct timeval now = ast_tvnow();
353 /* XXX check what is this exactly */
355 /* We won't be pedantic and check the tv_usec val */
356 if (p->lastdisc.tv_sec > (now.tv_sec + wrapuptime/1000)) {
357 p->lastdisc.tv_sec = now.tv_sec + wrapuptime/1000;
358 p->lastdisc.tv_usec = now.tv_usec;
361 p->wrapuptime = wrapuptime;
363 if (pending)
364 p->dead = 1;
365 else
366 p->dead = 0;
367 return p;
371 * Deletes an agent after doing some clean up.
372 * Further documentation: How safe is this function ? What state should the agent be to be cleaned.
373 * \param p Agent to be deleted.
374 * \returns Always 0.
376 static int agent_cleanup(struct agent_pvt *p)
378 struct ast_channel *chan = p->owner;
379 p->owner = NULL;
380 chan->tech_pvt = NULL;
381 p->app_sleep_cond = 1;
382 /* Release ownership of the agent to other threads (presumably running the login app). */
383 ast_mutex_unlock(&p->app_lock);
384 if (chan)
385 ast_channel_free(chan);
386 if (p->dead) {
387 ast_mutex_destroy(&p->lock);
388 ast_mutex_destroy(&p->app_lock);
389 free(p);
391 return 0;
394 static int check_availability(struct agent_pvt *newlyavailable, int needlock);
396 static int agent_answer(struct ast_channel *ast)
398 ast_log(LOG_WARNING, "Huh? Agent is being asked to answer?\n");
399 return -1;
402 static int __agent_start_monitoring(struct ast_channel *ast, struct agent_pvt *p, int needlock)
404 char tmp[AST_MAX_BUF],tmp2[AST_MAX_BUF], *pointer;
405 char filename[AST_MAX_BUF];
406 int res = -1;
407 if (!p)
408 return -1;
409 if (!ast->monitor) {
410 snprintf(filename, sizeof(filename), "agent-%s-%s",p->agent, ast->uniqueid);
411 /* substitute . for - */
412 if ((pointer = strchr(filename, '.')))
413 *pointer = '-';
414 snprintf(tmp, sizeof(tmp), "%s%s",savecallsin ? savecallsin : "", filename);
415 ast_monitor_start(ast, recordformat, tmp, needlock);
416 ast_monitor_setjoinfiles(ast, 1);
417 snprintf(tmp2, sizeof(tmp2), "%s%s.%s", urlprefix ? urlprefix : "", filename, recordformatext);
418 #if 0
419 ast_verbose("name is %s, link is %s\n",tmp, tmp2);
420 #endif
421 if (!ast->cdr)
422 ast->cdr = ast_cdr_alloc();
423 ast_cdr_setuserfield(ast, tmp2);
424 res = 0;
425 } else
426 ast_log(LOG_ERROR, "Recording already started on that call.\n");
427 return res;
430 static int agent_start_monitoring(struct ast_channel *ast, int needlock)
432 return __agent_start_monitoring(ast, ast->tech_pvt, needlock);
435 static struct ast_frame *agent_read(struct ast_channel *ast)
437 struct agent_pvt *p = ast->tech_pvt;
438 struct ast_frame *f = NULL;
439 static struct ast_frame answer_frame = { AST_FRAME_CONTROL, AST_CONTROL_ANSWER };
440 const char *status;
441 ast_mutex_lock(&p->lock);
442 CHECK_FORMATS(ast, p);
443 if (p->chan) {
444 ast_copy_flags(p->chan, ast, AST_FLAG_EXCEPTION);
445 p->chan->fdno = (ast->fdno == AST_AGENT_FD) ? AST_TIMING_FD : ast->fdno;
446 f = ast_read(p->chan);
447 } else
448 f = &ast_null_frame;
449 if (!f) {
450 /* If there's a channel, hang it up (if it's on a callback) make it NULL */
451 if (p->chan) {
452 p->chan->_bridge = NULL;
453 /* Note that we don't hangup if it's not a callback because Asterisk will do it
454 for us when the PBX instance that called login finishes */
455 if (!ast_strlen_zero(p->loginchan)) {
456 if (p->chan)
457 ast_log(LOG_DEBUG, "Bridge on '%s' being cleared (2)\n", p->chan->name);
459 status = pbx_builtin_getvar_helper(p->chan, "CHANLOCALSTATUS");
460 if (autologoffunavail && status && !strcasecmp(status, "CHANUNAVAIL")) {
461 long logintime = time(NULL) - p->loginstart;
462 p->loginstart = 0;
463 ast_log(LOG_NOTICE, "Agent read: '%s' is not available now, auto logoff\n", p->name);
464 agent_logoff_maintenance(p, p->loginchan, logintime, ast->uniqueid, "Chanunavail");
466 ast_hangup(p->chan);
467 if (p->wrapuptime && p->acknowledged)
468 p->lastdisc = ast_tvadd(ast_tvnow(), ast_samp2tv(p->wrapuptime, 1000));
470 p->chan = NULL;
471 p->acknowledged = 0;
473 } else {
474 /* if acknowledgement is not required, and the channel is up, we may have missed
475 an AST_CONTROL_ANSWER (if there was one), so mark the call acknowledged anyway */
476 if (!p->ackcall && !p->acknowledged && p->chan && (p->chan->_state == AST_STATE_UP))
477 p->acknowledged = 1;
478 switch (f->frametype) {
479 case AST_FRAME_CONTROL:
480 if (f->subclass == AST_CONTROL_ANSWER) {
481 if (p->ackcall) {
482 if (option_verbose > 2)
483 ast_verbose(VERBOSE_PREFIX_3 "%s answered, waiting for '#' to acknowledge\n", p->chan->name);
484 /* Don't pass answer along */
485 ast_frfree(f);
486 f = &ast_null_frame;
487 } else {
488 p->acknowledged = 1;
489 /* Use the builtin answer frame for the
490 recording start check below. */
491 ast_frfree(f);
492 f = &answer_frame;
495 break;
496 case AST_FRAME_DTMF_BEGIN:
497 case AST_FRAME_DTMF_END:
498 if (!p->acknowledged && (f->subclass == '#')) {
499 if (option_verbose > 2)
500 ast_verbose(VERBOSE_PREFIX_3 "%s acknowledged\n", p->chan->name);
501 p->acknowledged = 1;
502 ast_frfree(f);
503 f = &answer_frame;
504 } else if (f->subclass == '*' && endcall) {
505 /* terminates call */
506 ast_frfree(f);
507 f = NULL;
509 break;
510 case AST_FRAME_VOICE:
511 case AST_FRAME_VIDEO:
512 /* don't pass voice or video until the call is acknowledged */
513 if (!p->acknowledged) {
514 ast_frfree(f);
515 f = &ast_null_frame;
517 default:
518 /* pass everything else on through */
519 break;
523 CLEANUP(ast,p);
524 if (p->chan && !p->chan->_bridge) {
525 if (strcasecmp(p->chan->tech->type, "Local")) {
526 p->chan->_bridge = ast;
527 if (p->chan)
528 ast_log(LOG_DEBUG, "Bridge on '%s' being set to '%s' (3)\n", p->chan->name, p->chan->_bridge->name);
531 ast_mutex_unlock(&p->lock);
532 if (recordagentcalls && f == &answer_frame)
533 agent_start_monitoring(ast,0);
534 return f;
537 static int agent_sendhtml(struct ast_channel *ast, int subclass, const char *data, int datalen)
539 struct agent_pvt *p = ast->tech_pvt;
540 int res = -1;
541 ast_mutex_lock(&p->lock);
542 if (p->chan)
543 res = ast_channel_sendhtml(p->chan, subclass, data, datalen);
544 ast_mutex_unlock(&p->lock);
545 return res;
548 static int agent_sendtext(struct ast_channel *ast, const char *text)
550 struct agent_pvt *p = ast->tech_pvt;
551 int res = -1;
552 ast_mutex_lock(&p->lock);
553 if (p->chan)
554 res = ast_sendtext(p->chan, text);
555 ast_mutex_unlock(&p->lock);
556 return res;
559 static int agent_write(struct ast_channel *ast, struct ast_frame *f)
561 struct agent_pvt *p = ast->tech_pvt;
562 int res = -1;
563 CHECK_FORMATS(ast, p);
564 ast_mutex_lock(&p->lock);
565 if (!p->chan)
566 res = 0;
567 else {
568 if ((f->frametype != AST_FRAME_VOICE) ||
569 (f->frametype != AST_FRAME_VIDEO) ||
570 (f->subclass == p->chan->writeformat)) {
571 res = ast_write(p->chan, f);
572 } else {
573 ast_log(LOG_DEBUG, "Dropping one incompatible %s frame on '%s' to '%s'\n",
574 f->frametype == AST_FRAME_VOICE ? "audio" : "video",
575 ast->name, p->chan->name);
576 res = 0;
579 CLEANUP(ast, p);
580 ast_mutex_unlock(&p->lock);
581 return res;
584 static int agent_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
586 struct agent_pvt *p = newchan->tech_pvt;
587 ast_mutex_lock(&p->lock);
588 if (p->owner != oldchan) {
589 ast_log(LOG_WARNING, "old channel wasn't %p but was %p\n", oldchan, p->owner);
590 ast_mutex_unlock(&p->lock);
591 return -1;
593 p->owner = newchan;
594 ast_mutex_unlock(&p->lock);
595 return 0;
598 static int agent_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen)
600 struct agent_pvt *p = ast->tech_pvt;
601 int res = -1;
602 ast_mutex_lock(&p->lock);
603 if (p->chan)
604 res = p->chan->tech->indicate ? p->chan->tech->indicate(p->chan, condition, data, datalen) : -1;
605 else
606 res = 0;
607 ast_mutex_unlock(&p->lock);
608 return res;
611 static int agent_digit_begin(struct ast_channel *ast, char digit)
613 struct agent_pvt *p = ast->tech_pvt;
614 int res = -1;
615 ast_mutex_lock(&p->lock);
616 ast_senddigit_begin(p->chan, digit);
617 ast_mutex_unlock(&p->lock);
618 return res;
621 static int agent_digit_end(struct ast_channel *ast, char digit, unsigned int duration)
623 struct agent_pvt *p = ast->tech_pvt;
624 int res = -1;
625 ast_mutex_lock(&p->lock);
626 ast_senddigit_end(p->chan, digit, duration);
627 ast_mutex_unlock(&p->lock);
628 return res;
631 static int agent_call(struct ast_channel *ast, char *dest, int timeout)
633 struct agent_pvt *p = ast->tech_pvt;
634 int res = -1;
635 int newstate=0;
636 ast_mutex_lock(&p->lock);
637 p->acknowledged = 0;
638 if (!p->chan) {
639 if (p->pending) {
640 ast_log(LOG_DEBUG, "Pretending to dial on pending agent\n");
641 newstate = AST_STATE_DIALING;
642 res = 0;
643 } else {
644 ast_log(LOG_NOTICE, "Whoa, they hung up between alloc and call... what are the odds of that?\n");
645 res = -1;
647 ast_mutex_unlock(&p->lock);
648 if (newstate)
649 ast_setstate(ast, newstate);
650 return res;
651 } else if (!ast_strlen_zero(p->loginchan)) {
652 time(&p->start);
653 /* Call on this agent */
654 if (option_verbose > 2)
655 ast_verbose(VERBOSE_PREFIX_3 "outgoing agentcall, to agent '%s', on '%s'\n", p->agent, p->chan->name);
656 ast_set_callerid(p->chan,
657 ast->cid.cid_num, ast->cid.cid_name, NULL);
658 ast_channel_inherit_variables(ast, p->chan);
659 res = ast_call(p->chan, p->loginchan, 0);
660 CLEANUP(ast,p);
661 ast_mutex_unlock(&p->lock);
662 return res;
664 ast_verbose( VERBOSE_PREFIX_3 "agent_call, call to agent '%s' call on '%s'\n", p->agent, p->chan->name);
665 if (option_debug > 2)
666 ast_log(LOG_DEBUG, "Playing beep, lang '%s'\n", p->chan->language);
667 res = ast_streamfile(p->chan, beep, p->chan->language);
668 if (option_debug > 2)
669 ast_log(LOG_DEBUG, "Played beep, result '%d'\n", res);
670 if (!res) {
671 res = ast_waitstream(p->chan, "");
672 if (option_debug > 2)
673 ast_log(LOG_DEBUG, "Waited for stream, result '%d'\n", res);
675 if (!res) {
676 res = ast_set_read_format(p->chan, ast_best_codec(p->chan->nativeformats));
677 if (option_debug > 2)
678 ast_log(LOG_DEBUG, "Set read format, result '%d'\n", res);
679 if (res)
680 ast_log(LOG_WARNING, "Unable to set read format to %s\n", ast_getformatname(ast_best_codec(p->chan->nativeformats)));
681 } else {
682 /* Agent hung-up */
683 p->chan = NULL;
686 if (!res) {
687 res = ast_set_write_format(p->chan, ast_best_codec(p->chan->nativeformats));
688 if (option_debug > 2)
689 ast_log(LOG_DEBUG, "Set write format, result '%d'\n", res);
690 if (res)
691 ast_log(LOG_WARNING, "Unable to set write format to %s\n", ast_getformatname(ast_best_codec(p->chan->nativeformats)));
693 if(!res) {
694 /* Call is immediately up, or might need ack */
695 if (p->ackcall > 1)
696 newstate = AST_STATE_RINGING;
697 else {
698 newstate = AST_STATE_UP;
699 if (recordagentcalls)
700 agent_start_monitoring(ast, 0);
701 p->acknowledged = 1;
703 res = 0;
705 CLEANUP(ast, p);
706 ast_mutex_unlock(&p->lock);
707 if (newstate)
708 ast_setstate(ast, newstate);
709 return res;
712 /*! \brief store/clear the global variable that stores agentid based on the callerid */
713 static void set_agentbycallerid(const char *callerid, const char *agent)
715 char buf[AST_MAX_BUF];
717 /* if there is no Caller ID, nothing to do */
718 if (ast_strlen_zero(callerid))
719 return;
721 snprintf(buf, sizeof(buf), "%s_%s", GETAGENTBYCALLERID, callerid);
722 pbx_builtin_setvar_helper(NULL, buf, agent);
725 static int agent_hangup(struct ast_channel *ast)
727 struct agent_pvt *p = ast->tech_pvt;
728 int howlong = 0;
729 const char *status;
730 ast_mutex_lock(&p->lock);
731 p->owner = NULL;
732 ast->tech_pvt = NULL;
733 p->app_sleep_cond = 1;
734 p->acknowledged = 0;
736 /* if they really are hung up then set start to 0 so the test
737 * later if we're called on an already downed channel
738 * doesn't cause an agent to be logged out like when
739 * agent_request() is followed immediately by agent_hangup()
740 * as in apps/app_chanisavail.c:chanavail_exec()
743 if (option_debug)
744 ast_log(LOG_DEBUG, "Hangup called for state %s\n", ast_state2str(ast->_state));
745 if (p->start && (ast->_state != AST_STATE_UP)) {
746 howlong = time(NULL) - p->start;
747 p->start = 0;
748 } else if (ast->_state == AST_STATE_RESERVED)
749 howlong = 0;
750 else
751 p->start = 0;
752 if (p->chan) {
753 p->chan->_bridge = NULL;
754 /* If they're dead, go ahead and hang up on the agent now */
755 if (!ast_strlen_zero(p->loginchan)) {
756 /* Store last disconnect time */
757 if (p->wrapuptime)
758 p->lastdisc = ast_tvadd(ast_tvnow(), ast_samp2tv(p->wrapuptime, 1000));
759 else
760 p->lastdisc = ast_tv(0,0);
761 if (p->chan) {
762 status = pbx_builtin_getvar_helper(p->chan, "CHANLOCALSTATUS");
763 if (autologoffunavail && status && !strcasecmp(status, "CHANUNAVAIL")) {
764 long logintime = time(NULL) - p->loginstart;
765 p->loginstart = 0;
766 ast_log(LOG_NOTICE, "Agent hangup: '%s' is not available now, auto logoff\n", p->name);
767 agent_logoff_maintenance(p, p->loginchan, logintime, ast->uniqueid, "Chanunavail");
769 /* Recognize the hangup and pass it along immediately */
770 ast_hangup(p->chan);
771 p->chan = NULL;
773 ast_log(LOG_DEBUG, "Hungup, howlong is %d, autologoff is %d\n", howlong, p->autologoff);
774 if ((p->deferlogoff) || (howlong && p->autologoff && (howlong > p->autologoff))) {
775 long logintime = time(NULL) - p->loginstart;
776 p->loginstart = 0;
777 if (!p->deferlogoff)
778 ast_log(LOG_NOTICE, "Agent '%s' didn't answer/confirm within %d seconds (waited %d)\n", p->name, p->autologoff, howlong);
779 p->deferlogoff = 0;
780 agent_logoff_maintenance(p, p->loginchan, logintime, ast->uniqueid, "Autologoff");
781 if (persistent_agents)
782 dump_agents();
784 } else if (p->dead) {
785 ast_channel_lock(p->chan);
786 ast_softhangup(p->chan, AST_SOFTHANGUP_EXPLICIT);
787 ast_channel_unlock(p->chan);
788 } else if (p->loginstart) {
789 ast_channel_lock(p->chan);
790 ast_indicate_data(p->chan, AST_CONTROL_HOLD,
791 S_OR(p->moh, NULL),
792 !ast_strlen_zero(p->moh) ? strlen(p->moh) + 1 : 0);
793 ast_channel_unlock(p->chan);
796 ast_mutex_unlock(&p->lock);
798 /* Only register a device state change if the agent is still logged in */
799 if (!p->loginstart) {
800 p->loginchan[0] = '\0';
801 p->logincallerid[0] = '\0';
802 if (persistent_agents)
803 dump_agents();
804 } else {
805 ast_device_state_changed("Agent/%s", p->agent);
808 if (p->pending) {
809 AST_LIST_LOCK(&agents);
810 AST_LIST_REMOVE(&agents, p, list);
811 AST_LIST_UNLOCK(&agents);
813 if (p->abouttograb) {
814 /* Let the "about to grab" thread know this isn't valid anymore, and let it
815 kill it later */
816 p->abouttograb = 0;
817 } else if (p->dead) {
818 ast_mutex_destroy(&p->lock);
819 ast_mutex_destroy(&p->app_lock);
820 free(p);
821 } else {
822 if (p->chan) {
823 /* Not dead -- check availability now */
824 ast_mutex_lock(&p->lock);
825 /* Store last disconnect time */
826 p->lastdisc = ast_tvnow();
827 ast_mutex_unlock(&p->lock);
829 /* Release ownership of the agent to other threads (presumably running the login app). */
830 if (ast_strlen_zero(p->loginchan))
831 ast_mutex_unlock(&p->app_lock);
833 return 0;
836 static int agent_cont_sleep( void *data )
838 struct agent_pvt *p;
839 int res;
841 p = (struct agent_pvt *)data;
843 ast_mutex_lock(&p->lock);
844 res = p->app_sleep_cond;
845 if (p->lastdisc.tv_sec) {
846 if (ast_tvdiff_ms(ast_tvnow(), p->lastdisc) > p->wrapuptime)
847 res = 1;
849 ast_mutex_unlock(&p->lock);
851 if(option_debug > 4 && !res )
852 ast_log(LOG_DEBUG, "agent_cont_sleep() returning %d\n", res );
854 return res;
857 static int agent_ack_sleep(void *data)
859 struct agent_pvt *p;
860 int res=0;
861 int to = 1000;
862 struct ast_frame *f;
864 /* Wait a second and look for something */
866 p = (struct agent_pvt *) data;
867 if (!p->chan)
868 return -1;
870 for(;;) {
871 to = ast_waitfor(p->chan, to);
872 if (to < 0)
873 return -1;
874 if (!to)
875 return 0;
876 f = ast_read(p->chan);
877 if (!f)
878 return -1;
879 if (f->frametype == AST_FRAME_DTMF)
880 res = f->subclass;
881 else
882 res = 0;
883 ast_frfree(f);
884 ast_mutex_lock(&p->lock);
885 if (!p->app_sleep_cond) {
886 ast_mutex_unlock(&p->lock);
887 return 0;
888 } else if (res == '#') {
889 ast_mutex_unlock(&p->lock);
890 return 1;
892 ast_mutex_unlock(&p->lock);
893 res = 0;
895 return res;
898 static struct ast_channel *agent_bridgedchannel(struct ast_channel *chan, struct ast_channel *bridge)
900 struct agent_pvt *p = bridge->tech_pvt;
901 struct ast_channel *ret = NULL;
903 if (p) {
904 if (chan == p->chan)
905 ret = bridge->_bridge;
906 else if (chan == bridge->_bridge)
907 ret = p->chan;
910 if (option_debug)
911 ast_log(LOG_DEBUG, "Asked for bridged channel on '%s'/'%s', returning '%s'\n", chan->name, bridge->name, ret ? ret->name : "<none>");
912 return ret;
915 /*! \brief Create new agent channel */
916 static struct ast_channel *agent_new(struct agent_pvt *p, int state)
918 struct ast_channel *tmp;
919 #if 0
920 if (!p->chan) {
921 ast_log(LOG_WARNING, "No channel? :(\n");
922 return NULL;
924 #endif
925 if (p->pending)
926 tmp = ast_channel_alloc(0, state, 0, 0, "Agent/P%s-%d", p->agent, ast_random() & 0xffff);
927 else
928 tmp = ast_channel_alloc(0, state, 0, 0, "Agent/%s", p->agent);
929 if (!tmp) {
930 ast_log(LOG_WARNING, "Unable to allocate agent channel structure\n");
931 return NULL;
934 tmp->tech = &agent_tech;
935 if (p->chan) {
936 tmp->nativeformats = p->chan->nativeformats;
937 tmp->writeformat = p->chan->writeformat;
938 tmp->rawwriteformat = p->chan->writeformat;
939 tmp->readformat = p->chan->readformat;
940 tmp->rawreadformat = p->chan->readformat;
941 ast_string_field_set(tmp, language, p->chan->language);
942 ast_copy_string(tmp->context, p->chan->context, sizeof(tmp->context));
943 ast_copy_string(tmp->exten, p->chan->exten, sizeof(tmp->exten));
944 /* XXX Is this really all we copy form the originating channel?? */
945 } else {
946 tmp->nativeformats = AST_FORMAT_SLINEAR;
947 tmp->writeformat = AST_FORMAT_SLINEAR;
948 tmp->rawwriteformat = AST_FORMAT_SLINEAR;
949 tmp->readformat = AST_FORMAT_SLINEAR;
950 tmp->rawreadformat = AST_FORMAT_SLINEAR;
952 /* Safe, agentlock already held */
953 tmp->tech_pvt = p;
954 p->owner = tmp;
955 /* XXX: this needs fixing */
956 #if 0
957 ast_atomic_fetchadd_int(&__mod_desc->usecnt, +1);
958 #endif
959 ast_update_use_count();
960 tmp->priority = 1;
961 /* Wake up and wait for other applications (by definition the login app)
962 * to release this channel). Takes ownership of the agent channel
963 * to this thread only.
964 * For signalling the other thread, ast_queue_frame is used until we
965 * can safely use signals for this purpose. The pselect() needs to be
966 * implemented in the kernel for this.
968 p->app_sleep_cond = 0;
969 if(ast_strlen_zero(p->loginchan) && ast_mutex_trylock(&p->app_lock)) {
970 if (p->chan) {
971 ast_queue_frame(p->chan, &ast_null_frame);
972 ast_mutex_unlock(&p->lock); /* For other thread to read the condition. */
973 ast_mutex_lock(&p->app_lock);
974 ast_mutex_lock(&p->lock);
975 } else {
976 ast_log(LOG_WARNING, "Agent disconnected while we were connecting the call\n");
977 p->owner = NULL;
978 tmp->tech_pvt = NULL;
979 p->app_sleep_cond = 1;
980 ast_channel_free( tmp );
981 ast_mutex_unlock(&p->lock); /* For other thread to read the condition. */
982 ast_mutex_unlock(&p->app_lock);
983 return NULL;
985 } else if (!ast_strlen_zero(p->loginchan)) {
986 if (p->chan)
987 ast_queue_frame(p->chan, &ast_null_frame);
988 if (!p->chan) {
989 ast_log(LOG_WARNING, "Agent disconnected while we were connecting the call\n");
990 p->owner = NULL;
991 tmp->tech_pvt = NULL;
992 p->app_sleep_cond = 1;
993 ast_channel_free( tmp );
994 ast_mutex_unlock(&p->lock); /* For other thread to read the condition. */
995 return NULL;
998 ast_indicate(p->chan, AST_CONTROL_UNHOLD);
999 p->owning_app = pthread_self();
1000 /* After the above step, there should not be any blockers. */
1001 if (p->chan) {
1002 if (ast_test_flag(p->chan, AST_FLAG_BLOCKING)) {
1003 ast_log( LOG_ERROR, "A blocker exists after agent channel ownership acquired\n" );
1004 CRASH;
1007 return tmp;
1012 * Read configuration data. The file named agents.conf.
1014 * \returns Always 0, or so it seems.
1016 static int read_agent_config(void)
1018 struct ast_config *cfg;
1019 struct ast_config *ucfg;
1020 struct ast_variable *v;
1021 struct agent_pvt *p;
1022 const char *general_val;
1023 const char *catname;
1024 const char *hasagent;
1025 int genhasagent;
1027 group = 0;
1028 autologoff = 0;
1029 wrapuptime = 0;
1030 ackcall = 0;
1031 endcall = 1;
1032 cfg = ast_config_load(config);
1033 if (!cfg) {
1034 ast_log(LOG_NOTICE, "No agent configuration found -- agent support disabled\n");
1035 return 0;
1037 AST_LIST_LOCK(&agents);
1038 AST_LIST_TRAVERSE(&agents, p, list) {
1039 p->dead = 1;
1041 strcpy(moh, "default");
1042 /* set the default recording values */
1043 recordagentcalls = 0;
1044 strcpy(recordformat, "wav");
1045 strcpy(recordformatext, "wav");
1046 urlprefix[0] = '\0';
1047 savecallsin[0] = '\0';
1049 /* Read in [general] section for persistence */
1050 if ((general_val = ast_variable_retrieve(cfg, "general", "persistentagents")))
1051 persistent_agents = ast_true(general_val);
1052 multiplelogin = ast_true(ast_variable_retrieve(cfg, "general", "multiplelogin"));
1054 /* Read in the [agents] section */
1055 v = ast_variable_browse(cfg, "agents");
1056 while(v) {
1057 /* Create the interface list */
1058 if (!strcasecmp(v->name, "agent")) {
1059 add_agent(v->value, 0);
1060 } else if (!strcasecmp(v->name, "group")) {
1061 group = ast_get_group(v->value);
1062 } else if (!strcasecmp(v->name, "autologoff")) {
1063 autologoff = atoi(v->value);
1064 if (autologoff < 0)
1065 autologoff = 0;
1066 } else if (!strcasecmp(v->name, "ackcall")) {
1067 if (!strcasecmp(v->value, "always"))
1068 ackcall = 2;
1069 else if (ast_true(v->value))
1070 ackcall = 1;
1071 else
1072 ackcall = 0;
1073 } else if (!strcasecmp(v->name, "endcall")) {
1074 endcall = ast_true(v->value);
1075 } else if (!strcasecmp(v->name, "wrapuptime")) {
1076 wrapuptime = atoi(v->value);
1077 if (wrapuptime < 0)
1078 wrapuptime = 0;
1079 } else if (!strcasecmp(v->name, "maxlogintries") && !ast_strlen_zero(v->value)) {
1080 maxlogintries = atoi(v->value);
1081 if (maxlogintries < 0)
1082 maxlogintries = 0;
1083 } else if (!strcasecmp(v->name, "goodbye") && !ast_strlen_zero(v->value)) {
1084 strcpy(agentgoodbye,v->value);
1085 } else if (!strcasecmp(v->name, "musiconhold")) {
1086 ast_copy_string(moh, v->value, sizeof(moh));
1087 } else if (!strcasecmp(v->name, "updatecdr")) {
1088 if (ast_true(v->value))
1089 updatecdr = 1;
1090 else
1091 updatecdr = 0;
1092 } else if (!strcasecmp(v->name, "autologoffunavail")) {
1093 if (ast_true(v->value))
1094 autologoffunavail = 1;
1095 else
1096 autologoffunavail = 0;
1097 } else if (!strcasecmp(v->name, "recordagentcalls")) {
1098 recordagentcalls = ast_true(v->value);
1099 } else if (!strcasecmp(v->name, "recordformat")) {
1100 ast_copy_string(recordformat, v->value, sizeof(recordformat));
1101 if (!strcasecmp(v->value, "wav49"))
1102 strcpy(recordformatext, "WAV");
1103 else
1104 ast_copy_string(recordformatext, v->value, sizeof(recordformatext));
1105 } else if (!strcasecmp(v->name, "urlprefix")) {
1106 ast_copy_string(urlprefix, v->value, sizeof(urlprefix));
1107 if (urlprefix[strlen(urlprefix) - 1] != '/')
1108 strncat(urlprefix, "/", sizeof(urlprefix) - strlen(urlprefix) - 1);
1109 } else if (!strcasecmp(v->name, "savecallsin")) {
1110 if (v->value[0] == '/')
1111 ast_copy_string(savecallsin, v->value, sizeof(savecallsin));
1112 else
1113 snprintf(savecallsin, sizeof(savecallsin) - 2, "/%s", v->value);
1114 if (savecallsin[strlen(savecallsin) - 1] != '/')
1115 strncat(savecallsin, "/", sizeof(savecallsin) - strlen(savecallsin) - 1);
1116 } else if (!strcasecmp(v->name, "custom_beep")) {
1117 ast_copy_string(beep, v->value, sizeof(beep));
1119 v = v->next;
1121 if ((ucfg = ast_config_load("users.conf"))) {
1122 genhasagent = ast_true(ast_variable_retrieve(ucfg, "general", "hasagent"));
1123 catname = ast_category_browse(ucfg, NULL);
1124 while(catname) {
1125 if (strcasecmp(catname, "general")) {
1126 hasagent = ast_variable_retrieve(ucfg, catname, "hasagent");
1127 if (ast_true(hasagent) || (!hasagent && genhasagent)) {
1128 char tmp[256];
1129 const char *fullname = ast_variable_retrieve(ucfg, catname, "fullname");
1130 const char *secret = ast_variable_retrieve(ucfg, catname, "secret");
1131 if (!fullname)
1132 fullname = "";
1133 if (!secret)
1134 secret = "";
1135 snprintf(tmp, sizeof(tmp), "%s,%s,%s", catname, secret,fullname);
1136 add_agent(tmp, 0);
1139 catname = ast_category_browse(ucfg, catname);
1141 ast_config_destroy(ucfg);
1143 AST_LIST_TRAVERSE_SAFE_BEGIN(&agents, p, list) {
1144 if (p->dead) {
1145 AST_LIST_REMOVE_CURRENT(&agents, list);
1146 /* Destroy if appropriate */
1147 if (!p->owner) {
1148 if (!p->chan) {
1149 ast_mutex_destroy(&p->lock);
1150 ast_mutex_destroy(&p->app_lock);
1151 free(p);
1152 } else {
1153 /* Cause them to hang up */
1154 ast_softhangup(p->chan, AST_SOFTHANGUP_EXPLICIT);
1159 AST_LIST_TRAVERSE_SAFE_END
1160 AST_LIST_UNLOCK(&agents);
1161 ast_config_destroy(cfg);
1162 return 1;
1165 static int check_availability(struct agent_pvt *newlyavailable, int needlock)
1167 struct ast_channel *chan=NULL, *parent=NULL;
1168 struct agent_pvt *p;
1169 int res;
1171 if (option_debug)
1172 ast_log(LOG_DEBUG, "Checking availability of '%s'\n", newlyavailable->agent);
1173 if (needlock)
1174 AST_LIST_LOCK(&agents);
1175 AST_LIST_TRAVERSE(&agents, p, list) {
1176 if (p == newlyavailable) {
1177 continue;
1179 ast_mutex_lock(&p->lock);
1180 if (!p->abouttograb && p->pending && ((p->group && (newlyavailable->group & p->group)) || !strcmp(p->agent, newlyavailable->agent))) {
1181 if (option_debug)
1182 ast_log(LOG_DEBUG, "Call '%s' looks like a winner for agent '%s'\n", p->owner->name, newlyavailable->agent);
1183 /* We found a pending call, time to merge */
1184 chan = agent_new(newlyavailable, AST_STATE_DOWN);
1185 parent = p->owner;
1186 p->abouttograb = 1;
1187 ast_mutex_unlock(&p->lock);
1188 break;
1190 ast_mutex_unlock(&p->lock);
1192 if (needlock)
1193 AST_LIST_UNLOCK(&agents);
1194 if (parent && chan) {
1195 if (newlyavailable->ackcall > 1) {
1196 /* Don't do beep here */
1197 res = 0;
1198 } else {
1199 if (option_debug > 2)
1200 ast_log( LOG_DEBUG, "Playing beep, lang '%s'\n", newlyavailable->chan->language);
1201 res = ast_streamfile(newlyavailable->chan, beep, newlyavailable->chan->language);
1202 if (option_debug > 2)
1203 ast_log( LOG_DEBUG, "Played beep, result '%d'\n", res);
1204 if (!res) {
1205 res = ast_waitstream(newlyavailable->chan, "");
1206 ast_log( LOG_DEBUG, "Waited for stream, result '%d'\n", res);
1209 if (!res) {
1210 /* Note -- parent may have disappeared */
1211 if (p->abouttograb) {
1212 newlyavailable->acknowledged = 1;
1213 /* Safe -- agent lock already held */
1214 ast_setstate(parent, AST_STATE_UP);
1215 ast_setstate(chan, AST_STATE_UP);
1216 ast_copy_string(parent->context, chan->context, sizeof(parent->context));
1217 /* Go ahead and mark the channel as a zombie so that masquerade will
1218 destroy it for us, and we need not call ast_hangup */
1219 ast_mutex_lock(&parent->lock);
1220 ast_set_flag(chan, AST_FLAG_ZOMBIE);
1221 ast_channel_masquerade(parent, chan);
1222 ast_mutex_unlock(&parent->lock);
1223 p->abouttograb = 0;
1224 } else {
1225 if (option_debug)
1226 ast_log(LOG_DEBUG, "Sneaky, parent disappeared in the mean time...\n");
1227 agent_cleanup(newlyavailable);
1229 } else {
1230 if (option_debug)
1231 ast_log(LOG_DEBUG, "Ugh... Agent hung up at exactly the wrong time\n");
1232 agent_cleanup(newlyavailable);
1235 return 0;
1238 static int check_beep(struct agent_pvt *newlyavailable, int needlock)
1240 struct agent_pvt *p;
1241 int res=0;
1243 ast_log(LOG_DEBUG, "Checking beep availability of '%s'\n", newlyavailable->agent);
1244 if (needlock)
1245 AST_LIST_LOCK(&agents);
1246 AST_LIST_TRAVERSE(&agents, p, list) {
1247 if (p == newlyavailable) {
1248 continue;
1250 ast_mutex_lock(&p->lock);
1251 if (!p->abouttograb && p->pending && ((p->group && (newlyavailable->group & p->group)) || !strcmp(p->agent, newlyavailable->agent))) {
1252 if (option_debug)
1253 ast_log(LOG_DEBUG, "Call '%s' looks like a would-be winner for agent '%s'\n", p->owner->name, newlyavailable->agent);
1254 ast_mutex_unlock(&p->lock);
1255 break;
1257 ast_mutex_unlock(&p->lock);
1259 if (needlock)
1260 AST_LIST_UNLOCK(&agents);
1261 if (p) {
1262 ast_mutex_unlock(&newlyavailable->lock);
1263 if (option_debug > 2)
1264 ast_log( LOG_DEBUG, "Playing beep, lang '%s'\n", newlyavailable->chan->language);
1265 res = ast_streamfile(newlyavailable->chan, beep, newlyavailable->chan->language);
1266 if (option_debug > 2)
1267 ast_log( LOG_DEBUG, "Played beep, result '%d'\n", res);
1268 if (!res) {
1269 res = ast_waitstream(newlyavailable->chan, "");
1270 if (option_debug)
1271 ast_log( LOG_DEBUG, "Waited for stream, result '%d'\n", res);
1273 ast_mutex_lock(&newlyavailable->lock);
1275 return res;
1278 /* 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. */
1279 static int allow_multiple_login(char *chan, char *context)
1281 struct agent_pvt *p;
1282 char loginchan[80];
1284 if(multiplelogin)
1285 return 1;
1286 if(!chan)
1287 return 0;
1289 snprintf(loginchan, sizeof(loginchan), "%s@%s", chan, S_OR(context, "default"));
1291 AST_LIST_TRAVERSE(&agents, p, list) {
1292 if(!strcasecmp(chan, p->loginchan))
1293 return 0;
1295 return -1;
1298 /*! \brief Part of the Asterisk PBX interface */
1299 static struct ast_channel *agent_request(const char *type, int format, void *data, int *cause)
1301 struct agent_pvt *p;
1302 struct ast_channel *chan = NULL;
1303 char *s;
1304 ast_group_t groupmatch;
1305 int groupoff;
1306 int waitforagent=0;
1307 int hasagent = 0;
1308 struct timeval tv;
1310 s = data;
1311 if ((s[0] == '@') && (sscanf(s + 1, "%d", &groupoff) == 1)) {
1312 groupmatch = (1 << groupoff);
1313 } else if ((s[0] == ':') && (sscanf(s + 1, "%d", &groupoff) == 1)) {
1314 groupmatch = (1 << groupoff);
1315 waitforagent = 1;
1316 } else
1317 groupmatch = 0;
1319 /* Check actual logged in agents first */
1320 AST_LIST_LOCK(&agents);
1321 AST_LIST_TRAVERSE(&agents, p, list) {
1322 ast_mutex_lock(&p->lock);
1323 if (!p->pending && ((groupmatch && (p->group & groupmatch)) || !strcmp(data, p->agent)) &&
1324 ast_strlen_zero(p->loginchan)) {
1325 if (p->chan)
1326 hasagent++;
1327 if (!p->lastdisc.tv_sec) {
1328 /* Agent must be registered, but not have any active call, and not be in a waiting state */
1329 if (!p->owner && p->chan) {
1330 /* Fixed agent */
1331 chan = agent_new(p, AST_STATE_DOWN);
1333 if (chan) {
1334 ast_mutex_unlock(&p->lock);
1335 break;
1339 ast_mutex_unlock(&p->lock);
1341 if (!p) {
1342 AST_LIST_TRAVERSE(&agents, p, list) {
1343 ast_mutex_lock(&p->lock);
1344 if (!p->pending && ((groupmatch && (p->group & groupmatch)) || !strcmp(data, p->agent))) {
1345 if (p->chan || !ast_strlen_zero(p->loginchan))
1346 hasagent++;
1347 tv = ast_tvnow();
1348 #if 0
1349 ast_log(LOG_NOTICE, "Time now: %ld, Time of lastdisc: %ld\n", tv.tv_sec, p->lastdisc.tv_sec);
1350 #endif
1351 if (!p->lastdisc.tv_sec || (tv.tv_sec > p->lastdisc.tv_sec)) {
1352 p->lastdisc = ast_tv(0, 0);
1353 /* Agent must be registered, but not have any active call, and not be in a waiting state */
1354 if (!p->owner && p->chan) {
1355 /* Could still get a fixed agent */
1356 chan = agent_new(p, AST_STATE_DOWN);
1357 } else if (!p->owner && !ast_strlen_zero(p->loginchan)) {
1358 /* Adjustable agent */
1359 p->chan = ast_request("Local", format, p->loginchan, cause);
1360 if (p->chan)
1361 chan = agent_new(p, AST_STATE_DOWN);
1363 if (chan) {
1364 ast_mutex_unlock(&p->lock);
1365 break;
1369 ast_mutex_unlock(&p->lock);
1373 if (!chan && waitforagent) {
1374 /* No agent available -- but we're requesting to wait for one.
1375 Allocate a place holder */
1376 if (hasagent) {
1377 if (option_debug)
1378 ast_log(LOG_DEBUG, "Creating place holder for '%s'\n", s);
1379 p = add_agent(data, 1);
1380 p->group = groupmatch;
1381 chan = agent_new(p, AST_STATE_DOWN);
1382 if (!chan)
1383 ast_log(LOG_WARNING, "Weird... Fix this to drop the unused pending agent\n");
1384 } else
1385 ast_log(LOG_DEBUG, "Not creating place holder for '%s' since nobody logged in\n", s);
1387 *cause = hasagent ? AST_CAUSE_BUSY : AST_CAUSE_UNREGISTERED;
1388 AST_LIST_UNLOCK(&agents);
1389 return chan;
1392 static force_inline int powerof(unsigned int d)
1394 int x = ffs(d);
1396 if (x)
1397 return x - 1;
1399 return 0;
1403 * Lists agents and their status to the Manager API.
1404 * It is registered on load_module() and it gets called by the manager backend.
1405 * \param s
1406 * \param m
1407 * \returns
1408 * \sa action_agent_logoff(), action_agent_callback_login(), load_module().
1410 static int action_agents(struct mansession *s, const struct message *m)
1412 const char *id = astman_get_header(m,"ActionID");
1413 char idText[256] = "";
1414 char chanbuf[256];
1415 struct agent_pvt *p;
1416 char *username = NULL;
1417 char *loginChan = NULL;
1418 char *talkingtoChan = NULL;
1419 char *status = NULL;
1421 if (!ast_strlen_zero(id))
1422 snprintf(idText, sizeof(idText) ,"ActionID: %s\r\n", id);
1423 astman_send_ack(s, m, "Agents will follow");
1424 AST_LIST_LOCK(&agents);
1425 AST_LIST_TRAVERSE(&agents, p, list) {
1426 ast_mutex_lock(&p->lock);
1428 /* Status Values:
1429 AGENT_LOGGEDOFF - Agent isn't logged in
1430 AGENT_IDLE - Agent is logged in, and waiting for call
1431 AGENT_ONCALL - Agent is logged in, and on a call
1432 AGENT_UNKNOWN - Don't know anything about agent. Shouldn't ever get this. */
1434 username = S_OR(p->name, "None");
1436 /* Set a default status. It 'should' get changed. */
1437 status = "AGENT_UNKNOWN";
1439 if (!ast_strlen_zero(p->loginchan) && !p->chan) {
1440 loginChan = p->loginchan;
1441 talkingtoChan = "n/a";
1442 status = "AGENT_IDLE";
1443 if (p->acknowledged) {
1444 snprintf(chanbuf, sizeof(chanbuf), " %s (Confirmed)", p->loginchan);
1445 loginChan = chanbuf;
1447 } else if (p->chan) {
1448 loginChan = ast_strdupa(p->chan->name);
1449 if (p->owner && p->owner->_bridge) {
1450 talkingtoChan = p->chan->cid.cid_num;
1451 status = "AGENT_ONCALL";
1452 } else {
1453 talkingtoChan = "n/a";
1454 status = "AGENT_IDLE";
1456 } else {
1457 loginChan = "n/a";
1458 talkingtoChan = "n/a";
1459 status = "AGENT_LOGGEDOFF";
1462 astman_append(s, "Event: Agents\r\n"
1463 "Agent: %s\r\n"
1464 "Name: %s\r\n"
1465 "Status: %s\r\n"
1466 "LoggedInChan: %s\r\n"
1467 "LoggedInTime: %d\r\n"
1468 "TalkingTo: %s\r\n"
1469 "%s"
1470 "\r\n",
1471 p->agent, username, status, loginChan, (int)p->loginstart, talkingtoChan, idText);
1472 ast_mutex_unlock(&p->lock);
1474 AST_LIST_UNLOCK(&agents);
1475 astman_append(s, "Event: AgentsComplete\r\n"
1476 "%s"
1477 "\r\n",idText);
1478 return 0;
1481 static void agent_logoff_maintenance(struct agent_pvt *p, char *loginchan, long logintime, const char *uniqueid, char *logcommand)
1483 char *tmp = NULL;
1484 char agent[AST_MAX_AGENT];
1486 if (!ast_strlen_zero(logcommand))
1487 tmp = logcommand;
1488 else
1489 tmp = ast_strdupa("");
1491 snprintf(agent, sizeof(agent), "Agent/%s", p->agent);
1493 if (!ast_strlen_zero(uniqueid)) {
1494 manager_event(EVENT_FLAG_AGENT, "Agentcallbacklogoff",
1495 "Agent: %s\r\n"
1496 "Reason: %s\r\n"
1497 "Loginchan: %s\r\n"
1498 "Logintime: %ld\r\n"
1499 "Uniqueid: %s\r\n",
1500 p->agent, tmp, loginchan, logintime, uniqueid);
1501 } else {
1502 manager_event(EVENT_FLAG_AGENT, "Agentcallbacklogoff",
1503 "Agent: %s\r\n"
1504 "Reason: %s\r\n"
1505 "Loginchan: %s\r\n"
1506 "Logintime: %ld\r\n",
1507 p->agent, tmp, loginchan, logintime);
1510 ast_queue_log("NONE", ast_strlen_zero(uniqueid) ? "NONE" : uniqueid, agent, "AGENTCALLBACKLOGOFF", "%s|%ld|%s", loginchan, logintime, tmp);
1511 set_agentbycallerid(p->logincallerid, NULL);
1512 p->loginchan[0] ='\0';
1513 p->logincallerid[0] = '\0';
1514 ast_device_state_changed("Agent/%s", p->agent);
1515 if (persistent_agents)
1516 dump_agents();
1520 static int agent_logoff(const char *agent, int soft)
1522 struct agent_pvt *p;
1523 long logintime;
1524 int ret = -1; /* Return -1 if no agent if found */
1526 AST_LIST_TRAVERSE(&agents, p, list) {
1527 if (!strcasecmp(p->agent, agent)) {
1528 ret = 0;
1529 if (p->owner || p->chan) {
1530 p->deferlogoff = 1;
1531 if (!soft) {
1532 if (p->owner)
1533 ast_softhangup(p->owner, AST_SOFTHANGUP_EXPLICIT);
1534 if (p->chan)
1535 ast_softhangup(p->chan, AST_SOFTHANGUP_EXPLICIT);
1537 } else {
1538 logintime = time(NULL) - p->loginstart;
1539 p->loginstart = 0;
1540 agent_logoff_maintenance(p, p->loginchan, logintime, NULL, "CommandLogoff");
1542 break;
1546 return ret;
1549 static int agent_logoff_cmd(int fd, int argc, char **argv)
1551 int ret;
1552 char *agent;
1554 if (argc < 3 || argc > 4)
1555 return RESULT_SHOWUSAGE;
1556 if (argc == 4 && strcasecmp(argv[3], "soft"))
1557 return RESULT_SHOWUSAGE;
1559 agent = argv[2] + 6;
1560 ret = agent_logoff(agent, argc == 4);
1561 if (ret == 0)
1562 ast_cli(fd, "Logging out %s\n", agent);
1564 return RESULT_SUCCESS;
1568 * Sets an agent as no longer logged in in the Manager API.
1569 * It is registered on load_module() and it gets called by the manager backend.
1570 * \param s
1571 * \param m
1572 * \returns
1573 * \sa action_agents(), action_agent_callback_login(), load_module().
1575 static int action_agent_logoff(struct mansession *s, const struct message *m)
1577 const char *agent = astman_get_header(m, "Agent");
1578 const char *soft_s = astman_get_header(m, "Soft"); /* "true" is don't hangup */
1579 int soft;
1580 int ret; /* return value of agent_logoff */
1582 if (ast_strlen_zero(agent)) {
1583 astman_send_error(s, m, "No agent specified");
1584 return 0;
1587 soft = ast_true(soft_s) ? 1 : 0;
1588 ret = agent_logoff(agent, soft);
1589 if (ret == 0)
1590 astman_send_ack(s, m, "Agent logged out");
1591 else
1592 astman_send_error(s, m, "No such agent");
1594 return 0;
1597 static char *complete_agent_logoff_cmd(const char *line, const char *word, int pos, int state)
1599 if (pos == 2) {
1600 struct agent_pvt *p;
1601 char name[AST_MAX_AGENT];
1602 int which = 0, len = strlen(word);
1604 AST_LIST_TRAVERSE(&agents, p, list) {
1605 snprintf(name, sizeof(name), "Agent/%s", p->agent);
1606 if (!strncasecmp(word, name, len) && ++which > state)
1607 return ast_strdup(name);
1609 } else if (pos == 3 && state == 0)
1610 return ast_strdup("soft");
1612 return NULL;
1616 * Show agents in cli.
1618 static int agents_show(int fd, int argc, char **argv)
1620 struct agent_pvt *p;
1621 char username[AST_MAX_BUF];
1622 char location[AST_MAX_BUF] = "";
1623 char talkingto[AST_MAX_BUF] = "";
1624 char moh[AST_MAX_BUF];
1625 int count_agents = 0; /*!< Number of agents configured */
1626 int online_agents = 0; /*!< Number of online agents */
1627 int offline_agents = 0; /*!< Number of offline agents */
1628 if (argc != 2)
1629 return RESULT_SHOWUSAGE;
1630 AST_LIST_LOCK(&agents);
1631 AST_LIST_TRAVERSE(&agents, p, list) {
1632 ast_mutex_lock(&p->lock);
1633 if (p->pending) {
1634 if (p->group)
1635 ast_cli(fd, "-- Pending call to group %d\n", powerof(p->group));
1636 else
1637 ast_cli(fd, "-- Pending call to agent %s\n", p->agent);
1638 } else {
1639 if (!ast_strlen_zero(p->name))
1640 snprintf(username, sizeof(username), "(%s) ", p->name);
1641 else
1642 username[0] = '\0';
1643 if (p->chan) {
1644 snprintf(location, sizeof(location), "logged in on %s", p->chan->name);
1645 if (p->owner && ast_bridged_channel(p->owner))
1646 snprintf(talkingto, sizeof(talkingto), " talking to %s", ast_bridged_channel(p->owner)->name);
1647 else
1648 strcpy(talkingto, " is idle");
1649 online_agents++;
1650 } else if (!ast_strlen_zero(p->loginchan)) {
1651 if (ast_tvdiff_ms(ast_tvnow(), p->lastdisc) > 0 || !(p->lastdisc.tv_sec))
1652 snprintf(location, sizeof(location) - 20, "available at '%s'", p->loginchan);
1653 else
1654 snprintf(location, sizeof(location) - 20, "wrapping up at '%s'", p->loginchan);
1655 talkingto[0] = '\0';
1656 online_agents++;
1657 if (p->acknowledged)
1658 strncat(location, " (Confirmed)", sizeof(location) - strlen(location) - 1);
1659 } else {
1660 strcpy(location, "not logged in");
1661 talkingto[0] = '\0';
1662 offline_agents++;
1664 if (!ast_strlen_zero(p->moh))
1665 snprintf(moh, sizeof(moh), " (musiconhold is '%s')", p->moh);
1666 ast_cli(fd, "%-12.12s %s%s%s%s\n", p->agent,
1667 username, location, talkingto, moh);
1668 count_agents++;
1670 ast_mutex_unlock(&p->lock);
1672 AST_LIST_UNLOCK(&agents);
1673 if ( !count_agents )
1674 ast_cli(fd, "No Agents are configured in %s\n",config);
1675 else
1676 ast_cli(fd, "%d agents configured [%d online , %d offline]\n",count_agents, online_agents, offline_agents);
1677 ast_cli(fd, "\n");
1679 return RESULT_SUCCESS;
1683 static int agents_show_online(int fd, int argc, char **argv)
1685 struct agent_pvt *p;
1686 char username[AST_MAX_BUF];
1687 char location[AST_MAX_BUF] = "";
1688 char talkingto[AST_MAX_BUF] = "";
1689 char moh[AST_MAX_BUF];
1690 int count_agents = 0; /* Number of agents configured */
1691 int online_agents = 0; /* Number of online agents */
1692 int agent_status = 0; /* 0 means offline, 1 means online */
1693 if (argc != 3)
1694 return RESULT_SHOWUSAGE;
1695 AST_LIST_LOCK(&agents);
1696 AST_LIST_TRAVERSE(&agents, p, list) {
1697 agent_status = 0; /* reset it to offline */
1698 ast_mutex_lock(&p->lock);
1699 if (!ast_strlen_zero(p->name))
1700 snprintf(username, sizeof(username), "(%s) ", p->name);
1701 else
1702 username[0] = '\0';
1703 if (p->chan) {
1704 snprintf(location, sizeof(location), "logged in on %s", p->chan->name);
1705 if (p->owner && ast_bridged_channel(p->owner))
1706 snprintf(talkingto, sizeof(talkingto), " talking to %s", ast_bridged_channel(p->owner)->name);
1707 else
1708 strcpy(talkingto, " is idle");
1709 agent_status = 1;
1710 online_agents++;
1711 } else if (!ast_strlen_zero(p->loginchan)) {
1712 snprintf(location, sizeof(location) - 20, "available at '%s'", p->loginchan);
1713 talkingto[0] = '\0';
1714 agent_status = 1;
1715 online_agents++;
1716 if (p->acknowledged)
1717 strncat(location, " (Confirmed)", sizeof(location) - strlen(location) - 1);
1719 if (!ast_strlen_zero(p->moh))
1720 snprintf(moh, sizeof(moh), " (musiconhold is '%s')", p->moh);
1721 if (agent_status)
1722 ast_cli(fd, "%-12.12s %s%s%s%s\n", p->agent, username, location, talkingto, moh);
1723 count_agents++;
1724 ast_mutex_unlock(&p->lock);
1726 AST_LIST_UNLOCK(&agents);
1727 if (!count_agents)
1728 ast_cli(fd, "No Agents are configured in %s\n", config);
1729 else
1730 ast_cli(fd, "%d agents online\n", online_agents);
1731 ast_cli(fd, "\n");
1732 return RESULT_SUCCESS;
1737 static char show_agents_usage[] =
1738 "Usage: agent show\n"
1739 " Provides summary information on agents.\n";
1741 static char show_agents_online_usage[] =
1742 "Usage: agent show online\n"
1743 " Provides a list of all online agents.\n";
1745 static char agent_logoff_usage[] =
1746 "Usage: agent logoff <channel> [soft]\n"
1747 " Sets an agent as no longer logged in.\n"
1748 " If 'soft' is specified, do not hangup existing calls.\n";
1750 static struct ast_cli_entry cli_show_agents_deprecated = {
1751 { "show", "agents", NULL },
1752 agents_show, NULL,
1753 NULL, NULL };
1755 static struct ast_cli_entry cli_show_agents_online_deprecated = {
1756 { "show", "agents", "online" },
1757 agents_show_online, NULL,
1758 NULL, NULL };
1760 static struct ast_cli_entry cli_agents[] = {
1761 { { "agent", "show", NULL },
1762 agents_show, "Show status of agents",
1763 show_agents_usage, NULL, &cli_show_agents_deprecated },
1765 { { "agent", "show", "online" },
1766 agents_show_online, "Show all online agents",
1767 show_agents_online_usage, NULL, &cli_show_agents_online_deprecated },
1769 { { "agent", "logoff", NULL },
1770 agent_logoff_cmd, "Sets an agent offline",
1771 agent_logoff_usage, complete_agent_logoff_cmd },
1775 * \brief Log in agent application.
1777 * \param chan
1778 * \param data
1779 * \param callbackmode non-zero for AgentCallbackLogin
1781 static int __login_exec(struct ast_channel *chan, void *data, int callbackmode)
1783 int res=0;
1784 int tries = 0;
1785 int max_login_tries = maxlogintries;
1786 struct agent_pvt *p;
1787 struct ast_module_user *u;
1788 int login_state = 0;
1789 char user[AST_MAX_AGENT] = "";
1790 char pass[AST_MAX_AGENT];
1791 char agent[AST_MAX_AGENT] = "";
1792 char xpass[AST_MAX_AGENT] = "";
1793 char *errmsg;
1794 char *parse;
1795 AST_DECLARE_APP_ARGS(args,
1796 AST_APP_ARG(agent_id);
1797 AST_APP_ARG(options);
1798 AST_APP_ARG(extension);
1800 const char *tmpoptions = NULL;
1801 char *context = NULL;
1802 int play_announcement = 1;
1803 char agent_goodbye[AST_MAX_FILENAME_LEN];
1804 int update_cdr = updatecdr;
1805 char *filename = "agent-loginok";
1806 char tmpchan[AST_MAX_BUF] = "";
1808 u = ast_module_user_add(chan);
1810 parse = ast_strdupa(data);
1812 AST_STANDARD_APP_ARGS(args, parse);
1814 ast_copy_string(agent_goodbye, agentgoodbye, sizeof(agent_goodbye));
1816 /* Set Channel Specific Login Overrides */
1817 if (pbx_builtin_getvar_helper(chan, "AGENTLMAXLOGINTRIES") && strlen(pbx_builtin_getvar_helper(chan, "AGENTLMAXLOGINTRIES"))) {
1818 max_login_tries = atoi(pbx_builtin_getvar_helper(chan, "AGENTMAXLOGINTRIES"));
1819 if (max_login_tries < 0)
1820 max_login_tries = 0;
1821 tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTMAXLOGINTRIES");
1822 if (option_verbose > 2)
1823 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);
1825 if (pbx_builtin_getvar_helper(chan, "AGENTUPDATECDR") && !ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENTUPDATECDR"))) {
1826 if (ast_true(pbx_builtin_getvar_helper(chan, "AGENTUPDATECDR")))
1827 update_cdr = 1;
1828 else
1829 update_cdr = 0;
1830 tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTUPDATECDR");
1831 if (option_verbose > 2)
1832 ast_verbose(VERBOSE_PREFIX_3 "Saw variable AGENTUPDATECDR=%s, setting update_cdr to: %d on Channel '%s'.\n",tmpoptions,update_cdr,chan->name);
1834 if (pbx_builtin_getvar_helper(chan, "AGENTGOODBYE") && !ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENTGOODBYE"))) {
1835 strcpy(agent_goodbye, pbx_builtin_getvar_helper(chan, "AGENTGOODBYE"));
1836 tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTGOODBYE");
1837 if (option_verbose > 2)
1838 ast_verbose(VERBOSE_PREFIX_3 "Saw variable AGENTGOODBYE=%s, setting agent_goodbye to: %s on Channel '%s'.\n",tmpoptions,agent_goodbye,chan->name);
1840 /* End Channel Specific Login Overrides */
1842 if (callbackmode && args.extension) {
1843 parse = args.extension;
1844 args.extension = strsep(&parse, "@");
1845 context = parse;
1848 if (!ast_strlen_zero(args.options)) {
1849 if (strchr(args.options, 's')) {
1850 play_announcement = 0;
1854 if (chan->_state != AST_STATE_UP)
1855 res = ast_answer(chan);
1856 if (!res) {
1857 if (!ast_strlen_zero(args.agent_id))
1858 ast_copy_string(user, args.agent_id, AST_MAX_AGENT);
1859 else
1860 res = ast_app_getdata(chan, "agent-user", user, sizeof(user) - 1, 0);
1862 while (!res && (max_login_tries==0 || tries < max_login_tries)) {
1863 tries++;
1864 /* Check for password */
1865 AST_LIST_LOCK(&agents);
1866 AST_LIST_TRAVERSE(&agents, p, list) {
1867 if (!strcmp(p->agent, user) && !p->pending)
1868 ast_copy_string(xpass, p->password, sizeof(xpass));
1870 AST_LIST_UNLOCK(&agents);
1871 if (!res) {
1872 if (!ast_strlen_zero(xpass))
1873 res = ast_app_getdata(chan, "agent-pass", pass, sizeof(pass) - 1, 0);
1874 else
1875 pass[0] = '\0';
1877 errmsg = "agent-incorrect";
1879 #if 0
1880 ast_log(LOG_NOTICE, "user: %s, pass: %s\n", user, pass);
1881 #endif
1883 /* Check again for accuracy */
1884 AST_LIST_LOCK(&agents);
1885 AST_LIST_TRAVERSE(&agents, p, list) {
1886 ast_mutex_lock(&p->lock);
1887 if (!strcmp(p->agent, user) &&
1888 !strcmp(p->password, pass) && !p->pending) {
1889 login_state = 1; /* Successful Login */
1891 /* Ensure we can't be gotten until we're done */
1892 gettimeofday(&p->lastdisc, NULL);
1893 p->lastdisc.tv_sec++;
1895 /* Set Channel Specific Agent Overrides */
1896 if (pbx_builtin_getvar_helper(chan, "AGENTACKCALL") && strlen(pbx_builtin_getvar_helper(chan, "AGENTACKCALL"))) {
1897 if (!strcasecmp(pbx_builtin_getvar_helper(chan, "AGENTACKCALL"), "always"))
1898 p->ackcall = 2;
1899 else if (ast_true(pbx_builtin_getvar_helper(chan, "AGENTACKCALL")))
1900 p->ackcall = 1;
1901 else
1902 p->ackcall = 0;
1903 tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTACKCALL");
1904 if (option_verbose > 2)
1905 ast_verbose(VERBOSE_PREFIX_3 "Saw variable AGENTACKCALL=%s, setting ackcall to: %d for Agent '%s'.\n",tmpoptions,p->ackcall,p->agent);
1907 if (pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF") && strlen(pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF"))) {
1908 p->autologoff = atoi(pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF"));
1909 if (p->autologoff < 0)
1910 p->autologoff = 0;
1911 tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF");
1912 if (option_verbose > 2)
1913 ast_verbose(VERBOSE_PREFIX_3 "Saw variable AGENTAUTOLOGOFF=%s, setting autologff to: %d for Agent '%s'.\n",tmpoptions,p->autologoff,p->agent);
1915 if (pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME") && strlen(pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME"))) {
1916 p->wrapuptime = atoi(pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME"));
1917 if (p->wrapuptime < 0)
1918 p->wrapuptime = 0;
1919 tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME");
1920 if (option_verbose > 2)
1921 ast_verbose(VERBOSE_PREFIX_3 "Saw variable AGENTWRAPUPTIME=%s, setting wrapuptime to: %d for Agent '%s'.\n",tmpoptions,p->wrapuptime,p->agent);
1923 /* End Channel Specific Agent Overrides */
1924 if (!p->chan) {
1925 char last_loginchan[80] = "";
1926 long logintime;
1927 snprintf(agent, sizeof(agent), "Agent/%s", p->agent);
1929 if (callbackmode) {
1930 int pos = 0;
1931 /* Retrieve login chan */
1932 for (;;) {
1933 if (!ast_strlen_zero(args.extension)) {
1934 ast_copy_string(tmpchan, args.extension, sizeof(tmpchan));
1935 res = 0;
1936 } else
1937 res = ast_app_getdata(chan, "agent-newlocation", tmpchan+pos, sizeof(tmpchan) - 2, 0);
1938 if (ast_strlen_zero(tmpchan) )
1939 break;
1940 if(ast_exists_extension(chan, S_OR(context,"default"), tmpchan,1, NULL) ) {
1941 if(!allow_multiple_login(tmpchan,context) ) {
1942 args.extension = NULL;
1943 pos = 0;
1944 } else
1945 break;
1947 if (args.extension) {
1948 ast_log(LOG_WARNING, "Extension '%s' is not valid for automatic login of agent '%s'\n", args.extension, p->agent);
1949 args.extension = NULL;
1950 pos = 0;
1951 } else {
1952 ast_log(LOG_WARNING, "Extension '%s@%s' is not valid for automatic login of agent '%s'\n", tmpchan, S_OR(context, "default"), p->agent);
1953 res = ast_streamfile(chan, "invalid", chan->language);
1954 if (!res)
1955 res = ast_waitstream(chan, AST_DIGIT_ANY);
1956 if (res > 0) {
1957 tmpchan[0] = res;
1958 tmpchan[1] = '\0';
1959 pos = 1;
1960 } else {
1961 tmpchan[0] = '\0';
1962 pos = 0;
1966 args.extension = tmpchan;
1967 if (!res) {
1968 set_agentbycallerid(p->logincallerid, NULL);
1969 if (!ast_strlen_zero(context) && !ast_strlen_zero(tmpchan))
1970 snprintf(p->loginchan, sizeof(p->loginchan), "%s@%s", tmpchan, context);
1971 else {
1972 ast_copy_string(last_loginchan, p->loginchan, sizeof(last_loginchan));
1973 ast_copy_string(p->loginchan, tmpchan, sizeof(p->loginchan));
1975 p->acknowledged = 0;
1976 if (ast_strlen_zero(p->loginchan)) {
1977 login_state = 2;
1978 filename = "agent-loggedoff";
1979 } else {
1980 if (chan->cid.cid_num) {
1981 ast_copy_string(p->logincallerid, chan->cid.cid_num, sizeof(p->logincallerid));
1982 set_agentbycallerid(p->logincallerid, p->agent);
1983 } else
1984 p->logincallerid[0] = '\0';
1987 if(update_cdr && chan->cdr)
1988 snprintf(chan->cdr->channel, sizeof(chan->cdr->channel), "Agent/%s", p->agent);
1991 } else {
1992 p->loginchan[0] = '\0';
1993 p->logincallerid[0] = '\0';
1994 p->acknowledged = 0;
1996 ast_mutex_unlock(&p->lock);
1997 AST_LIST_UNLOCK(&agents);
1998 if( !res && play_announcement==1 )
1999 res = ast_streamfile(chan, filename, chan->language);
2000 if (!res)
2001 ast_waitstream(chan, "");
2002 AST_LIST_LOCK(&agents);
2003 ast_mutex_lock(&p->lock);
2004 if (!res) {
2005 res = ast_set_read_format(chan, ast_best_codec(chan->nativeformats));
2006 if (res)
2007 ast_log(LOG_WARNING, "Unable to set read format to %d\n", ast_best_codec(chan->nativeformats));
2009 if (!res) {
2010 res = ast_set_write_format(chan, ast_best_codec(chan->nativeformats));
2011 if (res)
2012 ast_log(LOG_WARNING, "Unable to set write format to %d\n", ast_best_codec(chan->nativeformats));
2014 /* Check once more just in case */
2015 if (p->chan)
2016 res = -1;
2017 if (callbackmode && !res) {
2018 /* Just say goodbye and be done with it */
2019 if (!ast_strlen_zero(p->loginchan)) {
2020 if (p->loginstart == 0)
2021 time(&p->loginstart);
2022 manager_event(EVENT_FLAG_AGENT, "Agentcallbacklogin",
2023 "Agent: %s\r\n"
2024 "Loginchan: %s\r\n"
2025 "Uniqueid: %s\r\n",
2026 p->agent, p->loginchan, chan->uniqueid);
2027 ast_queue_log("NONE", chan->uniqueid, agent, "AGENTCALLBACKLOGIN", "%s", p->loginchan);
2028 if (option_verbose > 1)
2029 ast_verbose(VERBOSE_PREFIX_2 "Callback Agent '%s' logged in on %s\n", p->agent, p->loginchan);
2030 ast_device_state_changed("Agent/%s", p->agent);
2031 if (persistent_agents)
2032 dump_agents();
2033 } else {
2034 logintime = time(NULL) - p->loginstart;
2035 p->loginstart = 0;
2037 agent_logoff_maintenance(p, last_loginchan, logintime, chan->uniqueid, NULL);
2038 if (option_verbose > 1)
2039 ast_verbose(VERBOSE_PREFIX_2 "Callback Agent '%s' logged out\n", p->agent);
2041 AST_LIST_UNLOCK(&agents);
2042 if (!res)
2043 res = ast_safe_sleep(chan, 500);
2044 ast_mutex_unlock(&p->lock);
2045 } else if (!res) {
2046 ast_indicate_data(chan, AST_CONTROL_HOLD,
2047 S_OR(p->moh, NULL),
2048 !ast_strlen_zero(p->moh) ? strlen(p->moh) + 1 : 0);
2049 if (p->loginstart == 0)
2050 time(&p->loginstart);
2051 manager_event(EVENT_FLAG_AGENT, "Agentlogin",
2052 "Agent: %s\r\n"
2053 "Channel: %s\r\n"
2054 "Uniqueid: %s\r\n",
2055 p->agent, chan->name, chan->uniqueid);
2056 if (update_cdr && chan->cdr)
2057 snprintf(chan->cdr->channel, sizeof(chan->cdr->channel), "Agent/%s", p->agent);
2058 ast_queue_log("NONE", chan->uniqueid, agent, "AGENTLOGIN", "%s", chan->name);
2059 if (option_verbose > 1)
2060 ast_verbose(VERBOSE_PREFIX_2 "Agent '%s' logged in (format %s/%s)\n", p->agent,
2061 ast_getformatname(chan->readformat), ast_getformatname(chan->writeformat));
2062 /* Login this channel and wait for it to go away */
2063 p->chan = chan;
2064 if (p->ackcall > 1)
2065 check_beep(p, 0);
2066 else
2067 check_availability(p, 0);
2068 ast_mutex_unlock(&p->lock);
2069 AST_LIST_UNLOCK(&agents);
2070 ast_device_state_changed("Agent/%s", p->agent);
2071 while (res >= 0) {
2072 ast_mutex_lock(&p->lock);
2073 if (p->chan != chan)
2074 res = -1;
2075 ast_mutex_unlock(&p->lock);
2076 /* Yield here so other interested threads can kick in. */
2077 sched_yield();
2078 if (res)
2079 break;
2081 AST_LIST_LOCK(&agents);
2082 ast_mutex_lock(&p->lock);
2083 if (p->lastdisc.tv_sec) {
2084 if (ast_tvdiff_ms(ast_tvnow(), p->lastdisc) > p->wrapuptime) {
2085 if (option_debug)
2086 ast_log(LOG_DEBUG, "Wrapup time for %s expired!\n", p->agent);
2087 p->lastdisc = ast_tv(0, 0);
2088 if (p->ackcall > 1)
2089 check_beep(p, 0);
2090 else
2091 check_availability(p, 0);
2094 ast_mutex_unlock(&p->lock);
2095 AST_LIST_UNLOCK(&agents);
2096 /* Synchronize channel ownership between call to agent and itself. */
2097 ast_mutex_lock( &p->app_lock );
2098 ast_mutex_lock(&p->lock);
2099 p->owning_app = pthread_self();
2100 ast_mutex_unlock(&p->lock);
2101 if (p->ackcall > 1)
2102 res = agent_ack_sleep(p);
2103 else
2104 res = ast_safe_sleep_conditional( chan, 1000, agent_cont_sleep, p );
2105 ast_mutex_unlock( &p->app_lock );
2106 if ((p->ackcall > 1) && (res == 1)) {
2107 AST_LIST_LOCK(&agents);
2108 ast_mutex_lock(&p->lock);
2109 check_availability(p, 0);
2110 ast_mutex_unlock(&p->lock);
2111 AST_LIST_UNLOCK(&agents);
2112 res = 0;
2114 sched_yield();
2116 ast_mutex_lock(&p->lock);
2117 if (res && p->owner)
2118 ast_log(LOG_WARNING, "Huh? We broke out when there was still an owner?\n");
2119 /* Log us off if appropriate */
2120 if (p->chan == chan)
2121 p->chan = NULL;
2122 p->acknowledged = 0;
2123 logintime = time(NULL) - p->loginstart;
2124 p->loginstart = 0;
2125 ast_mutex_unlock(&p->lock);
2126 manager_event(EVENT_FLAG_AGENT, "Agentlogoff",
2127 "Agent: %s\r\n"
2128 "Logintime: %ld\r\n"
2129 "Uniqueid: %s\r\n",
2130 p->agent, logintime, chan->uniqueid);
2131 ast_queue_log("NONE", chan->uniqueid, agent, "AGENTLOGOFF", "%s|%ld", chan->name, logintime);
2132 if (option_verbose > 1)
2133 ast_verbose(VERBOSE_PREFIX_2 "Agent '%s' logged out\n", p->agent);
2134 /* If there is no owner, go ahead and kill it now */
2135 ast_device_state_changed("Agent/%s", p->agent);
2136 if (p->dead && !p->owner) {
2137 ast_mutex_destroy(&p->lock);
2138 ast_mutex_destroy(&p->app_lock);
2139 free(p);
2142 else {
2143 ast_mutex_unlock(&p->lock);
2144 p = NULL;
2146 res = -1;
2147 } else {
2148 ast_mutex_unlock(&p->lock);
2149 errmsg = "agent-alreadyon";
2150 p = NULL;
2152 break;
2154 ast_mutex_unlock(&p->lock);
2156 if (!p)
2157 AST_LIST_UNLOCK(&agents);
2159 if (!res && (max_login_tries==0 || tries < max_login_tries))
2160 res = ast_app_getdata(chan, errmsg, user, sizeof(user) - 1, 0);
2163 if (!res)
2164 res = ast_safe_sleep(chan, 500);
2166 /* AgentLogin() exit */
2167 if (!callbackmode) {
2168 ast_module_user_remove(u);
2169 return -1;
2170 } else { /* AgentCallbackLogin() exit*/
2171 /* Set variables */
2172 if (login_state > 0) {
2173 pbx_builtin_setvar_helper(chan, "AGENTNUMBER", user);
2174 if (login_state==1) {
2175 pbx_builtin_setvar_helper(chan, "AGENTSTATUS", "on");
2176 pbx_builtin_setvar_helper(chan, "AGENTEXTEN", args.extension);
2177 } else
2178 pbx_builtin_setvar_helper(chan, "AGENTSTATUS", "off");
2179 } else {
2180 pbx_builtin_setvar_helper(chan, "AGENTSTATUS", "fail");
2182 if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 1, chan->cid.cid_num)) {
2183 ast_module_user_remove(u);
2184 return 0;
2186 /* Do we need to play agent-goodbye now that we will be hanging up? */
2187 if (play_announcement) {
2188 if (!res)
2189 res = ast_safe_sleep(chan, 1000);
2190 res = ast_streamfile(chan, agent_goodbye, chan->language);
2191 if (!res)
2192 res = ast_waitstream(chan, "");
2193 if (!res)
2194 res = ast_safe_sleep(chan, 1000);
2198 ast_module_user_remove(u);
2200 /* We should never get here if next priority exists when in callbackmode */
2201 return -1;
2205 * Called by the AgentLogin application (from the dial plan).
2207 * \param chan
2208 * \param data
2209 * \returns
2210 * \sa callback_login_exec(), agentmonitoroutgoing_exec(), load_module().
2212 static int login_exec(struct ast_channel *chan, void *data)
2214 return __login_exec(chan, data, 0);
2217 static void callback_deprecated(void)
2219 static int depwarning = 0;
2221 if (!depwarning) {
2222 depwarning = 1;
2224 ast_log(LOG_WARNING, "AgentCallbackLogin is deprecated and will be removed in a future release.\n");
2225 ast_log(LOG_WARNING, "See doc/queues-with-callback-members.txt for an example of how to achieve\n");
2226 ast_log(LOG_WARNING, "the same functionality using only dialplan logic.\n");
2231 * Called by the AgentCallbackLogin application (from the dial plan).
2233 * \param chan
2234 * \param data
2235 * \returns
2236 * \sa login_exec(), agentmonitoroutgoing_exec(), load_module().
2238 static int callback_exec(struct ast_channel *chan, void *data)
2240 callback_deprecated();
2242 return __login_exec(chan, data, 1);
2246 * Sets an agent as logged in by callback in the Manager API.
2247 * It is registered on load_module() and it gets called by the manager backend.
2248 * \param s
2249 * \param m
2250 * \returns
2251 * \sa action_agents(), action_agent_logoff(), load_module().
2253 static int action_agent_callback_login(struct mansession *s, const struct message *m)
2255 const char *agent = astman_get_header(m, "Agent");
2256 const char *exten = astman_get_header(m, "Exten");
2257 const char *context = astman_get_header(m, "Context");
2258 const char *wrapuptime_s = astman_get_header(m, "WrapupTime");
2259 const char *ackcall_s = astman_get_header(m, "AckCall");
2260 struct agent_pvt *p;
2261 int login_state = 0;
2263 callback_deprecated();
2265 if (ast_strlen_zero(agent)) {
2266 astman_send_error(s, m, "No agent specified");
2267 return 0;
2270 if (ast_strlen_zero(exten)) {
2271 astman_send_error(s, m, "No extension specified");
2272 return 0;
2275 AST_LIST_LOCK(&agents);
2276 AST_LIST_TRAVERSE(&agents, p, list) {
2277 if (strcmp(p->agent, agent) || p->pending)
2278 continue;
2279 if (p->chan) {
2280 login_state = 2; /* already logged in (and on the phone)*/
2281 break;
2283 ast_mutex_lock(&p->lock);
2284 login_state = 1; /* Successful Login */
2286 if (ast_strlen_zero(context))
2287 ast_copy_string(p->loginchan, exten, sizeof(p->loginchan));
2288 else
2289 snprintf(p->loginchan, sizeof(p->loginchan), "%s@%s", exten, context);
2291 if (!ast_strlen_zero(wrapuptime_s)) {
2292 p->wrapuptime = atoi(wrapuptime_s);
2293 if (p->wrapuptime < 0)
2294 p->wrapuptime = 0;
2297 if (ast_true(ackcall_s))
2298 p->ackcall = 1;
2299 else
2300 p->ackcall = 0;
2302 if (p->loginstart == 0)
2303 time(&p->loginstart);
2304 manager_event(EVENT_FLAG_AGENT, "Agentcallbacklogin",
2305 "Agent: %s\r\n"
2306 "Loginchan: %s\r\n",
2307 p->agent, p->loginchan);
2308 ast_queue_log("NONE", "NONE", agent, "AGENTCALLBACKLOGIN", "%s", p->loginchan);
2309 if (option_verbose > 1)
2310 ast_verbose(VERBOSE_PREFIX_2 "Callback Agent '%s' logged in on %s\n", p->agent, p->loginchan);
2311 ast_device_state_changed("Agent/%s", p->agent);
2312 ast_mutex_unlock(&p->lock);
2313 if (persistent_agents)
2314 dump_agents();
2316 AST_LIST_UNLOCK(&agents);
2318 if (login_state == 1)
2319 astman_send_ack(s, m, "Agent logged in");
2320 else if (login_state == 0)
2321 astman_send_error(s, m, "No such agent");
2322 else if (login_state == 2)
2323 astman_send_error(s, m, "Agent already logged in");
2325 return 0;
2329 * \brief Called by the AgentMonitorOutgoing application (from the dial plan).
2331 * \param chan
2332 * \param data
2333 * \returns
2334 * \sa login_exec(), callback_login_exec(), load_module().
2336 static int agentmonitoroutgoing_exec(struct ast_channel *chan, void *data)
2338 int exitifnoagentid = 0;
2339 int nowarnings = 0;
2340 int changeoutgoing = 0;
2341 int res = 0;
2342 char agent[AST_MAX_AGENT];
2344 if (data) {
2345 if (strchr(data, 'd'))
2346 exitifnoagentid = 1;
2347 if (strchr(data, 'n'))
2348 nowarnings = 1;
2349 if (strchr(data, 'c'))
2350 changeoutgoing = 1;
2352 if (chan->cid.cid_num) {
2353 const char *tmp;
2354 char agentvar[AST_MAX_BUF];
2355 snprintf(agentvar, sizeof(agentvar), "%s_%s", GETAGENTBYCALLERID, chan->cid.cid_num);
2356 if ((tmp = pbx_builtin_getvar_helper(NULL, agentvar))) {
2357 struct agent_pvt *p;
2358 ast_copy_string(agent, tmp, sizeof(agent));
2359 AST_LIST_LOCK(&agents);
2360 AST_LIST_TRAVERSE(&agents, p, list) {
2361 if (!strcasecmp(p->agent, tmp)) {
2362 if (changeoutgoing) snprintf(chan->cdr->channel, sizeof(chan->cdr->channel), "Agent/%s", p->agent);
2363 __agent_start_monitoring(chan, p, 1);
2364 break;
2367 AST_LIST_UNLOCK(&agents);
2369 } else {
2370 res = -1;
2371 if (!nowarnings)
2372 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);
2374 } else {
2375 res = -1;
2376 if (!nowarnings)
2377 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");
2379 /* check if there is n + 101 priority */
2380 /*! \todo XXX Needs to check option priorityjump etc etc */
2381 if (res) {
2382 if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 101, chan->cid.cid_num)) {
2383 chan->priority+=100;
2384 if (option_verbose > 2)
2385 ast_verbose(VERBOSE_PREFIX_3 "Going to %d priority because there is no callerid or the agentid cannot be found.\n",chan->priority);
2386 } else if (exitifnoagentid)
2387 return res;
2389 return 0;
2393 * \brief Dump AgentCallbackLogin agents to the ASTdb database for persistence
2395 static void dump_agents(void)
2397 struct agent_pvt *cur_agent = NULL;
2398 char buf[256];
2400 AST_LIST_TRAVERSE(&agents, cur_agent, list) {
2401 if (cur_agent->chan)
2402 continue;
2404 if (!ast_strlen_zero(cur_agent->loginchan)) {
2405 snprintf(buf, sizeof(buf), "%s;%s", cur_agent->loginchan, cur_agent->logincallerid);
2406 if (ast_db_put(pa_family, cur_agent->agent, buf))
2407 ast_log(LOG_WARNING, "failed to create persistent entry in ASTdb for %s!\n", buf);
2408 else if (option_debug)
2409 ast_log(LOG_DEBUG, "Saved Agent: %s on %s\n", cur_agent->agent, cur_agent->loginchan);
2410 } else {
2411 /* Delete - no agent or there is an error */
2412 ast_db_del(pa_family, cur_agent->agent);
2418 * \brief Reload the persistent agents from astdb.
2420 static void reload_agents(void)
2422 char *agent_num;
2423 struct ast_db_entry *db_tree;
2424 struct ast_db_entry *entry;
2425 struct agent_pvt *cur_agent;
2426 char agent_data[256];
2427 char *parse;
2428 char *agent_chan;
2429 char *agent_callerid;
2431 db_tree = ast_db_gettree(pa_family, NULL);
2433 AST_LIST_LOCK(&agents);
2434 for (entry = db_tree; entry; entry = entry->next) {
2435 agent_num = entry->key + strlen(pa_family) + 2;
2436 AST_LIST_TRAVERSE(&agents, cur_agent, list) {
2437 ast_mutex_lock(&cur_agent->lock);
2438 if (strcmp(agent_num, cur_agent->agent) == 0)
2439 break;
2440 ast_mutex_unlock(&cur_agent->lock);
2442 if (!cur_agent) {
2443 ast_db_del(pa_family, agent_num);
2444 continue;
2445 } else
2446 ast_mutex_unlock(&cur_agent->lock);
2447 if (!ast_db_get(pa_family, agent_num, agent_data, sizeof(agent_data)-1)) {
2448 if (option_debug)
2449 ast_log(LOG_DEBUG, "Reload Agent from AstDB: %s on %s\n", cur_agent->agent, agent_data);
2450 parse = agent_data;
2451 agent_chan = strsep(&parse, ";");
2452 agent_callerid = strsep(&parse, ";");
2453 ast_copy_string(cur_agent->loginchan, agent_chan, sizeof(cur_agent->loginchan));
2454 if (agent_callerid) {
2455 ast_copy_string(cur_agent->logincallerid, agent_callerid, sizeof(cur_agent->logincallerid));
2456 set_agentbycallerid(cur_agent->logincallerid, cur_agent->agent);
2457 } else
2458 cur_agent->logincallerid[0] = '\0';
2459 if (cur_agent->loginstart == 0)
2460 time(&cur_agent->loginstart);
2461 ast_device_state_changed("Agent/%s", cur_agent->agent);
2464 AST_LIST_UNLOCK(&agents);
2465 if (db_tree) {
2466 ast_log(LOG_NOTICE, "Agents successfully reloaded from database.\n");
2467 ast_db_freetree(db_tree);
2471 /*! \brief Part of PBX channel interface */
2472 static int agent_devicestate(void *data)
2474 struct agent_pvt *p;
2475 char *s;
2476 ast_group_t groupmatch;
2477 int groupoff;
2478 int waitforagent=0;
2479 int res = AST_DEVICE_INVALID;
2481 s = data;
2482 if ((s[0] == '@') && (sscanf(s + 1, "%d", &groupoff) == 1))
2483 groupmatch = (1 << groupoff);
2484 else if ((s[0] == ':') && (sscanf(s + 1, "%d", &groupoff) == 1)) {
2485 groupmatch = (1 << groupoff);
2486 waitforagent = 1;
2487 } else
2488 groupmatch = 0;
2490 /* Check actual logged in agents first */
2491 AST_LIST_LOCK(&agents);
2492 AST_LIST_TRAVERSE(&agents, p, list) {
2493 ast_mutex_lock(&p->lock);
2494 if (!p->pending && ((groupmatch && (p->group & groupmatch)) || !strcmp(data, p->agent))) {
2495 if (p->owner) {
2496 if (res != AST_DEVICE_INUSE)
2497 res = AST_DEVICE_BUSY;
2498 } else {
2499 if (res == AST_DEVICE_BUSY)
2500 res = AST_DEVICE_INUSE;
2501 if (p->chan || !ast_strlen_zero(p->loginchan)) {
2502 if (res == AST_DEVICE_INVALID)
2503 res = AST_DEVICE_UNKNOWN;
2504 } else if (res == AST_DEVICE_INVALID)
2505 res = AST_DEVICE_UNAVAILABLE;
2507 if (!strcmp(data, p->agent)) {
2508 ast_mutex_unlock(&p->lock);
2509 break;
2512 ast_mutex_unlock(&p->lock);
2514 AST_LIST_UNLOCK(&agents);
2515 return res;
2518 static struct agent_pvt *find_agent(char *agentid)
2520 struct agent_pvt *cur;
2522 AST_LIST_TRAVERSE(&agents, cur, list) {
2523 if (!strcmp(cur->agent, agentid))
2524 break;
2527 return cur;
2530 static int function_agent(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
2532 char *parse;
2533 AST_DECLARE_APP_ARGS(args,
2534 AST_APP_ARG(agentid);
2535 AST_APP_ARG(item);
2537 char *tmp;
2538 struct agent_pvt *agent;
2540 buf[0] = '\0';
2542 if (ast_strlen_zero(data)) {
2543 ast_log(LOG_WARNING, "The AGENT function requires an argument - agentid!\n");
2544 return -1;
2547 parse = ast_strdupa(data);
2549 AST_NONSTANDARD_APP_ARGS(args, parse, ':');
2550 if (!args.item)
2551 args.item = "status";
2553 if (!(agent = find_agent(args.agentid))) {
2554 ast_log(LOG_WARNING, "Agent '%s' not found!\n", args.agentid);
2555 return -1;
2558 if (!strcasecmp(args.item, "status")) {
2559 char *status = "LOGGEDOUT";
2560 if (agent->chan || !ast_strlen_zero(agent->loginchan))
2561 status = "LOGGEDIN";
2562 ast_copy_string(buf, status, len);
2563 } else if (!strcasecmp(args.item, "password"))
2564 ast_copy_string(buf, agent->password, len);
2565 else if (!strcasecmp(args.item, "name"))
2566 ast_copy_string(buf, agent->name, len);
2567 else if (!strcasecmp(args.item, "mohclass"))
2568 ast_copy_string(buf, agent->moh, len);
2569 else if (!strcasecmp(args.item, "channel")) {
2570 if (agent->chan) {
2571 ast_copy_string(buf, agent->chan->name, len);
2572 tmp = strrchr(buf, '-');
2573 if (tmp)
2574 *tmp = '\0';
2576 } else if (!strcasecmp(args.item, "exten"))
2577 ast_copy_string(buf, agent->loginchan, len);
2579 return 0;
2582 struct ast_custom_function agent_function = {
2583 .name = "AGENT",
2584 .synopsis = "Gets information about an Agent",
2585 .syntax = "AGENT(<agentid>[:item])",
2586 .read = function_agent,
2587 .desc = "The valid items to retrieve are:\n"
2588 "- status (default) The status of the agent\n"
2589 " LOGGEDIN | LOGGEDOUT\n"
2590 "- password The password of the agent\n"
2591 "- name The name of the agent\n"
2592 "- mohclass MusicOnHold class\n"
2593 "- exten The callback extension for the Agent (AgentCallbackLogin)\n"
2594 "- channel The name of the active channel for the Agent (AgentLogin)\n"
2599 * \brief Initialize the Agents module.
2600 * This function is being called by Asterisk when loading the module.
2601 * Among other things it registers applications, cli commands and reads the cofiguration file.
2603 * \returns int Always 0.
2605 static int load_module(void)
2607 /* Make sure we can register our agent channel type */
2608 if (ast_channel_register(&agent_tech)) {
2609 ast_log(LOG_ERROR, "Unable to register channel class 'Agent'\n");
2610 return -1;
2612 /* Read in the config */
2613 if (!read_agent_config())
2614 return AST_MODULE_LOAD_DECLINE;
2615 if (persistent_agents)
2616 reload_agents();
2617 /* Dialplan applications */
2618 ast_register_application(app, login_exec, synopsis, descrip);
2619 ast_register_application(app2, callback_exec, synopsis2, descrip2);
2620 ast_register_application(app3, agentmonitoroutgoing_exec, synopsis3, descrip3);
2622 /* Manager commands */
2623 ast_manager_register2("Agents", EVENT_FLAG_AGENT, action_agents, "Lists agents and their status", mandescr_agents);
2624 ast_manager_register2("AgentLogoff", EVENT_FLAG_AGENT, action_agent_logoff, "Sets an agent as no longer logged in", mandescr_agent_logoff);
2625 ast_manager_register2("AgentCallbackLogin", EVENT_FLAG_AGENT, action_agent_callback_login, "Sets an agent as logged in by callback", mandescr_agent_callback_login);
2627 /* CLI Commands */
2628 ast_cli_register_multiple(cli_agents, sizeof(cli_agents) / sizeof(struct ast_cli_entry));
2630 /* Dialplan Functions */
2631 ast_custom_function_register(&agent_function);
2633 return 0;
2636 static int reload(void)
2638 read_agent_config();
2639 if (persistent_agents)
2640 reload_agents();
2641 return 0;
2644 static int unload_module(void)
2646 struct agent_pvt *p;
2647 /* First, take us out of the channel loop */
2648 ast_channel_unregister(&agent_tech);
2649 /* Unregister dialplan functions */
2650 ast_custom_function_unregister(&agent_function);
2651 /* Unregister CLI commands */
2652 ast_cli_unregister_multiple(cli_agents, sizeof(cli_agents) / sizeof(struct ast_cli_entry));
2653 /* Unregister dialplan applications */
2654 ast_unregister_application(app);
2655 ast_unregister_application(app2);
2656 ast_unregister_application(app3);
2657 /* Unregister manager command */
2658 ast_manager_unregister("Agents");
2659 ast_manager_unregister("AgentLogoff");
2660 ast_manager_unregister("AgentCallbackLogin");
2661 /* Unregister channel */
2662 AST_LIST_LOCK(&agents);
2663 /* Hangup all interfaces if they have an owner */
2664 while ((p = AST_LIST_REMOVE_HEAD(&agents, list))) {
2665 if (p->owner)
2666 ast_softhangup(p->owner, AST_SOFTHANGUP_APPUNLOAD);
2667 free(p);
2669 AST_LIST_UNLOCK(&agents);
2670 AST_LIST_HEAD_DESTROY(&agents);
2671 return 0;
2674 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Agent Proxy Channel",
2675 .load = load_module,
2676 .unload = unload_module,
2677 .reload = reload,