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