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