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