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