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