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