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