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