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