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