00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034 #include "asterisk.h"
00035
00036 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
00037
00038 #include <stdio.h>
00039 #include <string.h>
00040 #include <errno.h>
00041 #include <unistd.h>
00042 #include <sys/socket.h>
00043 #include <stdlib.h>
00044 #include <fcntl.h>
00045 #include <netdb.h>
00046 #include <netinet/in.h>
00047 #include <arpa/inet.h>
00048 #include <sys/signal.h>
00049
00050 #include "asterisk/lock.h"
00051 #include "asterisk/channel.h"
00052 #include "asterisk/config.h"
00053 #include "asterisk/logger.h"
00054 #include "asterisk/module.h"
00055 #include "asterisk/pbx.h"
00056 #include "asterisk/options.h"
00057 #include "asterisk/lock.h"
00058 #include "asterisk/sched.h"
00059 #include "asterisk/io.h"
00060 #include "asterisk/rtp.h"
00061 #include "asterisk/acl.h"
00062 #include "asterisk/callerid.h"
00063 #include "asterisk/file.h"
00064 #include "asterisk/cli.h"
00065 #include "asterisk/app.h"
00066 #include "asterisk/musiconhold.h"
00067 #include "asterisk/manager.h"
00068 #include "asterisk/features.h"
00069 #include "asterisk/utils.h"
00070 #include "asterisk/causes.h"
00071 #include "asterisk/astdb.h"
00072 #include "asterisk/devicestate.h"
00073 #include "asterisk/monitor.h"
00074 #include "asterisk/stringfields.h"
00075
00076 static const char tdesc[] = "Call Agent Proxy Channel";
00077 static const char config[] = "agents.conf";
00078
00079 static const char app[] = "AgentLogin";
00080 static const char app2[] = "AgentCallbackLogin";
00081 static const char app3[] = "AgentMonitorOutgoing";
00082
00083 static const char synopsis[] = "Call agent login";
00084 static const char synopsis2[] = "Call agent callback login";
00085 static const char synopsis3[] = "Record agent's outgoing call";
00086
00087 static const char descrip[] =
00088 " AgentLogin([AgentNo][|options]):\n"
00089 "Asks the agent to login to the system. Always returns -1. While\n"
00090 "logged in, the agent can receive calls and will hear a 'beep'\n"
00091 "when a new call comes in. The agent can dump the call by pressing\n"
00092 "the star key.\n"
00093 "The option string may contain zero or more of the following characters:\n"
00094 " 's' -- silent login - do not announce the login ok segment after agent logged in/off\n";
00095
00096 static const char descrip2[] =
00097 " AgentCallbackLogin([AgentNo][|[options][|[exten]@context]]):\n"
00098 "Asks the agent to login to the system with callback.\n"
00099 "The agent's callback extension is called (optionally with the specified\n"
00100 "context).\n"
00101 "The option string may contain zero or more of the following characters:\n"
00102 " 's' -- silent login - do not announce the login ok segment agent logged in/off\n";
00103
00104 static const char descrip3[] =
00105 " AgentMonitorOutgoing([options]):\n"
00106 "Tries to figure out the id of the agent who is placing outgoing call based on\n"
00107 "comparison of the callerid of the current interface and the global variable \n"
00108 "placed by the AgentCallbackLogin application. That's why it should be used only\n"
00109 "with the AgentCallbackLogin app. Uses the monitoring functions in chan_agent \n"
00110 "instead of Monitor application. That have to be configured in the agents.conf file.\n"
00111 "\nReturn value:\n"
00112 "Normally the app returns 0 unless the options are passed. Also if the callerid or\n"
00113 "the agentid are not specified it'll look for n+101 priority.\n"
00114 "\nOptions:\n"
00115 " 'd' - make the app return -1 if there is an error condition and there is\n"
00116 " no extension n+101\n"
00117 " 'c' - change the CDR so that the source of the call is 'Agent/agent_id'\n"
00118 " 'n' - don't generate the warnings when there is no callerid or the\n"
00119 " agentid is not known.\n"
00120 " It's handy if you want to have one context for agent and non-agent calls.\n";
00121
00122 static const char mandescr_agents[] =
00123 "Description: Will list info about all possible agents.\n"
00124 "Variables: NONE\n";
00125
00126 static const char mandescr_agent_logoff[] =
00127 "Description: Sets an agent as no longer logged in.\n"
00128 "Variables: (Names marked with * are required)\n"
00129 " *Agent: Agent ID of the agent to log off\n"
00130 " Soft: Set to 'true' to not hangup existing calls\n";
00131
00132 static const char mandescr_agent_callback_login[] =
00133 "Description: Sets an agent as logged in with callback.\n"
00134 "Variables: (Names marked with * are required)\n"
00135 " *Agent: Agent ID of the agent to login\n"
00136 " *Exten: Extension to use for callback\n"
00137 " Context: Context to use for callback\n"
00138 " AckCall: Set to 'true' to require an acknowledgement by '#' when agent is called back\n"
00139 " WrapupTime: the minimum amount of time after disconnecting before the caller can receive a new call\n";
00140
00141 static char moh[80] = "default";
00142
00143 #define AST_MAX_AGENT 80
00144 #define AST_MAX_BUF 256
00145 #define AST_MAX_FILENAME_LEN 256
00146
00147 static const char pa_family[] = "/Agents";
00148 #define PA_MAX_LEN 2048
00149
00150 static int persistent_agents = 0;
00151 static void dump_agents(void);
00152
00153 static ast_group_t group;
00154 static int autologoff;
00155 static int wrapuptime;
00156 static int ackcall;
00157 static int endcall;
00158 static int multiplelogin = 1;
00159 static int autologoffunavail = 0;
00160
00161 static int maxlogintries = 3;
00162 static char agentgoodbye[AST_MAX_FILENAME_LEN] = "vm-goodbye";
00163
00164 static int recordagentcalls = 0;
00165 static char recordformat[AST_MAX_BUF] = "";
00166 static char recordformatext[AST_MAX_BUF] = "";
00167 static char urlprefix[AST_MAX_BUF] = "";
00168 static char savecallsin[AST_MAX_BUF] = "";
00169 static int updatecdr = 0;
00170 static char beep[AST_MAX_BUF] = "beep";
00171
00172 #define GETAGENTBYCALLERID "AGENTBYCALLERID"
00173
00174
00175 struct agent_pvt {
00176 ast_mutex_t lock;
00177 int dead;
00178 int pending;
00179 int abouttograb;
00180 int autologoff;
00181 int ackcall;
00182 int deferlogoff;
00183 time_t loginstart;
00184 time_t start;
00185 struct timeval lastdisc;
00186 int wrapuptime;
00187 ast_group_t group;
00188 int acknowledged;
00189 char moh[80];
00190 char agent[AST_MAX_AGENT];
00191 char password[AST_MAX_AGENT];
00192 char name[AST_MAX_AGENT];
00193 ast_mutex_t app_lock;
00194 volatile pthread_t owning_app;
00195 volatile int app_sleep_cond;
00196 struct ast_channel *owner;
00197 char loginchan[80];
00198 char logincallerid[80];
00199 struct ast_channel *chan;
00200 AST_LIST_ENTRY(agent_pvt) list;
00201 };
00202
00203 static AST_LIST_HEAD_STATIC(agents, agent_pvt);
00204
00205 #define CHECK_FORMATS(ast, p) do { \
00206 if (p->chan) {\
00207 if (ast->nativeformats != p->chan->nativeformats) { \
00208 ast_log(LOG_DEBUG, "Native formats changing from %d to %d\n", ast->nativeformats, p->chan->nativeformats); \
00209 \
00210 ast->nativeformats = p->chan->nativeformats; \
00211 ast_log(LOG_DEBUG, "Resetting read to %d and write to %d\n", ast->readformat, ast->writeformat);\
00212 ast_set_read_format(ast, ast->readformat); \
00213 ast_set_write_format(ast, ast->writeformat); \
00214 } \
00215 if (p->chan->readformat != ast->rawreadformat) \
00216 ast_set_read_format(p->chan, ast->rawreadformat); \
00217 if (p->chan->writeformat != ast->rawwriteformat) \
00218 ast_set_write_format(p->chan, ast->rawwriteformat); \
00219 } \
00220 } while(0)
00221
00222
00223
00224
00225
00226 #define CLEANUP(ast, p) do { \
00227 int x; \
00228 if (p->chan) { \
00229 for (x=0;x<AST_MAX_FDS;x++) {\
00230 if (x != AST_TIMING_FD) \
00231 ast->fds[x] = p->chan->fds[x]; \
00232 } \
00233 ast->fds[AST_AGENT_FD] = p->chan->fds[AST_TIMING_FD]; \
00234 } \
00235 } while(0)
00236
00237
00238 static struct ast_channel *agent_request(const char *type, int format, void *data, int *cause);
00239 static int agent_devicestate(void *data);
00240 static void agent_logoff_maintenance(struct agent_pvt *p, char *loginchan, long logintime, const char *uniqueid, char *logcommand);
00241 static int agent_digit_begin(struct ast_channel *ast, char digit);
00242 static int agent_digit_end(struct ast_channel *ast, char digit, unsigned int duration);
00243 static int agent_call(struct ast_channel *ast, char *dest, int timeout);
00244 static int agent_hangup(struct ast_channel *ast);
00245 static int agent_answer(struct ast_channel *ast);
00246 static struct ast_frame *agent_read(struct ast_channel *ast);
00247 static int agent_write(struct ast_channel *ast, struct ast_frame *f);
00248 static int agent_sendhtml(struct ast_channel *ast, int subclass, const char *data, int datalen);
00249 static int agent_sendtext(struct ast_channel *ast, const char *text);
00250 static int agent_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen);
00251 static int agent_fixup(struct ast_channel *oldchan, struct ast_channel *newchan);
00252 static struct ast_channel *agent_bridgedchannel(struct ast_channel *chan, struct ast_channel *bridge);
00253 static void set_agentbycallerid(const char *callerid, const char *agent);
00254
00255
00256 static const struct ast_channel_tech agent_tech = {
00257 .type = "Agent",
00258 .description = tdesc,
00259 .capabilities = -1,
00260 .requester = agent_request,
00261 .devicestate = agent_devicestate,
00262 .send_digit_begin = agent_digit_begin,
00263 .send_digit_end = agent_digit_end,
00264 .call = agent_call,
00265 .hangup = agent_hangup,
00266 .answer = agent_answer,
00267 .read = agent_read,
00268 .write = agent_write,
00269 .write_video = agent_write,
00270 .send_html = agent_sendhtml,
00271 .send_text = agent_sendtext,
00272 .exception = agent_read,
00273 .indicate = agent_indicate,
00274 .fixup = agent_fixup,
00275 .bridged_channel = agent_bridgedchannel,
00276 };
00277
00278
00279
00280
00281
00282
00283
00284
00285
00286 static struct agent_pvt *add_agent(char *agent, int pending)
00287 {
00288 char *parse;
00289 AST_DECLARE_APP_ARGS(args,
00290 AST_APP_ARG(agt);
00291 AST_APP_ARG(password);
00292 AST_APP_ARG(name);
00293 );
00294 char *password = NULL;
00295 char *name = NULL;
00296 char *agt = NULL;
00297 struct agent_pvt *p;
00298
00299 parse = ast_strdupa(agent);
00300
00301
00302 AST_NONSTANDARD_APP_ARGS(args, parse, ',');
00303
00304 if(args.argc == 0) {
00305 ast_log(LOG_WARNING, "A blank agent line!\n");
00306 return NULL;
00307 }
00308
00309 if(ast_strlen_zero(args.agt) ) {
00310 ast_log(LOG_WARNING, "An agent line with no agentid!\n");
00311 return NULL;
00312 } else
00313 agt = args.agt;
00314
00315 if(!ast_strlen_zero(args.password)) {
00316 password = args.password;
00317 while (*password && *password < 33) password++;
00318 }
00319 if(!ast_strlen_zero(args.name)) {
00320 name = args.name;
00321 while (*name && *name < 33) name++;
00322 }
00323
00324
00325 AST_LIST_TRAVERSE(&agents, p, list) {
00326 if (!pending && !strcmp(p->agent, agt))
00327 break;
00328 }
00329 if (!p) {
00330
00331 if (!(p = ast_calloc(1, sizeof(*p))))
00332 return NULL;
00333 ast_copy_string(p->agent, agt, sizeof(p->agent));
00334 ast_mutex_init(&p->lock);
00335 ast_mutex_init(&p->app_lock);
00336 p->owning_app = (pthread_t) -1;
00337 p->app_sleep_cond = 1;
00338 p->group = group;
00339 p->pending = pending;
00340 AST_LIST_INSERT_TAIL(&agents, p, list);
00341 }
00342
00343 ast_copy_string(p->password, password ? password : "", sizeof(p->password));
00344 ast_copy_string(p->name, name ? name : "", sizeof(p->name));
00345 ast_copy_string(p->moh, moh, sizeof(p->moh));
00346 p->ackcall = ackcall;
00347 p->autologoff = autologoff;
00348
00349
00350
00351 if (p->wrapuptime > wrapuptime) {
00352 struct timeval now = ast_tvnow();
00353
00354
00355
00356 if (p->lastdisc.tv_sec > (now.tv_sec + wrapuptime/1000)) {
00357 p->lastdisc.tv_sec = now.tv_sec + wrapuptime/1000;
00358 p->lastdisc.tv_usec = now.tv_usec;
00359 }
00360 }
00361 p->wrapuptime = wrapuptime;
00362
00363 if (pending)
00364 p->dead = 1;
00365 else
00366 p->dead = 0;
00367 return p;
00368 }
00369
00370
00371
00372
00373
00374
00375
00376 static int agent_cleanup(struct agent_pvt *p)
00377 {
00378 struct ast_channel *chan = p->owner;
00379 p->owner = NULL;
00380 chan->tech_pvt = NULL;
00381 p->app_sleep_cond = 1;
00382
00383 ast_mutex_unlock(&p->app_lock);
00384 if (chan)
00385 ast_channel_free(chan);
00386 if (p->dead) {
00387 ast_mutex_destroy(&p->lock);
00388 ast_mutex_destroy(&p->app_lock);
00389 free(p);
00390 }
00391 return 0;
00392 }
00393
00394 static int check_availability(struct agent_pvt *newlyavailable, int needlock);
00395
00396 static int agent_answer(struct ast_channel *ast)
00397 {
00398 ast_log(LOG_WARNING, "Huh? Agent is being asked to answer?\n");
00399 return -1;
00400 }
00401
00402 static int __agent_start_monitoring(struct ast_channel *ast, struct agent_pvt *p, int needlock)
00403 {
00404 char tmp[AST_MAX_BUF],tmp2[AST_MAX_BUF], *pointer;
00405 char filename[AST_MAX_BUF];
00406 int res = -1;
00407 if (!p)
00408 return -1;
00409 if (!ast->monitor) {
00410 snprintf(filename, sizeof(filename), "agent-%s-%s",p->agent, ast->uniqueid);
00411
00412 if ((pointer = strchr(filename, '.')))
00413 *pointer = '-';
00414 snprintf(tmp, sizeof(tmp), "%s%s",savecallsin ? savecallsin : "", filename);
00415 ast_monitor_start(ast, recordformat, tmp, needlock);
00416 ast_monitor_setjoinfiles(ast, 1);
00417 snprintf(tmp2, sizeof(tmp2), "%s%s.%s", urlprefix ? urlprefix : "", filename, recordformatext);
00418 #if 0
00419 ast_verbose("name is %s, link is %s\n",tmp, tmp2);
00420 #endif
00421 if (!ast->cdr)
00422 ast->cdr = ast_cdr_alloc();
00423 ast_cdr_setuserfield(ast, tmp2);
00424 res = 0;
00425 } else
00426 ast_log(LOG_ERROR, "Recording already started on that call.\n");
00427 return res;
00428 }
00429
00430 static int agent_start_monitoring(struct ast_channel *ast, int needlock)
00431 {
00432 return __agent_start_monitoring(ast, ast->tech_pvt, needlock);
00433 }
00434
00435 static struct ast_frame *agent_read(struct ast_channel *ast)
00436 {
00437 struct agent_pvt *p = ast->tech_pvt;
00438 struct ast_frame *f = NULL;
00439 static struct ast_frame answer_frame = { AST_FRAME_CONTROL, AST_CONTROL_ANSWER };
00440 const char *status;
00441 ast_mutex_lock(&p->lock);
00442 CHECK_FORMATS(ast, p);
00443 if (p->chan) {
00444 ast_copy_flags(p->chan, ast, AST_FLAG_EXCEPTION);
00445 p->chan->fdno = (ast->fdno == AST_AGENT_FD) ? AST_TIMING_FD : ast->fdno;
00446 f = ast_read(p->chan);
00447 } else
00448 f = &ast_null_frame;
00449 if (!f) {
00450
00451 if (p->chan) {
00452 p->chan->_bridge = NULL;
00453
00454
00455 if (!ast_strlen_zero(p->loginchan)) {
00456 if (p->chan)
00457 ast_log(LOG_DEBUG, "Bridge on '%s' being cleared (2)\n", p->chan->name);
00458
00459 status = pbx_builtin_getvar_helper(p->chan, "CHANLOCALSTATUS");
00460 if (autologoffunavail && status && !strcasecmp(status, "CHANUNAVAIL")) {
00461 long logintime = time(NULL) - p->loginstart;
00462 p->loginstart = 0;
00463 ast_log(LOG_NOTICE, "Agent read: '%s' is not available now, auto logoff\n", p->name);
00464 agent_logoff_maintenance(p, p->loginchan, logintime, ast->uniqueid, "Chanunavail");
00465 }
00466 ast_hangup(p->chan);
00467 if (p->wrapuptime && p->acknowledged)
00468 p->lastdisc = ast_tvadd(ast_tvnow(), ast_samp2tv(p->wrapuptime, 1000));
00469 }
00470 p->chan = NULL;
00471 p->acknowledged = 0;
00472 }
00473 } else {
00474
00475
00476 if (!p->ackcall && !p->acknowledged && p->chan && (p->chan->_state == AST_STATE_UP))
00477 p->acknowledged = 1;
00478 switch (f->frametype) {
00479 case AST_FRAME_CONTROL:
00480 if (f->subclass == AST_CONTROL_ANSWER) {
00481 if (p->ackcall) {
00482 if (option_verbose > 2)
00483 ast_verbose(VERBOSE_PREFIX_3 "%s answered, waiting for '#' to acknowledge\n", p->chan->name);
00484
00485 ast_frfree(f);
00486 f = &ast_null_frame;
00487 } else {
00488 p->acknowledged = 1;
00489
00490
00491 ast_frfree(f);
00492 f = &answer_frame;
00493 }
00494 }
00495 break;
00496 case AST_FRAME_DTMF_BEGIN:
00497 case AST_FRAME_DTMF_END:
00498 if (!p->acknowledged && (f->subclass == '#')) {
00499 if (option_verbose > 2)
00500 ast_verbose(VERBOSE_PREFIX_3 "%s acknowledged\n", p->chan->name);
00501 p->acknowledged = 1;
00502 ast_frfree(f);
00503 f = &answer_frame;
00504 } else if (f->subclass == '*' && endcall) {
00505
00506 ast_frfree(f);
00507 f = NULL;
00508 }
00509 break;
00510 case AST_FRAME_VOICE:
00511 case AST_FRAME_VIDEO:
00512
00513 if (!p->acknowledged) {
00514 ast_frfree(f);
00515 f = &ast_null_frame;
00516 }
00517 default:
00518
00519 break;
00520 }
00521 }
00522
00523 CLEANUP(ast,p);
00524 if (p->chan && !p->chan->_bridge) {
00525 if (strcasecmp(p->chan->tech->type, "Local")) {
00526 p->chan->_bridge = ast;
00527 if (p->chan)
00528 ast_log(LOG_DEBUG, "Bridge on '%s' being set to '%s' (3)\n", p->chan->name, p->chan->_bridge->name);
00529 }
00530 }
00531 ast_mutex_unlock(&p->lock);
00532 if (recordagentcalls && f == &answer_frame)
00533 agent_start_monitoring(ast,0);
00534 return f;
00535 }
00536
00537 static int agent_sendhtml(struct ast_channel *ast, int subclass, const char *data, int datalen)
00538 {
00539 struct agent_pvt *p = ast->tech_pvt;
00540 int res = -1;
00541 ast_mutex_lock(&p->lock);
00542 if (p->chan)
00543 res = ast_channel_sendhtml(p->chan, subclass, data, datalen);
00544 ast_mutex_unlock(&p->lock);
00545 return res;
00546 }
00547
00548 static int agent_sendtext(struct ast_channel *ast, const char *text)
00549 {
00550 struct agent_pvt *p = ast->tech_pvt;
00551 int res = -1;
00552 ast_mutex_lock(&p->lock);
00553 if (p->chan)
00554 res = ast_sendtext(p->chan, text);
00555 ast_mutex_unlock(&p->lock);
00556 return res;
00557 }
00558
00559 static int agent_write(struct ast_channel *ast, struct ast_frame *f)
00560 {
00561 struct agent_pvt *p = ast->tech_pvt;
00562 int res = -1;
00563 CHECK_FORMATS(ast, p);
00564 ast_mutex_lock(&p->lock);
00565 if (!p->chan)
00566 res = 0;
00567 else {
00568 if ((f->frametype != AST_FRAME_VOICE) ||
00569 (f->frametype != AST_FRAME_VIDEO) ||
00570 (f->subclass == p->chan->writeformat)) {
00571 res = ast_write(p->chan, f);
00572 } else {
00573 ast_log(LOG_DEBUG, "Dropping one incompatible %s frame on '%s' to '%s'\n",
00574 f->frametype == AST_FRAME_VOICE ? "audio" : "video",
00575 ast->name, p->chan->name);
00576 res = 0;
00577 }
00578 }
00579 CLEANUP(ast, p);
00580 ast_mutex_unlock(&p->lock);
00581 return res;
00582 }
00583
00584 static int agent_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
00585 {
00586 struct agent_pvt *p = newchan->tech_pvt;
00587 ast_mutex_lock(&p->lock);
00588 if (p->owner != oldchan) {
00589 ast_log(LOG_WARNING, "old channel wasn't %p but was %p\n", oldchan, p->owner);
00590 ast_mutex_unlock(&p->lock);
00591 return -1;
00592 }
00593 p->owner = newchan;
00594 ast_mutex_unlock(&p->lock);
00595 return 0;
00596 }
00597
00598 static int agent_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen)
00599 {
00600 struct agent_pvt *p = ast->tech_pvt;
00601 int res = -1;
00602 ast_mutex_lock(&p->lock);
00603 if (p->chan)
00604 res = p->chan->tech->indicate ? p->chan->tech->indicate(p->chan, condition, data, datalen) : -1;
00605 else
00606 res = 0;
00607 ast_mutex_unlock(&p->lock);
00608 return res;
00609 }
00610
00611 static int agent_digit_begin(struct ast_channel *ast, char digit)
00612 {
00613 struct agent_pvt *p = ast->tech_pvt;
00614 ast_mutex_lock(&p->lock);
00615 ast_senddigit_begin(p->chan, digit);
00616 ast_mutex_unlock(&p->lock);
00617 return 0;
00618 }
00619
00620 static int agent_digit_end(struct ast_channel *ast, char digit, unsigned int duration)
00621 {
00622 struct agent_pvt *p = ast->tech_pvt;
00623 ast_mutex_lock(&p->lock);
00624 ast_senddigit_end(p->chan, digit, duration);
00625 ast_mutex_unlock(&p->lock);
00626 return 0;
00627 }
00628
00629 static int agent_call(struct ast_channel *ast, char *dest, int timeout)
00630 {
00631 struct agent_pvt *p = ast->tech_pvt;
00632 int res = -1;
00633 int newstate=0;
00634 ast_mutex_lock(&p->lock);
00635 p->acknowledged = 0;
00636 if (!p->chan) {
00637 if (p->pending) {
00638 ast_log(LOG_DEBUG, "Pretending to dial on pending agent\n");
00639 newstate = AST_STATE_DIALING;
00640 res = 0;
00641 } else {
00642 ast_log(LOG_NOTICE, "Whoa, they hung up between alloc and call... what are the odds of that?\n");
00643 res = -1;
00644 }
00645 ast_mutex_unlock(&p->lock);
00646 if (newstate)
00647 ast_setstate(ast, newstate);
00648 return res;
00649 } else if (!ast_strlen_zero(p->loginchan)) {
00650 time(&p->start);
00651
00652 if (option_verbose > 2)
00653 ast_verbose(VERBOSE_PREFIX_3 "outgoing agentcall, to agent '%s', on '%s'\n", p->agent, p->chan->name);
00654 ast_set_callerid(p->chan,
00655 ast->cid.cid_num, ast->cid.cid_name, NULL);
00656 ast_channel_inherit_variables(ast, p->chan);
00657 res = ast_call(p->chan, p->loginchan, 0);
00658 CLEANUP(ast,p);
00659 ast_mutex_unlock(&p->lock);
00660 return res;
00661 }
00662 ast_verbose( VERBOSE_PREFIX_3 "agent_call, call to agent '%s' call on '%s'\n", p->agent, p->chan->name);
00663 if (option_debug > 2)
00664 ast_log(LOG_DEBUG, "Playing beep, lang '%s'\n", p->chan->language);
00665 res = ast_streamfile(p->chan, beep, p->chan->language);
00666 if (option_debug > 2)
00667 ast_log(LOG_DEBUG, "Played beep, result '%d'\n", res);
00668 if (!res) {
00669 res = ast_waitstream(p->chan, "");
00670 if (option_debug > 2)
00671 ast_log(LOG_DEBUG, "Waited for stream, result '%d'\n", res);
00672 }
00673 if (!res) {
00674 res = ast_set_read_format(p->chan, ast_best_codec(p->chan->nativeformats));
00675 if (option_debug > 2)
00676 ast_log(LOG_DEBUG, "Set read format, result '%d'\n", res);
00677 if (res)
00678 ast_log(LOG_WARNING, "Unable to set read format to %s\n", ast_getformatname(ast_best_codec(p->chan->nativeformats)));
00679 } else {
00680
00681 p->chan = NULL;
00682 }
00683
00684 if (!res) {
00685 res = ast_set_write_format(p->chan, ast_best_codec(p->chan->nativeformats));
00686 if (option_debug > 2)
00687 ast_log(LOG_DEBUG, "Set write format, result '%d'\n", res);
00688 if (res)
00689 ast_log(LOG_WARNING, "Unable to set write format to %s\n", ast_getformatname(ast_best_codec(p->chan->nativeformats)));
00690 }
00691 if(!res) {
00692
00693 if (p->ackcall > 1)
00694 newstate = AST_STATE_RINGING;
00695 else {
00696 newstate = AST_STATE_UP;
00697 if (recordagentcalls)
00698 agent_start_monitoring(ast, 0);
00699 p->acknowledged = 1;
00700 }
00701 res = 0;
00702 }
00703 CLEANUP(ast, p);
00704 ast_mutex_unlock(&p->lock);
00705 if (newstate)
00706 ast_setstate(ast, newstate);
00707 return res;
00708 }
00709
00710
00711 static void set_agentbycallerid(const char *callerid, const char *agent)
00712 {
00713 char buf[AST_MAX_BUF];
00714
00715
00716 if (ast_strlen_zero(callerid))
00717 return;
00718
00719 snprintf(buf, sizeof(buf), "%s_%s", GETAGENTBYCALLERID, callerid);
00720 pbx_builtin_setvar_helper(NULL, buf, agent);
00721 }
00722
00723 static int agent_hangup(struct ast_channel *ast)
00724 {
00725 struct agent_pvt *p = ast->tech_pvt;
00726 int howlong = 0;
00727 const char *status;
00728 ast_mutex_lock(&p->lock);
00729 p->owner = NULL;
00730 ast->tech_pvt = NULL;
00731 p->app_sleep_cond = 1;
00732 p->acknowledged = 0;
00733
00734
00735
00736
00737
00738
00739
00740
00741 if (option_debug)
00742 ast_log(LOG_DEBUG, "Hangup called for state %s\n", ast_state2str(ast->_state));
00743 if (p->start && (ast->_state != AST_STATE_UP)) {
00744 howlong = time(NULL) - p->start;
00745 p->start = 0;
00746 } else if (ast->_state == AST_STATE_RESERVED)
00747 howlong = 0;
00748 else
00749 p->start = 0;
00750 if (p->chan) {
00751 p->chan->_bridge = NULL;
00752
00753 if (!ast_strlen_zero(p->loginchan)) {
00754
00755 if (p->wrapuptime)
00756 p->lastdisc = ast_tvadd(ast_tvnow(), ast_samp2tv(p->wrapuptime, 1000));
00757 else
00758 p->lastdisc = ast_tv(0,0);
00759 if (p->chan) {
00760 status = pbx_builtin_getvar_helper(p->chan, "CHANLOCALSTATUS");
00761 if (autologoffunavail && status && !strcasecmp(status, "CHANUNAVAIL")) {
00762 long logintime = time(NULL) - p->loginstart;
00763 p->loginstart = 0;
00764 ast_log(LOG_NOTICE, "Agent hangup: '%s' is not available now, auto logoff\n", p->name);
00765 agent_logoff_maintenance(p, p->loginchan, logintime, ast->uniqueid, "Chanunavail");
00766 }
00767
00768 ast_hangup(p->chan);
00769 p->chan = NULL;
00770 }
00771 ast_log(LOG_DEBUG, "Hungup, howlong is %d, autologoff is %d\n", howlong, p->autologoff);
00772 if ((p->deferlogoff) || (howlong && p->autologoff && (howlong > p->autologoff))) {
00773 long logintime = time(NULL) - p->loginstart;
00774 p->loginstart = 0;
00775 if (!p->deferlogoff)
00776 ast_log(LOG_NOTICE, "Agent '%s' didn't answer/confirm within %d seconds (waited %d)\n", p->name, p->autologoff, howlong);
00777 p->deferlogoff = 0;
00778 agent_logoff_maintenance(p, p->loginchan, logintime, ast->uniqueid, "Autologoff");
00779 if (persistent_agents)
00780 dump_agents();
00781 }
00782 } else if (p->dead) {
00783 ast_channel_lock(p->chan);
00784 ast_softhangup(p->chan, AST_SOFTHANGUP_EXPLICIT);
00785 ast_channel_unlock(p->chan);
00786 } else if (p->loginstart) {
00787 ast_channel_lock(p->chan);
00788 ast_indicate_data(p->chan, AST_CONTROL_HOLD,
00789 S_OR(p->moh, NULL),
00790 !ast_strlen_zero(p->moh) ? strlen(p->moh) + 1 : 0);
00791 ast_channel_unlock(p->chan);
00792 }
00793 }
00794 ast_mutex_unlock(&p->lock);
00795
00796
00797 if (!p->loginstart) {
00798 p->loginchan[0] = '\0';
00799 p->logincallerid[0] = '\0';
00800 if (persistent_agents)
00801 dump_agents();
00802 } else {
00803 ast_device_state_changed("Agent/%s", p->agent);
00804 }
00805
00806 if (p->pending) {
00807 AST_LIST_LOCK(&agents);
00808 AST_LIST_REMOVE(&agents, p, list);
00809 AST_LIST_UNLOCK(&agents);
00810 }
00811 if (p->abouttograb) {
00812
00813
00814 p->abouttograb = 0;
00815 } else if (p->dead) {
00816 ast_mutex_destroy(&p->lock);
00817 ast_mutex_destroy(&p->app_lock);
00818 free(p);
00819 } else {
00820 if (p->chan) {
00821
00822 ast_mutex_lock(&p->lock);
00823
00824 p->lastdisc = ast_tvnow();
00825 ast_mutex_unlock(&p->lock);
00826 }
00827
00828 if (ast_strlen_zero(p->loginchan))
00829 ast_mutex_unlock(&p->app_lock);
00830 }
00831 return 0;
00832 }
00833
00834 static int agent_cont_sleep( void *data )
00835 {
00836 struct agent_pvt *p;
00837 int res;
00838
00839 p = (struct agent_pvt *)data;
00840
00841 ast_mutex_lock(&p->lock);
00842 res = p->app_sleep_cond;
00843 if (p->lastdisc.tv_sec) {
00844 if (ast_tvdiff_ms(ast_tvnow(), p->lastdisc) > p->wrapuptime)
00845 res = 1;
00846 }
00847 ast_mutex_unlock(&p->lock);
00848
00849 if(option_debug > 4 && !res )
00850 ast_log(LOG_DEBUG, "agent_cont_sleep() returning %d\n", res );
00851
00852 return res;
00853 }
00854
00855 static int agent_ack_sleep(void *data)
00856 {
00857 struct agent_pvt *p;
00858 int res=0;
00859 int to = 1000;
00860 struct ast_frame *f;
00861
00862
00863
00864 p = (struct agent_pvt *) data;
00865 if (!p->chan)
00866 return -1;
00867
00868 for(;;) {
00869 to = ast_waitfor(p->chan, to);
00870 if (to < 0)
00871 return -1;
00872 if (!to)
00873 return 0;
00874 f = ast_read(p->chan);
00875 if (!f)
00876 return -1;
00877 if (f->frametype == AST_FRAME_DTMF)
00878 res = f->subclass;
00879 else
00880 res = 0;
00881 ast_frfree(f);
00882 ast_mutex_lock(&p->lock);
00883 if (!p->app_sleep_cond) {
00884 ast_mutex_unlock(&p->lock);
00885 return 0;
00886 } else if (res == '#') {
00887 ast_mutex_unlock(&p->lock);
00888 return 1;
00889 }
00890 ast_mutex_unlock(&p->lock);
00891 res = 0;
00892 }
00893 return res;
00894 }
00895
00896 static struct ast_channel *agent_bridgedchannel(struct ast_channel *chan, struct ast_channel *bridge)
00897 {
00898 struct agent_pvt *p = bridge->tech_pvt;
00899 struct ast_channel *ret = NULL;
00900
00901 if (p) {
00902 if (chan == p->chan)
00903 ret = bridge->_bridge;
00904 else if (chan == bridge->_bridge)
00905 ret = p->chan;
00906 }
00907
00908 if (option_debug)
00909 ast_log(LOG_DEBUG, "Asked for bridged channel on '%s'/'%s', returning '%s'\n", chan->name, bridge->name, ret ? ret->name : "<none>");
00910 return ret;
00911 }
00912
00913
00914 static struct ast_channel *agent_new(struct agent_pvt *p, int state)
00915 {
00916 struct ast_channel *tmp;
00917 #if 0
00918 if (!p->chan) {
00919 ast_log(LOG_WARNING, "No channel? :(\n");
00920 return NULL;
00921 }
00922 #endif
00923 if (p->pending)
00924 tmp = ast_channel_alloc(0, state, 0, 0, "", p->chan ? p->chan->exten:"", p->chan ? p->chan->context:"", 0, "Agent/P%s-%d", p->agent, ast_random() & 0xffff);
00925 else
00926 tmp = ast_channel_alloc(0, state, 0, 0, "", p->chan ? p->chan->exten:"", p->chan ? p->chan->context:"", 0, "Agent/%s", p->agent);
00927 if (!tmp) {
00928 ast_log(LOG_WARNING, "Unable to allocate agent channel structure\n");
00929 return NULL;
00930 }
00931
00932 tmp->tech = &agent_tech;
00933 if (p->chan) {
00934 tmp->nativeformats = p->chan->nativeformats;
00935 tmp->writeformat = p->chan->writeformat;
00936 tmp->rawwriteformat = p->chan->writeformat;
00937 tmp->readformat = p->chan->readformat;
00938 tmp->rawreadformat = p->chan->readformat;
00939 ast_string_field_set(tmp, language, p->chan->language);
00940 ast_copy_string(tmp->context, p->chan->context, sizeof(tmp->context));
00941 ast_copy_string(tmp->exten, p->chan->exten, sizeof(tmp->exten));
00942
00943 } else {
00944 tmp->nativeformats = AST_FORMAT_SLINEAR;
00945 tmp->writeformat = AST_FORMAT_SLINEAR;
00946 tmp->rawwriteformat = AST_FORMAT_SLINEAR;
00947 tmp->readformat = AST_FORMAT_SLINEAR;
00948 tmp->rawreadformat = AST_FORMAT_SLINEAR;
00949 }
00950
00951 tmp->tech_pvt = p;
00952 p->owner = tmp;
00953
00954 #if 0
00955 ast_atomic_fetchadd_int(&__mod_desc->usecnt, +1);
00956 #endif
00957 ast_update_use_count();
00958 tmp->priority = 1;
00959
00960
00961
00962
00963
00964
00965
00966 p->app_sleep_cond = 0;
00967 if(ast_strlen_zero(p->loginchan) && ast_mutex_trylock(&p->app_lock)) {
00968 if (p->chan) {
00969 ast_queue_frame(p->chan, &ast_null_frame);
00970 ast_mutex_unlock(&p->lock);
00971 ast_mutex_lock(&p->app_lock);
00972 ast_mutex_lock(&p->lock);
00973 } else {
00974 ast_log(LOG_WARNING, "Agent disconnected while we were connecting the call\n");
00975 p->owner = NULL;
00976 tmp->tech_pvt = NULL;
00977 p->app_sleep_cond = 1;
00978 ast_channel_free( tmp );
00979 ast_mutex_unlock(&p->lock);
00980 ast_mutex_unlock(&p->app_lock);
00981 return NULL;
00982 }
00983 } else if (!ast_strlen_zero(p->loginchan)) {
00984 if (p->chan)
00985 ast_queue_frame(p->chan, &ast_null_frame);
00986 if (!p->chan) {
00987 ast_log(LOG_WARNING, "Agent disconnected while we were connecting the call\n");
00988 p->owner = NULL;
00989 tmp->tech_pvt = NULL;
00990 p->app_sleep_cond = 1;
00991 ast_channel_free( tmp );
00992 ast_mutex_unlock(&p->lock);
00993 return NULL;
00994 }
00995 }
00996 ast_indicate(p->chan, AST_CONTROL_UNHOLD);
00997 p->owning_app = pthread_self();
00998
00999 if (p->chan) {
01000 if (ast_test_flag(p->chan, AST_FLAG_BLOCKING)) {
01001 ast_log( LOG_ERROR, "A blocker exists after agent channel ownership acquired\n" );
01002 CRASH;
01003 }
01004 }
01005 return tmp;
01006 }
01007
01008
01009
01010
01011
01012
01013
01014 static int read_agent_config(void)
01015 {
01016 struct ast_config *cfg;
01017 struct ast_config *ucfg;
01018 struct ast_variable *v;
01019 struct agent_pvt *p;
01020 const char *general_val;
01021 const char *catname;
01022 const char *hasagent;
01023 int genhasagent;
01024
01025 group = 0;
01026 autologoff = 0;
01027 wrapuptime = 0;
01028 ackcall = 0;
01029 endcall = 1;
01030 cfg = ast_config_load(config);
01031 if (!cfg) {
01032 ast_log(LOG_NOTICE, "No agent configuration found -- agent support disabled\n");
01033 return 0;
01034 }
01035 AST_LIST_LOCK(&agents);
01036 AST_LIST_TRAVERSE(&agents, p, list) {
01037 p->dead = 1;
01038 }
01039 strcpy(moh, "default");
01040
01041 recordagentcalls = 0;
01042 strcpy(recordformat, "wav");
01043 strcpy(recordformatext, "wav");
01044 urlprefix[0] = '\0';
01045 savecallsin[0] = '\0';
01046
01047
01048 if ((general_val = ast_variable_retrieve(cfg, "general", "persistentagents")))
01049 persistent_agents = ast_true(general_val);
01050 multiplelogin = ast_true(ast_variable_retrieve(cfg, "general", "multiplelogin"));
01051
01052
01053 v = ast_variable_browse(cfg, "agents");
01054 while(v) {
01055
01056 if (!strcasecmp(v->name, "agent")) {
01057 add_agent(v->value, 0);
01058 } else if (!strcasecmp(v->name, "group")) {
01059 group = ast_get_group(v->value);
01060 } else if (!strcasecmp(v->name, "autologoff")) {
01061 autologoff = atoi(v->value);
01062 if (autologoff < 0)
01063 autologoff = 0;
01064 } else if (!strcasecmp(v->name, "ackcall")) {
01065 if (!strcasecmp(v->value, "always"))
01066 ackcall = 2;
01067 else if (ast_true(v->value))
01068 ackcall = 1;
01069 else
01070 ackcall = 0;
01071 } else if (!strcasecmp(v->name, "endcall")) {
01072 endcall = ast_true(v->value);
01073 } else if (!strcasecmp(v->name, "wrapuptime")) {
01074 wrapuptime = atoi(v->value);
01075 if (wrapuptime < 0)
01076 wrapuptime = 0;
01077 } else if (!strcasecmp(v->name, "maxlogintries") && !ast_strlen_zero(v->value)) {
01078 maxlogintries = atoi(v->value);
01079 if (maxlogintries < 0)
01080 maxlogintries = 0;
01081 } else if (!strcasecmp(v->name, "goodbye") && !ast_strlen_zero(v->value)) {
01082 strcpy(agentgoodbye,v->value);
01083 } else if (!strcasecmp(v->name, "musiconhold")) {
01084 ast_copy_string(moh, v->value, sizeof(moh));
01085 } else if (!strcasecmp(v->name, "updatecdr")) {
01086 if (ast_true(v->value))
01087 updatecdr = 1;
01088 else
01089 updatecdr = 0;
01090 } else if (!strcasecmp(v->name, "autologoffunavail")) {
01091 if (ast_true(v->value))
01092 autologoffunavail = 1;
01093 else
01094 autologoffunavail = 0;
01095 } else if (!strcasecmp(v->name, "recordagentcalls")) {
01096 recordagentcalls = ast_true(v->value);
01097 } else if (!strcasecmp(v->name, "recordformat")) {
01098 ast_copy_string(recordformat, v->value, sizeof(recordformat));
01099 if (!strcasecmp(v->value, "wav49"))
01100 strcpy(recordformatext, "WAV");
01101 else
01102 ast_copy_string(recordformatext, v->value, sizeof(recordformatext));
01103 } else if (!strcasecmp(v->name, "urlprefix")) {
01104 ast_copy_string(urlprefix, v->value, sizeof(urlprefix));
01105 if (urlprefix[strlen(urlprefix) - 1] != '/')
01106 strncat(urlprefix, "/", sizeof(urlprefix) - strlen(urlprefix) - 1);
01107 } else if (!strcasecmp(v->name, "savecallsin")) {
01108 if (v->value[0] == '/')
01109 ast_copy_string(savecallsin, v->value, sizeof(savecallsin));
01110 else
01111 snprintf(savecallsin, sizeof(savecallsin) - 2, "/%s", v->value);
01112 if (savecallsin[strlen(savecallsin) - 1] != '/')
01113 strncat(savecallsin, "/", sizeof(savecallsin) - strlen(savecallsin) - 1);
01114 } else if (!strcasecmp(v->name, "custom_beep")) {
01115 ast_copy_string(beep, v->value, sizeof(beep));
01116 }
01117 v = v->next;
01118 }
01119 if ((ucfg = ast_config_load("users.conf"))) {
01120 genhasagent = ast_true(ast_variable_retrieve(ucfg, "general", "hasagent"));
01121 catname = ast_category_browse(ucfg, NULL);
01122 while(catname) {
01123 if (strcasecmp(catname, "general")) {
01124 hasagent = ast_variable_retrieve(ucfg, catname, "hasagent");
01125 if (ast_true(hasagent) || (!hasagent && genhasagent)) {
01126 char tmp[256];
01127 const char *fullname = ast_variable_retrieve(ucfg, catname, "fullname");
01128 const char *secret = ast_variable_retrieve(ucfg, catname, "secret");
01129 if (!fullname)
01130 fullname = "";
01131 if (!secret)
01132 secret = "";
01133 snprintf(tmp, sizeof(tmp), "%s,%s,%s", catname, secret,fullname);
01134 add_agent(tmp, 0);
01135 }
01136 }
01137 catname = ast_category_browse(ucfg, catname);
01138 }
01139 ast_config_destroy(ucfg);
01140 }
01141 AST_LIST_TRAVERSE_SAFE_BEGIN(&agents, p, list) {
01142 if (p->dead) {
01143 AST_LIST_REMOVE_CURRENT(&agents, list);
01144
01145 if (!p->owner) {
01146 if (!p->chan) {
01147 ast_mutex_destroy(&p->lock);
01148 ast_mutex_destroy(&p->app_lock);
01149 free(p);
01150 } else {
01151
01152 ast_softhangup(p->chan, AST_SOFTHANGUP_EXPLICIT);
01153 }
01154 }
01155 }
01156 }
01157 AST_LIST_TRAVERSE_SAFE_END
01158 AST_LIST_UNLOCK(&agents);
01159 ast_config_destroy(cfg);
01160 return 1;
01161 }
01162
01163 static int check_availability(struct agent_pvt *newlyavailable, int needlock)
01164 {
01165 struct ast_channel *chan=NULL, *parent=NULL;
01166 struct agent_pvt *p;
01167 int res;
01168
01169 if (option_debug)
01170 ast_log(LOG_DEBUG, "Checking availability of '%s'\n", newlyavailable->agent);
01171 if (needlock)
01172 AST_LIST_LOCK(&agents);
01173 AST_LIST_TRAVERSE(&agents, p, list) {
01174 if (p == newlyavailable) {
01175 continue;
01176 }
01177 ast_mutex_lock(&p->lock);
01178 if (!p->abouttograb && p->pending && ((p->group && (newlyavailable->group & p->group)) || !strcmp(p->agent, newlyavailable->agent))) {
01179 if (option_debug)
01180 ast_log(LOG_DEBUG, "Call '%s' looks like a winner for agent '%s'\n", p->owner->name, newlyavailable->agent);
01181
01182 chan = agent_new(newlyavailable, AST_STATE_DOWN);
01183 parent = p->owner;
01184 p->abouttograb = 1;
01185 ast_mutex_unlock(&p->lock);
01186 break;
01187 }
01188 ast_mutex_unlock(&p->lock);
01189 }
01190 if (needlock)
01191 AST_LIST_UNLOCK(&agents);
01192 if (parent && chan) {
01193 if (newlyavailable->ackcall > 1) {
01194
01195 res = 0;
01196 } else {
01197 if (option_debug > 2)
01198 ast_log( LOG_DEBUG, "Playing beep, lang '%s'\n", newlyavailable->chan->language);
01199 res = ast_streamfile(newlyavailable->chan, beep, newlyavailable->chan->language);
01200 if (option_debug > 2)
01201 ast_log( LOG_DEBUG, "Played beep, result '%d'\n", res);
01202 if (!res) {
01203 res = ast_waitstream(newlyavailable->chan, "");
01204 ast_log( LOG_DEBUG, "Waited for stream, result '%d'\n", res);
01205 }
01206 }
01207 if (!res) {
01208
01209 if (p->abouttograb) {
01210 newlyavailable->acknowledged = 1;
01211
01212 ast_setstate(parent, AST_STATE_UP);
01213 ast_setstate(chan, AST_STATE_UP);
01214 ast_copy_string(parent->context, chan->context, sizeof(parent->context));
01215
01216
01217 ast_mutex_lock(&parent->lock);
01218 ast_set_flag(chan, AST_FLAG_ZOMBIE);
01219 ast_channel_masquerade(parent, chan);
01220 ast_mutex_unlock(&parent->lock);
01221 p->abouttograb = 0;
01222 } else {
01223 if (option_debug)
01224 ast_log(LOG_DEBUG, "Sneaky, parent disappeared in the mean time...\n");
01225 agent_cleanup(newlyavailable);
01226 }
01227 } else {
01228 if (option_debug)
01229 ast_log(LOG_DEBUG, "Ugh... Agent hung up at exactly the wrong time\n");
01230 agent_cleanup(newlyavailable);
01231 }
01232 }
01233 return 0;
01234 }
01235
01236 static int check_beep(struct agent_pvt *newlyavailable, int needlock)
01237 {
01238 struct agent_pvt *p;
01239 int res=0;
01240
01241 ast_log(LOG_DEBUG, "Checking beep availability of '%s'\n", newlyavailable->agent);
01242 if (needlock)
01243 AST_LIST_LOCK(&agents);
01244 AST_LIST_TRAVERSE(&agents, p, list) {
01245 if (p == newlyavailable) {
01246 continue;
01247 }
01248 ast_mutex_lock(&p->lock);
01249 if (!p->abouttograb && p->pending && ((p->group && (newlyavailable->group & p->group)) || !strcmp(p->agent, newlyavailable->agent))) {
01250 if (option_debug)
01251 ast_log(LOG_DEBUG, "Call '%s' looks like a would-be winner for agent '%s'\n", p->owner->name, newlyavailable->agent);
01252 ast_mutex_unlock(&p->lock);
01253 break;
01254 }
01255 ast_mutex_unlock(&p->lock);
01256 }
01257 if (needlock)
01258 AST_LIST_UNLOCK(&agents);
01259 if (p) {
01260 ast_mutex_unlock(&newlyavailable->lock);
01261 if (option_debug > 2)
01262 ast_log( LOG_DEBUG, "Playing beep, lang '%s'\n", newlyavailable->chan->language);
01263 res = ast_streamfile(newlyavailable->chan, beep, newlyavailable->chan->language);
01264 if (option_debug > 2)
01265 ast_log( LOG_DEBUG, "Played beep, result '%d'\n", res);
01266 if (!res) {
01267 res = ast_waitstream(newlyavailable->chan, "");
01268 if (option_debug)
01269 ast_log( LOG_DEBUG, "Waited for stream, result '%d'\n", res);
01270 }
01271 ast_mutex_lock(&newlyavailable->lock);
01272 }
01273 return res;
01274 }
01275
01276
01277 static int allow_multiple_login(char *chan, char *context)
01278 {
01279 struct agent_pvt *p;
01280 char loginchan[80];
01281
01282 if(multiplelogin)
01283 return 1;
01284 if(!chan)
01285 return 0;
01286
01287 snprintf(loginchan, sizeof(loginchan), "%s@%s", chan, S_OR(context, "default"));
01288
01289 AST_LIST_TRAVERSE(&agents, p, list) {
01290 if(!strcasecmp(chan, p->loginchan))
01291 return 0;
01292 }
01293 return -1;
01294 }
01295
01296
01297 static struct ast_channel *agent_request(const char *type, int format, void *data, int *cause)
01298 {
01299 struct agent_pvt *p;
01300 struct ast_channel *chan = NULL;
01301 char *s;
01302 ast_group_t groupmatch;
01303 int groupoff;
01304 int waitforagent=0;
01305 int hasagent = 0;
01306 struct timeval tv;
01307
01308 s = data;
01309 if ((s[0] == '@') && (sscanf(s + 1, "%d", &groupoff) == 1)) {
01310 groupmatch = (1 << groupoff);
01311 } else if ((s[0] == ':') && (sscanf(s + 1, "%d", &groupoff) == 1)) {
01312 groupmatch = (1 << groupoff);
01313 waitforagent = 1;
01314 } else
01315 groupmatch = 0;
01316
01317
01318 AST_LIST_LOCK(&agents);
01319 AST_LIST_TRAVERSE(&agents, p, list) {
01320 ast_mutex_lock(&p->lock);
01321 if (!p->pending && ((groupmatch && (p->group & groupmatch)) || !strcmp(data, p->agent)) &&
01322 ast_strlen_zero(p->loginchan)) {
01323 if (p->chan)
01324 hasagent++;
01325 if (!p->lastdisc.tv_sec) {
01326
01327 if (!p->owner && p->chan) {
01328
01329 chan = agent_new(p, AST_STATE_DOWN);
01330 }
01331 if (chan) {
01332 ast_mutex_unlock(&p->lock);
01333 break;
01334 }
01335 }
01336 }
01337 ast_mutex_unlock(&p->lock);
01338 }
01339 if (!p) {
01340 AST_LIST_TRAVERSE(&agents, p, list) {
01341 ast_mutex_lock(&p->lock);
01342 if (!p->pending && ((groupmatch && (p->group & groupmatch)) || !strcmp(data, p->agent))) {
01343 if (p->chan || !ast_strlen_zero(p->loginchan))
01344 hasagent++;
01345 tv = ast_tvnow();
01346 #if 0
01347 ast_log(LOG_NOTICE, "Time now: %ld, Time of lastdisc: %ld\n", tv.tv_sec, p->lastdisc.tv_sec);
01348 #endif
01349 if (!p->lastdisc.tv_sec || (tv.tv_sec > p->lastdisc.tv_sec)) {
01350 p->lastdisc = ast_tv(0, 0);
01351
01352 if (!p->owner && p->chan) {
01353
01354 chan = agent_new(p, AST_STATE_DOWN);
01355 } else if (!p->owner && !ast_strlen_zero(p->loginchan)) {
01356
01357 p->chan = ast_request("Local", format, p->loginchan, cause);
01358 if (p->chan)
01359 chan = agent_new(p, AST_STATE_DOWN);
01360 }
01361 if (chan) {
01362 ast_mutex_unlock(&p->lock);
01363 break;
01364 }
01365 }
01366 }
01367 ast_mutex_unlock(&p->lock);
01368 }
01369 }
01370
01371 if (!chan && waitforagent) {
01372
01373
01374 if (hasagent) {
01375 if (option_debug)
01376 ast_log(LOG_DEBUG, "Creating place holder for '%s'\n", s);
01377 p = add_agent(data, 1);
01378 p->group = groupmatch;
01379 chan = agent_new(p, AST_STATE_DOWN);
01380 if (!chan)
01381 ast_log(LOG_WARNING, "Weird... Fix this to drop the unused pending agent\n");
01382 } else
01383 ast_log(LOG_DEBUG, "Not creating place holder for '%s' since nobody logged in\n", s);
01384 }
01385 *cause = hasagent ? AST_CAUSE_BUSY : AST_CAUSE_UNREGISTERED;
01386 AST_LIST_UNLOCK(&agents);
01387 return chan;
01388 }
01389
01390 static force_inline int powerof(unsigned int d)
01391 {
01392 int x = ffs(d);
01393
01394 if (x)
01395 return x - 1;
01396
01397 return 0;
01398 }
01399
01400
01401
01402
01403
01404
01405
01406
01407
01408 static int action_agents(struct mansession *s, const struct message *m)
01409 {
01410 const char *id = astman_get_header(m,"ActionID");
01411 char idText[256] = "";
01412 char chanbuf[256];
01413 struct agent_pvt *p;
01414 char *username = NULL;
01415 char *loginChan = NULL;
01416 char *talkingtoChan = NULL;
01417 char *status = NULL;
01418
01419 if (!ast_strlen_zero(id))
01420 snprintf(idText, sizeof(idText) ,"ActionID: %s\r\n", id);
01421 astman_send_ack(s, m, "Agents will follow");
01422 AST_LIST_LOCK(&agents);
01423 AST_LIST_TRAVERSE(&agents, p, list) {
01424 ast_mutex_lock(&p->lock);
01425
01426
01427
01428
01429
01430
01431
01432 username = S_OR(p->name, "None");
01433
01434
01435 status = "AGENT_UNKNOWN";
01436
01437 if (!ast_strlen_zero(p->loginchan) && !p->chan) {
01438 loginChan = p->loginchan;
01439 talkingtoChan = "n/a";
01440 status = "AGENT_IDLE";
01441 if (p->acknowledged) {
01442 snprintf(chanbuf, sizeof(chanbuf), " %s (Confirmed)", p->loginchan);
01443 loginChan = chanbuf;
01444 }
01445 } else if (p->chan) {
01446 loginChan = ast_strdupa(p->chan->name);
01447 if (p->owner && p->owner->_bridge) {
01448 talkingtoChan = p->chan->cid.cid_num;
01449 status = "AGENT_ONCALL";
01450 } else {
01451 talkingtoChan = "n/a";
01452 status = "AGENT_IDLE";
01453 }
01454 } else {
01455 loginChan = "n/a";
01456 talkingtoChan = "n/a";
01457 status = "AGENT_LOGGEDOFF";
01458 }
01459
01460 astman_append(s, "Event: Agents\r\n"
01461 "Agent: %s\r\n"
01462 "Name: %s\r\n"
01463 "Status: %s\r\n"
01464 "LoggedInChan: %s\r\n"
01465 "LoggedInTime: %d\r\n"
01466 "TalkingTo: %s\r\n"
01467 "%s"
01468 "\r\n",
01469 p->agent, username, status, loginChan, (int)p->loginstart, talkingtoChan, idText);
01470 ast_mutex_unlock(&p->lock);
01471 }
01472 AST_LIST_UNLOCK(&agents);
01473 astman_append(s, "Event: AgentsComplete\r\n"
01474 "%s"
01475 "\r\n",idText);
01476 return 0;
01477 }
01478
01479 static void agent_logoff_maintenance(struct agent_pvt *p, char *loginchan, long logintime, const char *uniqueid, char *logcommand)
01480 {
01481 char *tmp = NULL;
01482 char agent[AST_MAX_AGENT];
01483
01484 if (!ast_strlen_zero(logcommand))
01485 tmp = logcommand;
01486 else
01487 tmp = ast_strdupa("");
01488
01489 snprintf(agent, sizeof(agent), "Agent/%s", p->agent);
01490
01491 if (!ast_strlen_zero(uniqueid)) {
01492 manager_event(EVENT_FLAG_AGENT, "Agentcallbacklogoff",
01493 "Agent: %s\r\n"
01494 "Reason: %s\r\n"
01495 "Loginchan: %s\r\n"
01496 "Logintime: %ld\r\n"
01497 "Uniqueid: %s\r\n",
01498 p->agent, tmp, loginchan, logintime, uniqueid);
01499 } else {
01500 manager_event(EVENT_FLAG_AGENT, "Agentcallbacklogoff",
01501 "Agent: %s\r\n"
01502 "Reason: %s\r\n"
01503 "Loginchan: %s\r\n"
01504 "Logintime: %ld\r\n",
01505 p->agent, tmp, loginchan, logintime);
01506 }
01507
01508 ast_queue_log("NONE", ast_strlen_zero(uniqueid) ? "NONE" : uniqueid, agent, "AGENTCALLBACKLOGOFF", "%s|%ld|%s", loginchan, logintime, tmp);
01509 set_agentbycallerid(p->logincallerid, NULL);
01510 p->loginchan[0] ='\0';
01511 p->logincallerid[0] = '\0';
01512 ast_device_state_changed("Agent/%s", p->agent);
01513 if (persistent_agents)
01514 dump_agents();
01515
01516 }
01517
01518 static int agent_logoff(const char *agent, int soft)
01519 {
01520 struct agent_pvt *p;
01521 long logintime;
01522 int ret = -1;
01523
01524 AST_LIST_TRAVERSE(&agents, p, list) {
01525 if (!strcasecmp(p->agent, agent)) {
01526 ret = 0;
01527 if (p->owner || p->chan) {
01528 p->deferlogoff = 1;
01529 if (!soft) {
01530 if (p->owner)
01531 ast_softhangup(p->owner, AST_SOFTHANGUP_EXPLICIT);
01532 if (p->chan)
01533 ast_softhangup(p->chan, AST_SOFTHANGUP_EXPLICIT);
01534 }
01535 } else {
01536 logintime = time(NULL) - p->loginstart;
01537 p->loginstart = 0;
01538 agent_logoff_maintenance(p, p->loginchan, logintime, NULL, "CommandLogoff");
01539 }
01540 break;
01541 }
01542 }
01543
01544 return ret;
01545 }
01546
01547 static int agent_logoff_cmd(int fd, int argc, char **argv)
01548 {
01549 int ret;
01550 char *agent;
01551
01552 if (argc < 3 || argc > 4)
01553 return RESULT_SHOWUSAGE;
01554 if (argc == 4 && strcasecmp(argv[3], "soft"))
01555 return RESULT_SHOWUSAGE;
01556
01557 agent = argv[2] + 6;
01558 ret = agent_logoff(agent, argc == 4);
01559 if (ret == 0)
01560 ast_cli(fd, "Logging out %s\n", agent);
01561
01562 return RESULT_SUCCESS;
01563 }
01564
01565
01566
01567
01568
01569
01570
01571
01572
01573 static int action_agent_logoff(struct mansession *s, const struct message *m)
01574 {
01575 const char *agent = astman_get_header(m, "Agent");
01576 const char *soft_s = astman_get_header(m, "Soft");
01577 int soft;
01578 int ret;
01579
01580 if (ast_strlen_zero(agent)) {
01581 astman_send_error(s, m, "No agent specified");
01582 return 0;
01583 }
01584
01585 soft = ast_true(soft_s) ? 1 : 0;
01586 ret = agent_logoff(agent, soft);
01587 if (ret == 0)
01588 astman_send_ack(s, m, "Agent logged out");
01589 else
01590 astman_send_error(s, m, "No such agent");
01591
01592 return 0;
01593 }
01594
01595 static char *complete_agent_logoff_cmd(const char *line, const char *word, int pos, int state)
01596 {
01597 if (pos == 2) {
01598 struct agent_pvt *p;
01599 char name[AST_MAX_AGENT];
01600 int which = 0, len = strlen(word);
01601
01602 AST_LIST_TRAVERSE(&agents, p, list) {
01603 snprintf(name, sizeof(name), "Agent/%s", p->agent);
01604 if (!strncasecmp(word, name, len) && ++which > state)
01605 return ast_strdup(name);
01606 }
01607 } else if (pos == 3 && state == 0)
01608 return ast_strdup("soft");
01609
01610 return NULL;
01611 }
01612
01613
01614
01615
01616 static int agents_show(int fd, int argc, char **argv)
01617 {
01618 struct agent_pvt *p;
01619 char username[AST_MAX_BUF];
01620 char location[AST_MAX_BUF] = "";
01621 char talkingto[AST_MAX_BUF] = "";
01622 char moh[AST_MAX_BUF];
01623 int count_agents = 0;
01624 int online_agents = 0;
01625 int offline_agents = 0;
01626 if (argc != 2)
01627 return RESULT_SHOWUSAGE;
01628 AST_LIST_LOCK(&agents);
01629 AST_LIST_TRAVERSE(&agents, p, list) {
01630 ast_mutex_lock(&p->lock);
01631 if (p->pending) {
01632 if (p->group)
01633 ast_cli(fd, "-- Pending call to group %d\n", powerof(p->group));
01634 else
01635 ast_cli(fd, "-- Pending call to agent %s\n", p->agent);
01636 } else {
01637 if (!ast_strlen_zero(p->name))
01638 snprintf(username, sizeof(username), "(%s) ", p->name);
01639 else
01640 username[0] = '\0';
01641 if (p->chan) {
01642 snprintf(location, sizeof(location), "logged in on %s", p->chan->name);
01643 if (p->owner && ast_bridged_channel(p->owner))
01644 snprintf(talkingto, sizeof(talkingto), " talking to %s", ast_bridged_channel(p->owner)->name);
01645 else
01646 strcpy(talkingto, " is idle");
01647 online_agents++;
01648 } else if (!ast_strlen_zero(p->loginchan)) {
01649 if (ast_tvdiff_ms(ast_tvnow(), p->lastdisc) > 0 || !(p->lastdisc.tv_sec))
01650 snprintf(location, sizeof(location) - 20, "available at '%s'", p->loginchan);
01651 else
01652 snprintf(location, sizeof(location) - 20, "wrapping up at '%s'", p->loginchan);
01653 talkingto[0] = '\0';
01654 online_agents++;
01655 if (p->acknowledged)
01656 strncat(location, " (Confirmed)", sizeof(location) - strlen(location) - 1);
01657 } else {
01658 strcpy(location, "not logged in");
01659 talkingto[0] = '\0';
01660 offline_agents++;
01661 }
01662 if (!ast_strlen_zero(p->moh))
01663 snprintf(moh, sizeof(moh), " (musiconhold is '%s')", p->moh);
01664 ast_cli(fd, "%-12.12s %s%s%s%s\n", p->agent,
01665 username, location, talkingto, moh);
01666 count_agents++;
01667 }
01668 ast_mutex_unlock(&p->lock);
01669 }
01670 AST_LIST_UNLOCK(&agents);
01671 if ( !count_agents )
01672 ast_cli(fd, "No Agents are configured in %s\n",config);
01673 else
01674 ast_cli(fd, "%d agents configured [%d online , %d offline]\n",count_agents, online_agents, offline_agents);
01675 ast_cli(fd, "\n");
01676
01677 return RESULT_SUCCESS;
01678 }
01679
01680
01681 static int agents_show_online(int fd, int argc, char **argv)
01682 {
01683 struct agent_pvt *p;
01684 char username[AST_MAX_BUF];
01685 char location[AST_MAX_BUF] = "";
01686 char talkingto[AST_MAX_BUF] = "";
01687 char moh[AST_MAX_BUF];
01688 int count_agents = 0;
01689 int online_agents = 0;
01690 int agent_status = 0;
01691 if (argc != 3)
01692 return RESULT_SHOWUSAGE;
01693 AST_LIST_LOCK(&agents);
01694 AST_LIST_TRAVERSE(&agents, p, list) {
01695 agent_status = 0;
01696 ast_mutex_lock(&p->lock);
01697 if (!ast_strlen_zero(p->name))
01698 snprintf(username, sizeof(username), "(%s) ", p->name);
01699 else
01700 username[0] = '\0';
01701 if (p->chan) {
01702 snprintf(location, sizeof(location), "logged in on %s", p->chan->name);
01703 if (p->owner && ast_bridged_channel(p->owner))
01704 snprintf(talkingto, sizeof(talkingto), " talking to %s", ast_bridged_channel(p->owner)->name);
01705 else
01706 strcpy(talkingto, " is idle");
01707 agent_status = 1;
01708 online_agents++;
01709 } else if (!ast_strlen_zero(p->loginchan)) {
01710 snprintf(location, sizeof(location) - 20, "available at '%s'", p->loginchan);
01711 talkingto[0] = '\0';
01712 agent_status = 1;
01713 online_agents++;
01714 if (p->acknowledged)
01715 strncat(location, " (Confirmed)", sizeof(location) - strlen(location) - 1);
01716 }
01717 if (!ast_strlen_zero(p->moh))
01718 snprintf(moh, sizeof(moh), " (musiconhold is '%s')", p->moh);
01719 if (agent_status)
01720 ast_cli(fd, "%-12.12s %s%s%s%s\n", p->agent, username, location, talkingto, moh);
01721 count_agents++;
01722 ast_mutex_unlock(&p->lock);
01723 }
01724 AST_LIST_UNLOCK(&agents);
01725 if (!count_agents)
01726 ast_cli(fd, "No Agents are configured in %s\n", config);
01727 else
01728 ast_cli(fd, "%d agents online\n", online_agents);
01729 ast_cli(fd, "\n");
01730 return RESULT_SUCCESS;
01731 }
01732
01733
01734
01735 static char show_agents_usage[] =
01736 "Usage: agent show\n"
01737 " Provides summary information on agents.\n";
01738
01739 static char show_agents_online_usage[] =
01740 "Usage: agent show online\n"
01741 " Provides a list of all online agents.\n";
01742
01743 static char agent_logoff_usage[] =
01744 "Usage: agent logoff <channel> [soft]\n"
01745 " Sets an agent as no longer logged in.\n"
01746 " If 'soft' is specified, do not hangup existing calls.\n";
01747
01748 static struct ast_cli_entry cli_show_agents_deprecated = {
01749 { "show", "agents", NULL },
01750 agents_show, NULL,
01751 NULL, NULL };
01752
01753 static struct ast_cli_entry cli_show_agents_online_deprecated = {
01754 { "show", "agents", "online" },
01755 agents_show_online, NULL,
01756 NULL, NULL };
01757
01758 static struct ast_cli_entry cli_agents[] = {
01759 { { "agent", "show", NULL },
01760 agents_show, "Show status of agents",
01761 show_agents_usage, NULL, &cli_show_agents_deprecated },
01762
01763 { { "agent", "show", "online" },
01764 agents_show_online, "Show all online agents",
01765 show_agents_online_usage, NULL, &cli_show_agents_online_deprecated },
01766
01767 { { "agent", "logoff", NULL },
01768 agent_logoff_cmd, "Sets an agent offline",
01769 agent_logoff_usage, complete_agent_logoff_cmd },
01770 };
01771
01772
01773
01774
01775
01776
01777
01778
01779 static int __login_exec(struct ast_channel *chan, void *data, int callbackmode)
01780 {
01781 int res=0;
01782 int tries = 0;
01783 int max_login_tries = maxlogintries;
01784 struct agent_pvt *p;
01785 struct ast_module_user *u;
01786 int login_state = 0;
01787 char user[AST_MAX_AGENT] = "";
01788 char pass[AST_MAX_AGENT];
01789 char agent[AST_MAX_AGENT] = "";
01790 char xpass[AST_MAX_AGENT] = "";
01791 char *errmsg;
01792 char *parse;
01793 AST_DECLARE_APP_ARGS(args,
01794 AST_APP_ARG(agent_id);
01795 AST_APP_ARG(options);
01796 AST_APP_ARG(extension);
01797 );
01798 const char *tmpoptions = NULL;
01799 char *context = NULL;
01800 int play_announcement = 1;
01801 char agent_goodbye[AST_MAX_FILENAME_LEN];
01802 int update_cdr = updatecdr;
01803 char *filename = "agent-loginok";
01804 char tmpchan[AST_MAX_BUF] = "";
01805
01806 u = ast_module_user_add(chan);
01807
01808 parse = ast_strdupa(data);
01809
01810 AST_STANDARD_APP_ARGS(args, parse);
01811
01812 ast_copy_string(agent_goodbye, agentgoodbye, sizeof(agent_goodbye));
01813
01814
01815 if (pbx_builtin_getvar_helper(chan, "AGENTLMAXLOGINTRIES") && strlen(pbx_builtin_getvar_helper(chan, "AGENTLMAXLOGINTRIES"))) {
01816 max_login_tries = atoi(pbx_builtin_getvar_helper(chan, "AGENTMAXLOGINTRIES"));
01817 if (max_login_tries < 0)
01818 max_login_tries = 0;
01819 tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTMAXLOGINTRIES");
01820 if (option_verbose > 2)
01821 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);
01822 }
01823 if (pbx_builtin_getvar_helper(chan, "AGENTUPDATECDR") && !ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENTUPDATECDR"))) {
01824 if (ast_true(pbx_builtin_getvar_helper(chan, "AGENTUPDATECDR")))
01825 update_cdr = 1;
01826 else
01827 update_cdr = 0;
01828 tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTUPDATECDR");
01829 if (option_verbose > 2)
01830 ast_verbose(VERBOSE_PREFIX_3 "Saw variable AGENTUPDATECDR=%s, setting update_cdr to: %d on Channel '%s'.\n",tmpoptions,update_cdr,chan->name);
01831 }
01832 if (pbx_builtin_getvar_helper(chan, "AGENTGOODBYE") && !ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENTGOODBYE"))) {
01833 strcpy(agent_goodbye, pbx_builtin_getvar_helper(chan, "AGENTGOODBYE"));
01834 tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTGOODBYE");
01835 if (option_verbose > 2)
01836 ast_verbose(VERBOSE_PREFIX_3 "Saw variable AGENTGOODBYE=%s, setting agent_goodbye to: %s on Channel '%s'.\n",tmpoptions,agent_goodbye,chan->name);
01837 }
01838
01839
01840 if (callbackmode && args.extension) {
01841 parse = args.extension;
01842 args.extension = strsep(&parse, "@");
01843 context = parse;
01844 }
01845
01846 if (!ast_strlen_zero(args.options)) {
01847 if (strchr(args.options, 's')) {
01848 play_announcement = 0;
01849 }
01850 }
01851
01852 if (chan->_state != AST_STATE_UP)
01853 res = ast_answer(chan);
01854 if (!res) {
01855 if (!ast_strlen_zero(args.agent_id))
01856 ast_copy_string(user, args.agent_id, AST_MAX_AGENT);
01857 else
01858 res = ast_app_getdata(chan, "agent-user", user, sizeof(user) - 1, 0);
01859 }
01860 while (!res && (max_login_tries==0 || tries < max_login_tries)) {
01861 tries++;
01862
01863 AST_LIST_LOCK(&agents);
01864 AST_LIST_TRAVERSE(&agents, p, list) {
01865 if (!strcmp(p->agent, user) && !p->pending)
01866 ast_copy_string(xpass, p->password, sizeof(xpass));
01867 }
01868 AST_LIST_UNLOCK(&agents);
01869 if (!res) {
01870 if (!ast_strlen_zero(xpass))
01871 res = ast_app_getdata(chan, "agent-pass", pass, sizeof(pass) - 1, 0);
01872 else
01873 pass[0] = '\0';
01874 }
01875 errmsg = "agent-incorrect";
01876
01877 #if 0
01878 ast_log(LOG_NOTICE, "user: %s, pass: %s\n", user, pass);
01879 #endif
01880
01881
01882 AST_LIST_LOCK(&agents);
01883 AST_LIST_TRAVERSE(&agents, p, list) {
01884 ast_mutex_lock(&p->lock);
01885 if (!strcmp(p->agent, user) &&
01886 !strcmp(p->password, pass) && !p->pending) {
01887 login_state = 1;
01888
01889
01890 gettimeofday(&p->lastdisc, NULL);
01891 p->lastdisc.tv_sec++;
01892
01893
01894 if (pbx_builtin_getvar_helper(chan, "AGENTACKCALL") && strlen(pbx_builtin_getvar_helper(chan, "AGENTACKCALL"))) {
01895 if (!strcasecmp(pbx_builtin_getvar_helper(chan, "AGENTACKCALL"), "always"))
01896 p->ackcall = 2;
01897 else if (ast_true(pbx_builtin_getvar_helper(chan, "AGENTACKCALL")))
01898 p->ackcall = 1;
01899 else
01900 p->ackcall = 0;
01901 tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTACKCALL");
01902 if (option_verbose > 2)
01903 ast_verbose(VERBOSE_PREFIX_3 "Saw variable AGENTACKCALL=%s, setting ackcall to: %d for Agent '%s'.\n",tmpoptions,p->ackcall,p->agent);
01904 }
01905 if (pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF") && strlen(pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF"))) {
01906 p->autologoff = atoi(pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF"));
01907 if (p->autologoff < 0)
01908 p->autologoff = 0;
01909 tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF");
01910 if (option_verbose > 2)
01911 ast_verbose(VERBOSE_PREFIX_3 "Saw variable AGENTAUTOLOGOFF=%s, setting autologff to: %d for Agent '%s'.\n",tmpoptions,p->autologoff,p->agent);
01912 }
01913 if (pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME") && strlen(pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME"))) {
01914 p->wrapuptime = atoi(pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME"));
01915 if (p->wrapuptime < 0)
01916 p->wrapuptime = 0;
01917 tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME");
01918 if (option_verbose > 2)
01919 ast_verbose(VERBOSE_PREFIX_3 "Saw variable AGENTWRAPUPTIME=%s, setting wrapuptime to: %d for Agent '%s'.\n",tmpoptions,p->wrapuptime,p->agent);
01920 }
01921
01922 if (!p->chan) {
01923 char last_loginchan[80] = "";
01924 long logintime;
01925 snprintf(agent, sizeof(agent), "Agent/%s", p->agent);
01926
01927 if (callbackmode) {
01928 int pos = 0;
01929
01930 for (;;) {
01931 if (!ast_strlen_zero(args.extension)) {
01932 ast_copy_string(tmpchan, args.extension, sizeof(tmpchan));
01933 res = 0;
01934 } else
01935 res = ast_app_getdata(chan, "agent-newlocation", tmpchan+pos, sizeof(tmpchan) - 2, 0);
01936 if (ast_strlen_zero(tmpchan) )
01937 break;
01938 if(ast_exists_extension(chan, S_OR(context,"default"), tmpchan,1, NULL) ) {
01939 if(!allow_multiple_login(tmpchan,context) ) {
01940 args.extension = NULL;
01941 pos = 0;
01942 } else
01943 break;
01944 }
01945 if (args.extension) {
01946 ast_log(LOG_WARNING, "Extension '%s' is not valid for automatic login of agent '%s'\n", args.extension, p->agent);
01947 args.extension = NULL;
01948 pos = 0;
01949 } else {
01950 ast_log(LOG_WARNING, "Extension '%s@%s' is not valid for automatic login of agent '%s'\n", tmpchan, S_OR(context, "default"), p->agent);
01951 res = ast_streamfile(chan, "invalid", chan->language);
01952 if (!res)
01953 res = ast_waitstream(chan, AST_DIGIT_ANY);
01954 if (res > 0) {
01955 tmpchan[0] = res;
01956 tmpchan[1] = '\0';
01957 pos = 1;
01958 } else {
01959 tmpchan[0] = '\0';
01960 pos = 0;
01961 }
01962 }
01963 }
01964 args.extension = tmpchan;
01965 if (!res) {
01966 set_agentbycallerid(p->logincallerid, NULL);
01967 if (!ast_strlen_zero(context) && !ast_strlen_zero(tmpchan))
01968 snprintf(p->loginchan, sizeof(p->loginchan), "%s@%s", tmpchan, context);
01969 else {
01970 ast_copy_string(last_loginchan, p->loginchan, sizeof(last_loginchan));
01971 ast_copy_string(p->loginchan, tmpchan, sizeof(p->loginchan));
01972 }
01973 p->acknowledged = 0;
01974 if (ast_strlen_zero(p->loginchan)) {
01975 login_state = 2;
01976 filename = "agent-loggedoff";
01977 } else {
01978 if (chan->cid.cid_num) {
01979 ast_copy_string(p->logincallerid, chan->cid.cid_num, sizeof(p->logincallerid));
01980 set_agentbycallerid(p->logincallerid, p->agent);
01981 } else
01982 p->logincallerid[0] = '\0';
01983 }
01984
01985 if(update_cdr && chan->cdr)
01986 snprintf(chan->cdr->channel, sizeof(chan->cdr->channel), "Agent/%s", p->agent);
01987
01988 }
01989 } else {
01990 p->loginchan[0] = '\0';
01991 p->logincallerid[0] = '\0';
01992 p->acknowledged = 0;
01993 }
01994 ast_mutex_unlock(&p->lock);
01995 AST_LIST_UNLOCK(&agents);
01996 if( !res && play_announcement==1 )
01997 res = ast_streamfile(chan, filename, chan->language);
01998 if (!res)
01999 ast_waitstream(chan, "");
02000 AST_LIST_LOCK(&agents);
02001 ast_mutex_lock(&p->lock);
02002 if (!res) {
02003 res = ast_set_read_format(chan, ast_best_codec(chan->nativeformats));
02004 if (res)
02005 ast_log(LOG_WARNING, "Unable to set read format to %d\n", ast_best_codec(chan->nativeformats));
02006 }
02007 if (!res) {
02008 res = ast_set_write_format(chan, ast_best_codec(chan->nativeformats));
02009 if (res)
02010 ast_log(LOG_WARNING, "Unable to set write format to %d\n", ast_best_codec(chan->nativeformats));
02011 }
02012
02013 if (p->chan)
02014 res = -1;
02015 if (callbackmode && !res) {
02016
02017 if (!ast_strlen_zero(p->loginchan)) {
02018 if (p->loginstart == 0)
02019 time(&p->loginstart);
02020 manager_event(EVENT_FLAG_AGENT, "Agentcallbacklogin",
02021 "Agent: %s\r\n"
02022 "Loginchan: %s\r\n"
02023 "Uniqueid: %s\r\n",
02024 p->agent, p->loginchan, chan->uniqueid);
02025 ast_queue_log("NONE", chan->uniqueid, agent, "AGENTCALLBACKLOGIN", "%s", p->loginchan);
02026 if (option_verbose > 1)
02027 ast_verbose(VERBOSE_PREFIX_2 "Callback Agent '%s' logged in on %s\n", p->agent, p->loginchan);
02028 ast_device_state_changed("Agent/%s", p->agent);
02029 if (persistent_agents)
02030 dump_agents();
02031 } else {
02032 logintime = time(NULL) - p->loginstart;
02033 p->loginstart = 0;
02034
02035 agent_logoff_maintenance(p, last_loginchan, logintime, chan->uniqueid, NULL);
02036 if (option_verbose > 1)
02037 ast_verbose(VERBOSE_PREFIX_2 "Callback Agent '%s' logged out\n", p->agent);
02038 }
02039 AST_LIST_UNLOCK(&agents);
02040 if (!res)
02041 res = ast_safe_sleep(chan, 500);
02042 ast_mutex_unlock(&p->lock);
02043 } else if (!res) {
02044 ast_indicate_data(chan, AST_CONTROL_HOLD,
02045 S_OR(p->moh, NULL),
02046 !ast_strlen_zero(p->moh) ? strlen(p->moh) + 1 : 0);
02047 if (p->loginstart == 0)
02048 time(&p->loginstart);
02049 manager_event(EVENT_FLAG_AGENT, "Agentlogin",
02050 "Agent: %s\r\n"
02051 "Channel: %s\r\n"
02052 "Uniqueid: %s\r\n",
02053 p->agent, chan->name, chan->uniqueid);
02054 if (update_cdr && chan->cdr)
02055 snprintf(chan->cdr->channel, sizeof(chan->cdr->channel), "Agent/%s", p->agent);
02056 ast_queue_log("NONE", chan->uniqueid, agent, "AGENTLOGIN", "%s", chan->name);
02057 if (option_verbose > 1)
02058 ast_verbose(VERBOSE_PREFIX_2 "Agent '%s' logged in (format %s/%s)\n", p->agent,
02059 ast_getformatname(chan->readformat), ast_getformatname(chan->writeformat));
02060
02061 p->chan = chan;
02062 if (p->ackcall > 1)
02063 check_beep(p, 0);
02064 else
02065 check_availability(p, 0);
02066 ast_mutex_unlock(&p->lock);
02067 AST_LIST_UNLOCK(&agents);
02068 ast_device_state_changed("Agent/%s", p->agent);
02069 while (res >= 0) {
02070 ast_mutex_lock(&p->lock);
02071 if (p->chan != chan)
02072 res = -1;
02073 ast_mutex_unlock(&p->lock);
02074
02075 sched_yield();
02076 if (res)
02077 break;
02078
02079 AST_LIST_LOCK(&agents);
02080 ast_mutex_lock(&p->lock);
02081 if (p->lastdisc.tv_sec) {
02082 if (ast_tvdiff_ms(ast_tvnow(), p->lastdisc) > p->wrapuptime) {
02083 if (option_debug)
02084 ast_log(LOG_DEBUG, "Wrapup time for %s expired!\n", p->agent);
02085 p->lastdisc = ast_tv(0, 0);
02086 if (p->ackcall > 1)
02087 check_beep(p, 0);
02088 else
02089 check_availability(p, 0);
02090 }
02091 }
02092 ast_mutex_unlock(&p->lock);
02093 AST_LIST_UNLOCK(&agents);
02094
02095 ast_mutex_lock( &p->app_lock );
02096 ast_mutex_lock(&p->lock);
02097 p->owning_app = pthread_self();
02098 ast_mutex_unlock(&p->lock);
02099 if (p->ackcall > 1)
02100 res = agent_ack_sleep(p);
02101 else
02102 res = ast_safe_sleep_conditional( chan, 1000, agent_cont_sleep, p );
02103 ast_mutex_unlock( &p->app_lock );
02104 if ((p->ackcall > 1) && (res == 1)) {
02105 AST_LIST_LOCK(&agents);
02106 ast_mutex_lock(&p->lock);
02107 check_availability(p, 0);
02108 ast_mutex_unlock(&p->lock);
02109 AST_LIST_UNLOCK(&agents);
02110 res = 0;
02111 }
02112 sched_yield();
02113 }
02114 ast_mutex_lock(&p->lock);
02115 if (res && p->owner)
02116 ast_log(LOG_WARNING, "Huh? We broke out when there was still an owner?\n");
02117
02118 if (p->chan == chan)
02119 p->chan = NULL;
02120 p->acknowledged = 0;
02121 logintime = time(NULL) - p->loginstart;
02122 p->loginstart = 0;
02123 ast_mutex_unlock(&p->lock);
02124 manager_event(EVENT_FLAG_AGENT, "Agentlogoff",
02125 "Agent: %s\r\n"
02126 "Logintime: %ld\r\n"
02127 "Uniqueid: %s\r\n",
02128 p->agent, logintime, chan->uniqueid);
02129 ast_queue_log("NONE", chan->uniqueid, agent, "AGENTLOGOFF", "%s|%ld", chan->name, logintime);
02130 if (option_verbose > 1)
02131 ast_verbose(VERBOSE_PREFIX_2 "Agent '%s' logged out\n", p->agent);
02132
02133 ast_device_state_changed("Agent/%s", p->agent);
02134 if (p->dead && !p->owner) {
02135 ast_mutex_destroy(&p->lock);
02136 ast_mutex_destroy(&p->app_lock);
02137 free(p);
02138 }
02139 }
02140 else {
02141 ast_mutex_unlock(&p->lock);
02142 p = NULL;
02143 }
02144 res = -1;
02145 } else {
02146 ast_mutex_unlock(&p->lock);
02147 errmsg = "agent-alreadyon";
02148 p = NULL;
02149 }
02150 break;
02151 }
02152 ast_mutex_unlock(&p->lock);
02153 }
02154 if (!p)
02155 AST_LIST_UNLOCK(&agents);
02156
02157 if (!res && (max_login_tries==0 || tries < max_login_tries))
02158 res = ast_app_getdata(chan, errmsg, user, sizeof(user) - 1, 0);
02159 }
02160
02161 if (!res)
02162 res = ast_safe_sleep(chan, 500);
02163
02164
02165 if (!callbackmode) {
02166 ast_module_user_remove(u);
02167 return -1;
02168 } else {
02169
02170 if (login_state > 0) {
02171 pbx_builtin_setvar_helper(chan, "AGENTNUMBER", user);
02172 if (login_state==1) {
02173 pbx_builtin_setvar_helper(chan, "AGENTSTATUS", "on");
02174 pbx_builtin_setvar_helper(chan, "AGENTEXTEN", args.extension);
02175 } else
02176 pbx_builtin_setvar_helper(chan, "AGENTSTATUS", "off");
02177 } else {
02178 pbx_builtin_setvar_helper(chan, "AGENTSTATUS", "fail");
02179 }
02180 if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 1, chan->cid.cid_num)) {
02181 ast_module_user_remove(u);
02182 return 0;
02183 }
02184
02185 if (play_announcement) {
02186 if (!res)
02187 res = ast_safe_sleep(chan, 1000);
02188 res = ast_streamfile(chan, agent_goodbye, chan->language);
02189 if (!res)
02190 res = ast_waitstream(chan, "");
02191 if (!res)
02192 res = ast_safe_sleep(chan, 1000);
02193 }
02194 }
02195
02196 ast_module_user_remove(u);
02197
02198
02199 return -1;
02200 }
02201
02202
02203
02204
02205
02206
02207
02208
02209
02210 static int login_exec(struct ast_channel *chan, void *data)
02211 {
02212 return __login_exec(chan, data, 0);
02213 }
02214
02215 static void callback_deprecated(void)
02216 {
02217 static int depwarning = 0;
02218
02219 if (!depwarning) {
02220 depwarning = 1;
02221
02222 ast_log(LOG_WARNING, "AgentCallbackLogin is deprecated and will be removed in a future release.\n");
02223 ast_log(LOG_WARNING, "See doc/queues-with-callback-members.txt for an example of how to achieve\n");
02224 ast_log(LOG_WARNING, "the same functionality using only dialplan logic.\n");
02225 }
02226 }
02227
02228
02229
02230
02231
02232
02233
02234
02235
02236 static int callback_exec(struct ast_channel *chan, void *data)
02237 {
02238 callback_deprecated();
02239
02240 return __login_exec(chan, data, 1);
02241 }
02242
02243
02244
02245
02246
02247
02248
02249
02250
02251 static int action_agent_callback_login(struct mansession *s, const struct message *m)
02252 {
02253 const char *agent = astman_get_header(m, "Agent");
02254 const char *exten = astman_get_header(m, "Exten");
02255 const char *context = astman_get_header(m, "Context");
02256 const char *wrapuptime_s = astman_get_header(m, "WrapupTime");
02257 const char *ackcall_s = astman_get_header(m, "AckCall");
02258 struct agent_pvt *p;
02259 int login_state = 0;
02260
02261 callback_deprecated();
02262
02263 if (ast_strlen_zero(agent)) {
02264 astman_send_error(s, m, "No agent specified");
02265 return 0;
02266 }
02267
02268 if (ast_strlen_zero(exten)) {
02269 astman_send_error(s, m, "No extension specified");
02270 return 0;
02271 }
02272
02273 AST_LIST_LOCK(&agents);
02274 AST_LIST_TRAVERSE(&agents, p, list) {
02275 if (strcmp(p->agent, agent) || p->pending)
02276 continue;
02277 if (p->chan) {
02278 login_state = 2;
02279 break;
02280 }
02281 ast_mutex_lock(&p->lock);
02282 login_state = 1;
02283
02284 if (ast_strlen_zero(context))
02285 ast_copy_string(p->loginchan, exten, sizeof(p->loginchan));
02286 else
02287 snprintf(p->loginchan, sizeof(p->loginchan), "%s@%s", exten, context);
02288
02289 if (!ast_strlen_zero(wrapuptime_s)) {
02290 p->wrapuptime = atoi(wrapuptime_s);
02291 if (p->wrapuptime < 0)
02292 p->wrapuptime = 0;
02293 }
02294
02295 if (ast_true(ackcall_s))
02296 p->ackcall = 1;
02297 else
02298 p->ackcall = 0;
02299
02300 if (p->loginstart == 0)
02301 time(&p->loginstart);
02302 manager_event(EVENT_FLAG_AGENT, "Agentcallbacklogin",
02303 "Agent: %s\r\n"
02304 "Loginchan: %s\r\n",
02305 p->agent, p->loginchan);
02306 ast_queue_log("NONE", "NONE", agent, "AGENTCALLBACKLOGIN", "%s", p->loginchan);
02307 if (option_verbose > 1)
02308 ast_verbose(VERBOSE_PREFIX_2 "Callback Agent '%s' logged in on %s\n", p->agent, p->loginchan);
02309 ast_device_state_changed("Agent/%s", p->agent);
02310 ast_mutex_unlock(&p->lock);
02311 if (persistent_agents)
02312 dump_agents();
02313 }
02314 AST_LIST_UNLOCK(&agents);
02315
02316 if (login_state == 1)
02317 astman_send_ack(s, m, "Agent logged in");
02318 else if (login_state == 0)
02319 astman_send_error(s, m, "No such agent");
02320 else if (login_state == 2)
02321 astman_send_error(s, m, "Agent already logged in");
02322
02323 return 0;
02324 }
02325
02326
02327
02328
02329
02330
02331
02332
02333
02334 static int agentmonitoroutgoing_exec(struct ast_channel *chan, void *data)
02335 {
02336 int exitifnoagentid = 0;
02337 int nowarnings = 0;
02338 int changeoutgoing = 0;
02339 int res = 0;
02340 char agent[AST_MAX_AGENT];
02341
02342 if (data) {
02343 if (strchr(data, 'd'))
02344 exitifnoagentid = 1;
02345 if (strchr(data, 'n'))
02346 nowarnings = 1;
02347 if (strchr(data, 'c'))
02348 changeoutgoing = 1;
02349 }
02350 if (chan->cid.cid_num) {
02351 const char *tmp;
02352 char agentvar[AST_MAX_BUF];
02353 snprintf(agentvar, sizeof(agentvar), "%s_%s", GETAGENTBYCALLERID, chan->cid.cid_num);
02354 if ((tmp = pbx_builtin_getvar_helper(NULL, agentvar))) {
02355 struct agent_pvt *p;
02356 ast_copy_string(agent, tmp, sizeof(agent));
02357 AST_LIST_LOCK(&agents);
02358 AST_LIST_TRAVERSE(&agents, p, list) {
02359 if (!strcasecmp(p->agent, tmp)) {
02360 if (changeoutgoing) snprintf(chan->cdr->channel, sizeof(chan->cdr->channel), "Agent/%s", p->agent);
02361 __agent_start_monitoring(chan, p, 1);
02362 break;
02363 }
02364 }
02365 AST_LIST_UNLOCK(&agents);
02366
02367 } else {
02368 res = -1;
02369 if (!nowarnings)
02370 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);
02371 }
02372 } else {
02373 res = -1;
02374 if (!nowarnings)
02375 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");
02376 }
02377
02378
02379 if (res) {
02380 if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 101, chan->cid.cid_num)) {
02381 chan->priority+=100;
02382 if (option_verbose > 2)
02383 ast_verbose(VERBOSE_PREFIX_3 "Going to %d priority because there is no callerid or the agentid cannot be found.\n",chan->priority);
02384 } else if (exitifnoagentid)
02385 return res;
02386 }
02387 return 0;
02388 }
02389
02390
02391
02392
02393 static void dump_agents(void)
02394 {
02395 struct agent_pvt *cur_agent = NULL;
02396 char buf[256];
02397
02398 AST_LIST_TRAVERSE(&agents, cur_agent, list) {
02399 if (cur_agent->chan)
02400 continue;
02401
02402 if (!ast_strlen_zero(cur_agent->loginchan)) {
02403 snprintf(buf, sizeof(buf), "%s;%s", cur_agent->loginchan, cur_agent->logincallerid);
02404 if (ast_db_put(pa_family, cur_agent->agent, buf))
02405 ast_log(LOG_WARNING, "failed to create persistent entry in ASTdb for %s!\n", buf);
02406 else if (option_debug)
02407 ast_log(LOG_DEBUG, "Saved Agent: %s on %s\n", cur_agent->agent, cur_agent->loginchan);
02408 } else {
02409
02410 ast_db_del(pa_family, cur_agent->agent);
02411 }
02412 }
02413 }
02414
02415
02416
02417
02418 static void reload_agents(void)
02419 {
02420 char *agent_num;
02421 struct ast_db_entry *db_tree;
02422 struct ast_db_entry *entry;
02423 struct agent_pvt *cur_agent;
02424 char agent_data[256];
02425 char *parse;
02426 char *agent_chan;
02427 char *agent_callerid;
02428
02429 db_tree = ast_db_gettree(pa_family, NULL);
02430
02431 AST_LIST_LOCK(&agents);
02432 for (entry = db_tree; entry; entry = entry->next) {
02433 agent_num = entry->key + strlen(pa_family) + 2;
02434 AST_LIST_TRAVERSE(&agents, cur_agent, list) {
02435 ast_mutex_lock(&cur_agent->lock);
02436 if (strcmp(agent_num, cur_agent->agent) == 0)
02437 break;
02438 ast_mutex_unlock(&cur_agent->lock);
02439 }
02440 if (!cur_agent) {
02441 ast_db_del(pa_family, agent_num);
02442 continue;
02443 } else
02444 ast_mutex_unlock(&cur_agent->lock);
02445 if (!ast_db_get(pa_family, agent_num, agent_data, sizeof(agent_data)-1)) {
02446 if (option_debug)
02447 ast_log(LOG_DEBUG, "Reload Agent from AstDB: %s on %s\n", cur_agent->agent, agent_data);
02448 parse = agent_data;
02449 agent_chan = strsep(&parse, ";");
02450 agent_callerid = strsep(&parse, ";");
02451 ast_copy_string(cur_agent->loginchan, agent_chan, sizeof(cur_agent->loginchan));
02452 if (agent_callerid) {
02453 ast_copy_string(cur_agent->logincallerid, agent_callerid, sizeof(cur_agent->logincallerid));
02454 set_agentbycallerid(cur_agent->logincallerid, cur_agent->agent);
02455 } else
02456 cur_agent->logincallerid[0] = '\0';
02457 if (cur_agent->loginstart == 0)
02458 time(&cur_agent->loginstart);
02459 ast_device_state_changed("Agent/%s", cur_agent->agent);
02460 }
02461 }
02462 AST_LIST_UNLOCK(&agents);
02463 if (db_tree) {
02464 ast_log(LOG_NOTICE, "Agents successfully reloaded from database.\n");
02465 ast_db_freetree(db_tree);
02466 }
02467 }
02468
02469
02470 static int agent_devicestate(void *data)
02471 {
02472 struct agent_pvt *p;
02473 char *s;
02474 ast_group_t groupmatch;
02475 int groupoff;
02476 int waitforagent=0;
02477 int res = AST_DEVICE_INVALID;
02478
02479 s = data;
02480 if ((s[0] == '@') && (sscanf(s + 1, "%d", &groupoff) == 1))
02481 groupmatch = (1 << groupoff);
02482 else if ((s[0] == ':') && (sscanf(s + 1, "%d", &groupoff) == 1)) {
02483 groupmatch = (1 << groupoff);
02484 waitforagent = 1;
02485 } else
02486 groupmatch = 0;
02487
02488
02489 AST_LIST_LOCK(&agents);
02490 AST_LIST_TRAVERSE(&agents, p, list) {
02491 ast_mutex_lock(&p->lock);
02492 if (!p->pending && ((groupmatch && (p->group & groupmatch)) || !strcmp(data, p->agent))) {
02493 if (p->owner) {
02494 if (res != AST_DEVICE_INUSE)
02495 res = AST_DEVICE_BUSY;
02496 } else {
02497 if (res == AST_DEVICE_BUSY)
02498 res = AST_DEVICE_INUSE;
02499 if (p->chan || !ast_strlen_zero(p->loginchan)) {
02500 if (res == AST_DEVICE_INVALID)
02501 res = AST_DEVICE_UNKNOWN;
02502 } else if (res == AST_DEVICE_INVALID)
02503 res = AST_DEVICE_UNAVAILABLE;
02504 }
02505 if (!strcmp(data, p->agent)) {
02506 ast_mutex_unlock(&p->lock);
02507 break;
02508 }
02509 }
02510 ast_mutex_unlock(&p->lock);
02511 }
02512 AST_LIST_UNLOCK(&agents);
02513 return res;
02514 }
02515
02516 static struct agent_pvt *find_agent(char *agentid)
02517 {
02518 struct agent_pvt *cur;
02519
02520 AST_LIST_TRAVERSE(&agents, cur, list) {
02521 if (!strcmp(cur->agent, agentid))
02522 break;
02523 }
02524
02525 return cur;
02526 }
02527
02528 static int function_agent(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
02529 {
02530 char *parse;
02531 AST_DECLARE_APP_ARGS(args,
02532 AST_APP_ARG(agentid);
02533 AST_APP_ARG(item);
02534 );
02535 char *tmp;
02536 struct agent_pvt *agent;
02537
02538 buf[0] = '\0';
02539
02540 if (ast_strlen_zero(data)) {
02541 ast_log(LOG_WARNING, "The AGENT function requires an argument - agentid!\n");
02542 return -1;
02543 }
02544
02545 parse = ast_strdupa(data);
02546
02547 AST_NONSTANDARD_APP_ARGS(args, parse, ':');
02548 if (!args.item)
02549 args.item = "status";
02550
02551 if (!(agent = find_agent(args.agentid))) {
02552 ast_log(LOG_WARNING, "Agent '%s' not found!\n", args.agentid);
02553 return -1;
02554 }
02555
02556 if (!strcasecmp(args.item, "status")) {
02557 char *status = "LOGGEDOUT";
02558 if (agent->chan || !ast_strlen_zero(agent->loginchan))
02559 status = "LOGGEDIN";
02560 ast_copy_string(buf, status, len);
02561 } else if (!strcasecmp(args.item, "password"))
02562 ast_copy_string(buf, agent->password, len);
02563 else if (!strcasecmp(args.item, "name"))
02564 ast_copy_string(buf, agent->name, len);
02565 else if (!strcasecmp(args.item, "mohclass"))
02566 ast_copy_string(buf, agent->moh, len);
02567 else if (!strcasecmp(args.item, "channel")) {
02568 if (agent->chan) {
02569 ast_copy_string(buf, agent->chan->name, len);
02570 tmp = strrchr(buf, '-');
02571 if (tmp)
02572 *tmp = '\0';
02573 }
02574 } else if (!strcasecmp(args.item, "exten"))
02575 ast_copy_string(buf, agent->loginchan, len);
02576
02577 return 0;
02578 }
02579
02580 struct ast_custom_function agent_function = {
02581 .name = "AGENT",
02582 .synopsis = "Gets information about an Agent",
02583 .syntax = "AGENT(<agentid>[:item])",
02584 .read = function_agent,
02585 .desc = "The valid items to retrieve are:\n"
02586 "- status (default) The status of the agent\n"
02587 " LOGGEDIN | LOGGEDOUT\n"
02588 "- password The password of the agent\n"
02589 "- name The name of the agent\n"
02590 "- mohclass MusicOnHold class\n"
02591 "- exten The callback extension for the Agent (AgentCallbackLogin)\n"
02592 "- channel The name of the active channel for the Agent (AgentLogin)\n"
02593 };
02594
02595
02596
02597
02598
02599
02600
02601
02602
02603 static int load_module(void)
02604 {
02605
02606 if (ast_channel_register(&agent_tech)) {
02607 ast_log(LOG_ERROR, "Unable to register channel class 'Agent'\n");
02608 return -1;
02609 }
02610
02611 if (!read_agent_config())
02612 return AST_MODULE_LOAD_DECLINE;
02613 if (persistent_agents)
02614 reload_agents();
02615
02616 ast_register_application(app, login_exec, synopsis, descrip);
02617 ast_register_application(app2, callback_exec, synopsis2, descrip2);
02618 ast_register_application(app3, agentmonitoroutgoing_exec, synopsis3, descrip3);
02619
02620
02621 ast_manager_register2("Agents", EVENT_FLAG_AGENT, action_agents, "Lists agents and their status", mandescr_agents);
02622 ast_manager_register2("AgentLogoff", EVENT_FLAG_AGENT, action_agent_logoff, "Sets an agent as no longer logged in", mandescr_agent_logoff);
02623 ast_manager_register2("AgentCallbackLogin", EVENT_FLAG_AGENT, action_agent_callback_login, "Sets an agent as logged in by callback", mandescr_agent_callback_login);
02624
02625
02626 ast_cli_register_multiple(cli_agents, sizeof(cli_agents) / sizeof(struct ast_cli_entry));
02627
02628
02629 ast_custom_function_register(&agent_function);
02630
02631 return 0;
02632 }
02633
02634 static int reload(void)
02635 {
02636 read_agent_config();
02637 if (persistent_agents)
02638 reload_agents();
02639 return 0;
02640 }
02641
02642 static int unload_module(void)
02643 {
02644 struct agent_pvt *p;
02645
02646 ast_channel_unregister(&agent_tech);
02647
02648 ast_custom_function_unregister(&agent_function);
02649
02650 ast_cli_unregister_multiple(cli_agents, sizeof(cli_agents) / sizeof(struct ast_cli_entry));
02651
02652 ast_unregister_application(app);
02653 ast_unregister_application(app2);
02654 ast_unregister_application(app3);
02655
02656 ast_manager_unregister("Agents");
02657 ast_manager_unregister("AgentLogoff");
02658 ast_manager_unregister("AgentCallbackLogin");
02659
02660 AST_LIST_LOCK(&agents);
02661
02662 while ((p = AST_LIST_REMOVE_HEAD(&agents, list))) {
02663 if (p->owner)
02664 ast_softhangup(p->owner, AST_SOFTHANGUP_APPUNLOAD);
02665 free(p);
02666 }
02667 AST_LIST_UNLOCK(&agents);
02668 AST_LIST_HEAD_DESTROY(&agents);
02669 return 0;
02670 }
02671
02672 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Agent Proxy Channel",
02673 .load = load_module,
02674 .unload = unload_module,
02675 .reload = reload,
02676 );