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