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