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