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