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: 189206 $")
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 agent_logoff(p->agent, 0);
00509 }
00510 }
00511 switch (f->frametype) {
00512 case AST_FRAME_CONTROL:
00513 if (f->subclass == AST_CONTROL_ANSWER) {
00514 if (p->ackcall) {
00515 ast_verb(3, "%s answered, waiting for '%c' to acknowledge\n", p->chan->name, p->acceptdtmf);
00516
00517 ast_frfree(f);
00518 f = &ast_null_frame;
00519 } else {
00520 p->acknowledged = 1;
00521
00522
00523 ast_frfree(f);
00524 f = &answer_frame;
00525 }
00526 }
00527 break;
00528 case AST_FRAME_DTMF_BEGIN:
00529
00530 if((!p->acknowledged && f->subclass == p->acceptdtmf) || (f->subclass == p->enddtmf && endcall)){
00531 ast_frfree(f);
00532 f = &ast_null_frame;
00533 }
00534 break;
00535 case AST_FRAME_DTMF_END:
00536 if (!p->acknowledged && (f->subclass == p->acceptdtmf)) {
00537 ast_verb(3, "%s acknowledged\n", p->chan->name);
00538 p->acknowledged = 1;
00539 ast_frfree(f);
00540 f = &answer_frame;
00541 } else if (f->subclass == p->enddtmf && endcall) {
00542
00543 ast_frfree(f);
00544 f = NULL;
00545 }
00546 break;
00547 case AST_FRAME_VOICE:
00548 case AST_FRAME_VIDEO:
00549
00550 if (!p->acknowledged) {
00551 ast_frfree(f);
00552 f = &ast_null_frame;
00553 }
00554 default:
00555
00556 break;
00557 }
00558 }
00559
00560 CLEANUP(ast,p);
00561 if (p->chan && !p->chan->_bridge) {
00562 if (strcasecmp(p->chan->tech->type, "Local")) {
00563 p->chan->_bridge = ast;
00564 if (p->chan)
00565 ast_debug(1, "Bridge on '%s' being set to '%s' (3)\n", p->chan->name, p->chan->_bridge->name);
00566 }
00567 }
00568 ast_mutex_unlock(&p->lock);
00569 if (recordagentcalls && f == &answer_frame)
00570 agent_start_monitoring(ast,0);
00571 return f;
00572 }
00573
00574 static int agent_sendhtml(struct ast_channel *ast, int subclass, const char *data, int datalen)
00575 {
00576 struct agent_pvt *p = ast->tech_pvt;
00577 int res = -1;
00578 ast_mutex_lock(&p->lock);
00579 if (p->chan)
00580 res = ast_channel_sendhtml(p->chan, subclass, data, datalen);
00581 ast_mutex_unlock(&p->lock);
00582 return res;
00583 }
00584
00585 static int agent_sendtext(struct ast_channel *ast, const char *text)
00586 {
00587 struct agent_pvt *p = ast->tech_pvt;
00588 int res = -1;
00589 ast_mutex_lock(&p->lock);
00590 if (p->chan)
00591 res = ast_sendtext(p->chan, text);
00592 ast_mutex_unlock(&p->lock);
00593 return res;
00594 }
00595
00596 static int agent_write(struct ast_channel *ast, struct ast_frame *f)
00597 {
00598 struct agent_pvt *p = ast->tech_pvt;
00599 int res = -1;
00600 CHECK_FORMATS(ast, p);
00601 ast_mutex_lock(&p->lock);
00602 if (!p->chan)
00603 res = 0;
00604 else {
00605 if ((f->frametype != AST_FRAME_VOICE) ||
00606 (f->frametype != AST_FRAME_VIDEO) ||
00607 (f->subclass == p->chan->writeformat)) {
00608 res = ast_write(p->chan, f);
00609 } else {
00610 ast_debug(1, "Dropping one incompatible %s frame on '%s' to '%s'\n",
00611 f->frametype == AST_FRAME_VOICE ? "audio" : "video",
00612 ast->name, p->chan->name);
00613 res = 0;
00614 }
00615 }
00616 CLEANUP(ast, p);
00617 ast_mutex_unlock(&p->lock);
00618 return res;
00619 }
00620
00621 static int agent_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
00622 {
00623 struct agent_pvt *p = newchan->tech_pvt;
00624 ast_mutex_lock(&p->lock);
00625 if (p->owner != oldchan) {
00626 ast_log(LOG_WARNING, "old channel wasn't %p but was %p\n", oldchan, p->owner);
00627 ast_mutex_unlock(&p->lock);
00628 return -1;
00629 }
00630 p->owner = newchan;
00631 ast_mutex_unlock(&p->lock);
00632 return 0;
00633 }
00634
00635 static int agent_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen)
00636 {
00637 struct agent_pvt *p = ast->tech_pvt;
00638 int res = -1;
00639 ast_mutex_lock(&p->lock);
00640 if (p->chan && !ast_check_hangup(p->chan)) {
00641 while (ast_channel_trylock(p->chan)) {
00642 ast_channel_unlock(ast);
00643 usleep(1);
00644 ast_channel_lock(ast);
00645 }
00646 res = p->chan->tech->indicate ? p->chan->tech->indicate(p->chan, condition, data, datalen) : -1;
00647 ast_channel_unlock(p->chan);
00648 } else
00649 res = 0;
00650 ast_mutex_unlock(&p->lock);
00651 return res;
00652 }
00653
00654 static int agent_digit_begin(struct ast_channel *ast, char digit)
00655 {
00656 struct agent_pvt *p = ast->tech_pvt;
00657 ast_mutex_lock(&p->lock);
00658 if (p->chan) {
00659 ast_senddigit_begin(p->chan, digit);
00660 }
00661 ast_mutex_unlock(&p->lock);
00662 return 0;
00663 }
00664
00665 static int agent_digit_end(struct ast_channel *ast, char digit, unsigned int duration)
00666 {
00667 struct agent_pvt *p = ast->tech_pvt;
00668 ast_mutex_lock(&p->lock);
00669 if (p->chan) {
00670 ast_senddigit_end(p->chan, digit, duration);
00671 }
00672 ast_mutex_unlock(&p->lock);
00673 return 0;
00674 }
00675
00676 static int agent_call(struct ast_channel *ast, char *dest, int timeout)
00677 {
00678 struct agent_pvt *p = ast->tech_pvt;
00679 int res = -1;
00680 int newstate=0;
00681 ast_mutex_lock(&p->lock);
00682 p->acknowledged = 0;
00683 if (!p->chan) {
00684 if (p->pending) {
00685 ast_debug(1, "Pretending to dial on pending agent\n");
00686 newstate = AST_STATE_DIALING;
00687 res = 0;
00688 } else {
00689 ast_log(LOG_NOTICE, "Whoa, they hung up between alloc and call... what are the odds of that?\n");
00690 res = -1;
00691 }
00692 ast_mutex_unlock(&p->lock);
00693 if (newstate)
00694 ast_setstate(ast, newstate);
00695 return res;
00696 } else if (!ast_strlen_zero(p->loginchan)) {
00697 time(&p->start);
00698
00699 ast_verb(3, "outgoing agentcall, to agent '%s', on '%s'\n", p->agent, p->chan->name);
00700 ast_set_callerid(p->chan,
00701 ast->cid.cid_num, ast->cid.cid_name, NULL);
00702 ast_channel_inherit_variables(ast, p->chan);
00703 res = ast_call(p->chan, p->loginchan, 0);
00704 CLEANUP(ast,p);
00705 ast_mutex_unlock(&p->lock);
00706 return res;
00707 }
00708 ast_verb(3, "agent_call, call to agent '%s' call on '%s'\n", p->agent, p->chan->name);
00709 ast_debug(3, "Playing beep, lang '%s'\n", p->chan->language);
00710 res = ast_streamfile(p->chan, beep, p->chan->language);
00711 ast_debug(3, "Played beep, result '%d'\n", res);
00712 if (!res) {
00713 res = ast_waitstream(p->chan, "");
00714 ast_debug(3, "Waited for stream, result '%d'\n", res);
00715 }
00716 if (!res) {
00717 res = ast_set_read_format(p->chan, ast_best_codec(p->chan->nativeformats));
00718 ast_debug(3, "Set read format, result '%d'\n", res);
00719 if (res)
00720 ast_log(LOG_WARNING, "Unable to set read format to %s\n", ast_getformatname(ast_best_codec(p->chan->nativeformats)));
00721 } else {
00722
00723 p->chan = NULL;
00724 ast_devstate_changed(AST_DEVICE_UNAVAILABLE, "Agent/%s", p->agent);
00725 }
00726
00727 if (!res) {
00728 res = ast_set_write_format(p->chan, ast_best_codec(p->chan->nativeformats));
00729 ast_debug(3, "Set write format, result '%d'\n", res);
00730 if (res)
00731 ast_log(LOG_WARNING, "Unable to set write format to %s\n", ast_getformatname(ast_best_codec(p->chan->nativeformats)));
00732 }
00733 if(!res) {
00734
00735 if (p->ackcall > 1)
00736 newstate = AST_STATE_RINGING;
00737 else {
00738 newstate = AST_STATE_UP;
00739 if (recordagentcalls)
00740 agent_start_monitoring(ast, 0);
00741 p->acknowledged = 1;
00742 }
00743 res = 0;
00744 }
00745 CLEANUP(ast, p);
00746 ast_mutex_unlock(&p->lock);
00747 if (newstate)
00748 ast_setstate(ast, newstate);
00749 return res;
00750 }
00751
00752
00753 static void set_agentbycallerid(const char *callerid, const char *agent)
00754 {
00755 char buf[AST_MAX_BUF];
00756
00757
00758 if (ast_strlen_zero(callerid))
00759 return;
00760
00761 snprintf(buf, sizeof(buf), "%s_%s", GETAGENTBYCALLERID, callerid);
00762 pbx_builtin_setvar_helper(NULL, buf, agent);
00763 }
00764
00765
00766 struct ast_channel* agent_get_base_channel(struct ast_channel *chan)
00767 {
00768 struct agent_pvt *p = NULL;
00769 struct ast_channel *base = chan;
00770
00771
00772 if (!chan || !chan->tech_pvt) {
00773 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);
00774 return NULL;
00775 }
00776 p = chan->tech_pvt;
00777 if (p->chan)
00778 base = p->chan;
00779 return base;
00780 }
00781
00782 int agent_set_base_channel(struct ast_channel *chan, struct ast_channel *base)
00783 {
00784 struct agent_pvt *p = NULL;
00785
00786 if (!chan || !base) {
00787 ast_log(LOG_ERROR, "whoa, you need a channel (0x%ld) and a base channel (0x%ld) for setting.\n", (long)chan, (long)base);
00788 return -1;
00789 }
00790 p = chan->tech_pvt;
00791 if (!p) {
00792 ast_log(LOG_ERROR, "whoa, channel %s is missing his tech_pvt structure!!.\n", chan->name);
00793 return -1;
00794 }
00795 p->chan = base;
00796 return 0;
00797 }
00798
00799 static int agent_hangup(struct ast_channel *ast)
00800 {
00801 struct agent_pvt *p = ast->tech_pvt;
00802 int howlong = 0;
00803 const char *status;
00804 ast_mutex_lock(&p->lock);
00805 p->owner = NULL;
00806 ast->tech_pvt = NULL;
00807 p->app_sleep_cond = 1;
00808 p->acknowledged = 0;
00809
00810
00811
00812
00813
00814
00815
00816
00817 ast_debug(1, "Hangup called for state %s\n", ast_state2str(ast->_state));
00818 if (p->start && (ast->_state != AST_STATE_UP)) {
00819 howlong = time(NULL) - p->start;
00820 p->start = 0;
00821 } else if (ast->_state == AST_STATE_RESERVED)
00822 howlong = 0;
00823 else
00824 p->start = 0;
00825 if (p->chan) {
00826 p->chan->_bridge = NULL;
00827
00828 if (!ast_strlen_zero(p->loginchan)) {
00829
00830 if (p->wrapuptime)
00831 p->lastdisc = ast_tvadd(ast_tvnow(), ast_samp2tv(p->wrapuptime, 1000));
00832 else
00833 p->lastdisc = ast_tv(0,0);
00834 if (p->chan) {
00835 status = pbx_builtin_getvar_helper(p->chan, "CHANLOCALSTATUS");
00836 if (autologoffunavail && status && !strcasecmp(status, "CHANUNAVAIL")) {
00837 long logintime = time(NULL) - p->loginstart;
00838 p->loginstart = 0;
00839 ast_log(LOG_NOTICE, "Agent hangup: '%s' is not available now, auto logoff\n", p->name);
00840 agent_logoff_maintenance(p, p->loginchan, logintime, ast->uniqueid, "Chanunavail");
00841 }
00842
00843 ast_hangup(p->chan);
00844 p->chan = NULL;
00845 ast_devstate_changed(AST_DEVICE_UNAVAILABLE, "Agent/%s", p->agent);
00846 }
00847 ast_debug(1, "Hungup, howlong is %d, autologoff is %d\n", howlong, p->autologoff);
00848 if ((p->deferlogoff) || (howlong && p->autologoff && (howlong > p->autologoff))) {
00849 long logintime = time(NULL) - p->loginstart;
00850 p->loginstart = 0;
00851 if (!p->deferlogoff)
00852 ast_log(LOG_NOTICE, "Agent '%s' didn't answer/confirm within %d seconds (waited %d)\n", p->name, p->autologoff, howlong);
00853 p->deferlogoff = 0;
00854 agent_logoff_maintenance(p, p->loginchan, logintime, ast->uniqueid, "Autologoff");
00855 if (persistent_agents)
00856 dump_agents();
00857 }
00858 } else if (p->dead) {
00859 ast_channel_lock(p->chan);
00860 ast_softhangup(p->chan, AST_SOFTHANGUP_EXPLICIT);
00861 ast_channel_unlock(p->chan);
00862 } else if (p->loginstart) {
00863 ast_channel_lock(p->chan);
00864 ast_indicate_data(p->chan, AST_CONTROL_HOLD,
00865 S_OR(p->moh, NULL),
00866 !ast_strlen_zero(p->moh) ? strlen(p->moh) + 1 : 0);
00867 ast_channel_unlock(p->chan);
00868 }
00869 }
00870 ast_mutex_unlock(&p->lock);
00871
00872
00873 if (!p->loginstart) {
00874 p->loginchan[0] = '\0';
00875 p->logincallerid[0] = '\0';
00876 if (persistent_agents)
00877 dump_agents();
00878 } else {
00879 ast_devstate_changed(AST_DEVICE_NOT_INUSE, "Agent/%s", p->agent);
00880 }
00881
00882 if (p->pending) {
00883 AST_LIST_LOCK(&agents);
00884 AST_LIST_REMOVE(&agents, p, list);
00885 AST_LIST_UNLOCK(&agents);
00886 }
00887 if (p->abouttograb) {
00888
00889
00890 p->abouttograb = 0;
00891 } else if (p->dead) {
00892 ast_mutex_destroy(&p->lock);
00893 ast_mutex_destroy(&p->app_lock);
00894 ast_cond_destroy(&p->app_complete_cond);
00895 ast_free(p);
00896 } else {
00897 if (p->chan) {
00898
00899 ast_mutex_lock(&p->lock);
00900
00901 p->lastdisc = ast_tvadd(ast_tvnow(), ast_samp2tv(p->wrapuptime, 1000));
00902 ast_mutex_unlock(&p->lock);
00903 }
00904
00905 if (ast_strlen_zero(p->loginchan)) {
00906 p->app_lock_flag = 0;
00907 ast_cond_signal(&p->app_complete_cond);
00908 }
00909 }
00910 return 0;
00911 }
00912
00913 static int agent_cont_sleep( void *data )
00914 {
00915 struct agent_pvt *p;
00916 int res;
00917
00918 p = (struct agent_pvt *)data;
00919
00920 ast_mutex_lock(&p->lock);
00921 res = p->app_sleep_cond;
00922 if (p->lastdisc.tv_sec) {
00923 if (ast_tvdiff_ms(ast_tvnow(), p->lastdisc) > 0)
00924 res = 1;
00925 }
00926 ast_mutex_unlock(&p->lock);
00927
00928 if (!res)
00929 ast_debug(5, "agent_cont_sleep() returning %d\n", res );
00930
00931 return res;
00932 }
00933
00934 static int agent_ack_sleep(void *data)
00935 {
00936 struct agent_pvt *p;
00937 int res=0;
00938 int to = 1000;
00939 struct ast_frame *f;
00940
00941
00942
00943 p = (struct agent_pvt *) data;
00944 if (!p->chan)
00945 return -1;
00946
00947 for(;;) {
00948 to = ast_waitfor(p->chan, to);
00949 if (to < 0)
00950 return -1;
00951 if (!to)
00952 return 0;
00953 f = ast_read(p->chan);
00954 if (!f)
00955 return -1;
00956 if (f->frametype == AST_FRAME_DTMF)
00957 res = f->subclass;
00958 else
00959 res = 0;
00960 ast_frfree(f);
00961 ast_mutex_lock(&p->lock);
00962 if (!p->app_sleep_cond) {
00963 ast_mutex_unlock(&p->lock);
00964 return 0;
00965 } else if (res == p->acceptdtmf) {
00966 ast_mutex_unlock(&p->lock);
00967 return 1;
00968 }
00969 ast_mutex_unlock(&p->lock);
00970 res = 0;
00971 }
00972 return res;
00973 }
00974
00975 static struct ast_channel *agent_bridgedchannel(struct ast_channel *chan, struct ast_channel *bridge)
00976 {
00977 struct agent_pvt *p = bridge->tech_pvt;
00978 struct ast_channel *ret = NULL;
00979
00980 if (p) {
00981 if (chan == p->chan)
00982 ret = bridge->_bridge;
00983 else if (chan == bridge->_bridge)
00984 ret = p->chan;
00985 }
00986
00987 ast_debug(1, "Asked for bridged channel on '%s'/'%s', returning '%s'\n", chan->name, bridge->name, ret ? ret->name : "<none>");
00988 return ret;
00989 }
00990
00991
00992 static struct ast_channel *agent_new(struct agent_pvt *p, int state)
00993 {
00994 struct ast_channel *tmp;
00995 int alreadylocked;
00996 #if 0
00997 if (!p->chan) {
00998 ast_log(LOG_WARNING, "No channel? :(\n");
00999 return NULL;
01000 }
01001 #endif
01002 if (p->pending)
01003 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);
01004 else
01005 tmp = ast_channel_alloc(0, state, 0, 0, "", p->chan ? p->chan->exten:"", p->chan ? p->chan->context:"", 0, "Agent/%s", p->agent);
01006 if (!tmp) {
01007 ast_log(LOG_WARNING, "Unable to allocate agent channel structure\n");
01008 return NULL;
01009 }
01010
01011 tmp->tech = &agent_tech;
01012 if (p->chan) {
01013 tmp->nativeformats = p->chan->nativeformats;
01014 tmp->writeformat = p->chan->writeformat;
01015 tmp->rawwriteformat = p->chan->writeformat;
01016 tmp->readformat = p->chan->readformat;
01017 tmp->rawreadformat = p->chan->readformat;
01018 ast_string_field_set(tmp, language, p->chan->language);
01019 ast_copy_string(tmp->context, p->chan->context, sizeof(tmp->context));
01020 ast_copy_string(tmp->exten, p->chan->exten, sizeof(tmp->exten));
01021
01022 } else {
01023 tmp->nativeformats = AST_FORMAT_SLINEAR;
01024 tmp->writeformat = AST_FORMAT_SLINEAR;
01025 tmp->rawwriteformat = AST_FORMAT_SLINEAR;
01026 tmp->readformat = AST_FORMAT_SLINEAR;
01027 tmp->rawreadformat = AST_FORMAT_SLINEAR;
01028 }
01029
01030 tmp->tech_pvt = p;
01031 p->owner = tmp;
01032 tmp->priority = 1;
01033
01034
01035
01036
01037
01038
01039
01040 p->app_sleep_cond = 0;
01041
01042 alreadylocked = p->app_lock_flag;
01043 p->app_lock_flag = 1;
01044
01045 if(ast_strlen_zero(p->loginchan) && alreadylocked) {
01046 if (p->chan) {
01047 ast_queue_frame(p->chan, &ast_null_frame);
01048 ast_mutex_unlock(&p->lock);
01049 p->app_lock_flag = 1;
01050 ast_mutex_lock(&p->lock);
01051 } else {
01052 ast_log(LOG_WARNING, "Agent disconnected while we were connecting the call\n");
01053 p->owner = NULL;
01054 tmp->tech_pvt = NULL;
01055 p->app_sleep_cond = 1;
01056 ast_channel_free( tmp );
01057 ast_mutex_unlock(&p->lock);
01058 p->app_lock_flag = 0;
01059 ast_cond_signal(&p->app_complete_cond);
01060 return NULL;
01061 }
01062 } else if (!ast_strlen_zero(p->loginchan)) {
01063 if (p->chan)
01064 ast_queue_frame(p->chan, &ast_null_frame);
01065 if (!p->chan) {
01066 ast_log(LOG_WARNING, "Agent disconnected while we were connecting the call\n");
01067 p->owner = NULL;
01068 tmp->tech_pvt = NULL;
01069 p->app_sleep_cond = 1;
01070 ast_channel_free( tmp );
01071 ast_mutex_unlock(&p->lock);
01072 return NULL;
01073 }
01074 }
01075 if (p->chan)
01076 ast_indicate(p->chan, AST_CONTROL_UNHOLD);
01077 return tmp;
01078 }
01079
01080
01081
01082
01083
01084
01085
01086 static int read_agent_config(int reload)
01087 {
01088 struct ast_config *cfg;
01089 struct ast_config *ucfg;
01090 struct ast_variable *v;
01091 struct agent_pvt *p;
01092 const char *general_val;
01093 const char *catname;
01094 const char *hasagent;
01095 int genhasagent;
01096 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
01097
01098 group = 0;
01099 autologoff = 0;
01100 wrapuptime = 0;
01101 ackcall = 0;
01102 endcall = 1;
01103 cfg = ast_config_load(config, config_flags);
01104 if (!cfg) {
01105 ast_log(LOG_NOTICE, "No agent configuration found -- agent support disabled\n");
01106 return 0;
01107 } else if (cfg == CONFIG_STATUS_FILEUNCHANGED)
01108 return -1;
01109 AST_LIST_LOCK(&agents);
01110 AST_LIST_TRAVERSE(&agents, p, list) {
01111 p->dead = 1;
01112 }
01113 strcpy(moh, "default");
01114
01115 recordagentcalls = 0;
01116 strcpy(recordformat, "wav");
01117 strcpy(recordformatext, "wav");
01118 urlprefix[0] = '\0';
01119 savecallsin[0] = '\0';
01120
01121
01122 if ((general_val = ast_variable_retrieve(cfg, "general", "persistentagents")))
01123 persistent_agents = ast_true(general_val);
01124 multiplelogin = ast_true(ast_variable_retrieve(cfg, "general", "multiplelogin"));
01125
01126
01127 v = ast_variable_browse(cfg, "agents");
01128 while(v) {
01129
01130 if (!strcasecmp(v->name, "agent")) {
01131 add_agent(v->value, 0);
01132 } else if (!strcasecmp(v->name, "group")) {
01133 group = ast_get_group(v->value);
01134 } else if (!strcasecmp(v->name, "autologoff")) {
01135 autologoff = atoi(v->value);
01136 if (autologoff < 0)
01137 autologoff = 0;
01138 } else if (!strcasecmp(v->name, "ackcall")) {
01139 if (!strcasecmp(v->value, "always"))
01140 ackcall = 2;
01141 else if (ast_true(v->value))
01142 ackcall = 1;
01143 else
01144 ackcall = 0;
01145 } else if (!strcasecmp(v->name, "endcall")) {
01146 endcall = ast_true(v->value);
01147 } else if (!strcasecmp(v->name, "acceptdtmf")) {
01148 acceptdtmf = *(v->value);
01149 ast_log(LOG_NOTICE, "Set acceptdtmf to %c\n", acceptdtmf);
01150 } else if (!strcasecmp(v->name, "enddtmf")) {
01151 enddtmf = *(v->value);
01152 } else if (!strcasecmp(v->name, "wrapuptime")) {
01153 wrapuptime = atoi(v->value);
01154 if (wrapuptime < 0)
01155 wrapuptime = 0;
01156 } else if (!strcasecmp(v->name, "maxlogintries") && !ast_strlen_zero(v->value)) {
01157 maxlogintries = atoi(v->value);
01158 if (maxlogintries < 0)
01159 maxlogintries = 0;
01160 } else if (!strcasecmp(v->name, "goodbye") && !ast_strlen_zero(v->value)) {
01161 strcpy(agentgoodbye,v->value);
01162 } else if (!strcasecmp(v->name, "musiconhold")) {
01163 ast_copy_string(moh, v->value, sizeof(moh));
01164 } else if (!strcasecmp(v->name, "updatecdr")) {
01165 if (ast_true(v->value))
01166 updatecdr = 1;
01167 else
01168 updatecdr = 0;
01169 } else if (!strcasecmp(v->name, "autologoffunavail")) {
01170 if (ast_true(v->value))
01171 autologoffunavail = 1;
01172 else
01173 autologoffunavail = 0;
01174 } else if (!strcasecmp(v->name, "recordagentcalls")) {
01175 recordagentcalls = ast_true(v->value);
01176 } else if (!strcasecmp(v->name, "recordformat")) {
01177 ast_copy_string(recordformat, v->value, sizeof(recordformat));
01178 if (!strcasecmp(v->value, "wav49"))
01179 strcpy(recordformatext, "WAV");
01180 else
01181 ast_copy_string(recordformatext, v->value, sizeof(recordformatext));
01182 } else if (!strcasecmp(v->name, "urlprefix")) {
01183 ast_copy_string(urlprefix, v->value, sizeof(urlprefix));
01184 if (urlprefix[strlen(urlprefix) - 1] != '/')
01185 strncat(urlprefix, "/", sizeof(urlprefix) - strlen(urlprefix) - 1);
01186 } else if (!strcasecmp(v->name, "savecallsin")) {
01187 if (v->value[0] == '/')
01188 ast_copy_string(savecallsin, v->value, sizeof(savecallsin));
01189 else
01190 snprintf(savecallsin, sizeof(savecallsin) - 2, "/%s", v->value);
01191 if (savecallsin[strlen(savecallsin) - 1] != '/')
01192 strncat(savecallsin, "/", sizeof(savecallsin) - strlen(savecallsin) - 1);
01193 } else if (!strcasecmp(v->name, "custom_beep")) {
01194 ast_copy_string(beep, v->value, sizeof(beep));
01195 }
01196 v = v->next;
01197 }
01198 if ((ucfg = ast_config_load("users.conf", config_flags)) && ucfg != CONFIG_STATUS_FILEUNCHANGED) {
01199 genhasagent = ast_true(ast_variable_retrieve(ucfg, "general", "hasagent"));
01200 catname = ast_category_browse(ucfg, NULL);
01201 while(catname) {
01202 if (strcasecmp(catname, "general")) {
01203 hasagent = ast_variable_retrieve(ucfg, catname, "hasagent");
01204 if (ast_true(hasagent) || (!hasagent && genhasagent)) {
01205 char tmp[256];
01206 const char *fullname = ast_variable_retrieve(ucfg, catname, "fullname");
01207 const char *secret = ast_variable_retrieve(ucfg, catname, "secret");
01208 if (!fullname)
01209 fullname = "";
01210 if (!secret)
01211 secret = "";
01212 snprintf(tmp, sizeof(tmp), "%s,%s,%s", catname, secret,fullname);
01213 add_agent(tmp, 0);
01214 }
01215 }
01216 catname = ast_category_browse(ucfg, catname);
01217 }
01218 ast_config_destroy(ucfg);
01219 }
01220 AST_LIST_TRAVERSE_SAFE_BEGIN(&agents, p, list) {
01221 if (p->dead) {
01222 AST_LIST_REMOVE_CURRENT(list);
01223
01224 if (!p->owner) {
01225 if (!p->chan) {
01226 ast_mutex_destroy(&p->lock);
01227 ast_mutex_destroy(&p->app_lock);
01228 ast_cond_destroy(&p->app_complete_cond);
01229 ast_free(p);
01230 } else {
01231
01232 ast_softhangup(p->chan, AST_SOFTHANGUP_EXPLICIT);
01233 }
01234 }
01235 }
01236 }
01237 AST_LIST_TRAVERSE_SAFE_END;
01238 AST_LIST_UNLOCK(&agents);
01239 ast_config_destroy(cfg);
01240 return 1;
01241 }
01242
01243 static int check_availability(struct agent_pvt *newlyavailable, int needlock)
01244 {
01245 struct ast_channel *chan=NULL, *parent=NULL;
01246 struct agent_pvt *p;
01247 int res;
01248
01249 ast_debug(1, "Checking availability of '%s'\n", newlyavailable->agent);
01250 if (needlock)
01251 AST_LIST_LOCK(&agents);
01252 AST_LIST_TRAVERSE(&agents, p, list) {
01253 if (p == newlyavailable) {
01254 continue;
01255 }
01256 ast_mutex_lock(&p->lock);
01257 if (!p->abouttograb && p->pending && ((p->group && (newlyavailable->group & p->group)) || !strcmp(p->agent, newlyavailable->agent))) {
01258 ast_debug(1, "Call '%s' looks like a winner for agent '%s'\n", p->owner->name, newlyavailable->agent);
01259
01260 chan = agent_new(newlyavailable, AST_STATE_DOWN);
01261 parent = p->owner;
01262 p->abouttograb = 1;
01263 ast_mutex_unlock(&p->lock);
01264 break;
01265 }
01266 ast_mutex_unlock(&p->lock);
01267 }
01268 if (needlock)
01269 AST_LIST_UNLOCK(&agents);
01270 if (parent && chan) {
01271 if (newlyavailable->ackcall > 1) {
01272
01273 res = 0;
01274 } else {
01275 ast_debug(3, "Playing beep, lang '%s'\n", newlyavailable->chan->language);
01276 res = ast_streamfile(newlyavailable->chan, beep, newlyavailable->chan->language);
01277 ast_debug(3, "Played beep, result '%d'\n", res);
01278 if (!res) {
01279 res = ast_waitstream(newlyavailable->chan, "");
01280 ast_debug(1, "Waited for stream, result '%d'\n", res);
01281 }
01282 }
01283 if (!res) {
01284
01285 if (p->abouttograb) {
01286 newlyavailable->acknowledged = 1;
01287
01288 ast_setstate(parent, AST_STATE_UP);
01289 ast_setstate(chan, AST_STATE_UP);
01290 ast_copy_string(parent->context, chan->context, sizeof(parent->context));
01291
01292
01293 ast_set_flag(chan, AST_FLAG_ZOMBIE);
01294 ast_channel_masquerade(parent, chan);
01295 p->abouttograb = 0;
01296 } else {
01297 ast_debug(1, "Sneaky, parent disappeared in the mean time...\n");
01298 agent_cleanup(newlyavailable);
01299 }
01300 } else {
01301 ast_debug(1, "Ugh... Agent hung up at exactly the wrong time\n");
01302 agent_cleanup(newlyavailable);
01303 }
01304 }
01305 return 0;
01306 }
01307
01308 static int check_beep(struct agent_pvt *newlyavailable, int needlock)
01309 {
01310 struct agent_pvt *p;
01311 int res=0;
01312
01313 ast_debug(1, "Checking beep availability of '%s'\n", newlyavailable->agent);
01314 if (needlock)
01315 AST_LIST_LOCK(&agents);
01316 AST_LIST_TRAVERSE(&agents, p, list) {
01317 if (p == newlyavailable) {
01318 continue;
01319 }
01320 ast_mutex_lock(&p->lock);
01321 if (!p->abouttograb && p->pending && ((p->group && (newlyavailable->group & p->group)) || !strcmp(p->agent, newlyavailable->agent))) {
01322 ast_debug(1, "Call '%s' looks like a would-be winner for agent '%s'\n", p->owner->name, newlyavailable->agent);
01323 ast_mutex_unlock(&p->lock);
01324 break;
01325 }
01326 ast_mutex_unlock(&p->lock);
01327 }
01328 if (needlock)
01329 AST_LIST_UNLOCK(&agents);
01330 if (p) {
01331 ast_mutex_unlock(&newlyavailable->lock);
01332 ast_debug(3, "Playing beep, lang '%s'\n", newlyavailable->chan->language);
01333 res = ast_streamfile(newlyavailable->chan, beep, newlyavailable->chan->language);
01334 ast_debug(1, "Played beep, result '%d'\n", res);
01335 if (!res) {
01336 res = ast_waitstream(newlyavailable->chan, "");
01337 ast_debug(1, "Waited for stream, result '%d'\n", res);
01338 }
01339 ast_mutex_lock(&newlyavailable->lock);
01340 }
01341 return res;
01342 }
01343
01344
01345 static struct ast_channel *agent_request(const char *type, int format, void *data, int *cause)
01346 {
01347 struct agent_pvt *p;
01348 struct ast_channel *chan = NULL;
01349 char *s;
01350 ast_group_t groupmatch;
01351 int groupoff;
01352 int waitforagent=0;
01353 int hasagent = 0;
01354 struct timeval now;
01355
01356 s = data;
01357 if ((s[0] == '@') && (sscanf(s + 1, "%d", &groupoff) == 1)) {
01358 groupmatch = (1 << groupoff);
01359 } else if ((s[0] == ':') && (sscanf(s + 1, "%d", &groupoff) == 1)) {
01360 groupmatch = (1 << groupoff);
01361 waitforagent = 1;
01362 } else
01363 groupmatch = 0;
01364
01365
01366 AST_LIST_LOCK(&agents);
01367 AST_LIST_TRAVERSE(&agents, p, list) {
01368 ast_mutex_lock(&p->lock);
01369 if (!p->pending && ((groupmatch && (p->group & groupmatch)) || !strcmp(data, p->agent)) &&
01370 ast_strlen_zero(p->loginchan)) {
01371 if (p->chan)
01372 hasagent++;
01373 now = ast_tvnow();
01374 if (!p->lastdisc.tv_sec || (now.tv_sec >= p->lastdisc.tv_sec)) {
01375 p->lastdisc = ast_tv(0, 0);
01376
01377 if (!p->owner && p->chan) {
01378
01379 chan = agent_new(p, AST_STATE_DOWN);
01380 }
01381 if (chan) {
01382 ast_mutex_unlock(&p->lock);
01383 break;
01384 }
01385 }
01386 }
01387 ast_mutex_unlock(&p->lock);
01388 }
01389 if (!p) {
01390 AST_LIST_TRAVERSE(&agents, p, list) {
01391 ast_mutex_lock(&p->lock);
01392 if (!p->pending && ((groupmatch && (p->group & groupmatch)) || !strcmp(data, p->agent))) {
01393 if (p->chan || !ast_strlen_zero(p->loginchan))
01394 hasagent++;
01395 now = ast_tvnow();
01396 #if 0
01397 ast_log(LOG_NOTICE, "Time now: %ld, Time of lastdisc: %ld\n", now.tv_sec, p->lastdisc.tv_sec);
01398 #endif
01399 if (!p->lastdisc.tv_sec || (now.tv_sec >= p->lastdisc.tv_sec)) {
01400 p->lastdisc = ast_tv(0, 0);
01401
01402 if (!p->owner && p->chan) {
01403
01404 chan = agent_new(p, AST_STATE_DOWN);
01405 } else if (!p->owner && !ast_strlen_zero(p->loginchan)) {
01406
01407 p->chan = ast_request("Local", format, p->loginchan, cause);
01408 if (p->chan)
01409 chan = agent_new(p, AST_STATE_DOWN);
01410 }
01411 if (chan) {
01412 ast_mutex_unlock(&p->lock);
01413 break;
01414 }
01415 }
01416 }
01417 ast_mutex_unlock(&p->lock);
01418 }
01419 }
01420
01421 if (!chan && waitforagent) {
01422
01423
01424 if (hasagent) {
01425 ast_debug(1, "Creating place holder for '%s'\n", s);
01426 p = add_agent(data, 1);
01427 p->group = groupmatch;
01428 chan = agent_new(p, AST_STATE_DOWN);
01429 if (!chan)
01430 ast_log(LOG_WARNING, "Weird... Fix this to drop the unused pending agent\n");
01431 } else {
01432 ast_debug(1, "Not creating place holder for '%s' since nobody logged in\n", s);
01433 }
01434 }
01435 *cause = hasagent ? AST_CAUSE_BUSY : AST_CAUSE_UNREGISTERED;
01436 AST_LIST_UNLOCK(&agents);
01437 return chan;
01438 }
01439
01440 static force_inline int powerof(unsigned int d)
01441 {
01442 int x = ffs(d);
01443
01444 if (x)
01445 return x - 1;
01446
01447 return 0;
01448 }
01449
01450
01451
01452
01453
01454
01455
01456
01457
01458 static int action_agents(struct mansession *s, const struct message *m)
01459 {
01460 const char *id = astman_get_header(m,"ActionID");
01461 char idText[256] = "";
01462 char chanbuf[256];
01463 struct agent_pvt *p;
01464 char *username = NULL;
01465 char *loginChan = NULL;
01466 char *talkingto = NULL;
01467 char *talkingtoChan = NULL;
01468 char *status = NULL;
01469
01470 if (!ast_strlen_zero(id))
01471 snprintf(idText, sizeof(idText) ,"ActionID: %s\r\n", id);
01472 astman_send_ack(s, m, "Agents will follow");
01473 AST_LIST_LOCK(&agents);
01474 AST_LIST_TRAVERSE(&agents, p, list) {
01475 ast_mutex_lock(&p->lock);
01476
01477
01478
01479
01480
01481
01482
01483 username = S_OR(p->name, "None");
01484
01485
01486 status = "AGENT_UNKNOWN";
01487
01488 if (!ast_strlen_zero(p->loginchan) && !p->chan) {
01489 loginChan = p->loginchan;
01490 talkingto = "n/a";
01491 talkingtoChan = "n/a";
01492 status = "AGENT_IDLE";
01493 if (p->acknowledged) {
01494 snprintf(chanbuf, sizeof(chanbuf), " %s (Confirmed)", p->loginchan);
01495 loginChan = chanbuf;
01496 }
01497 } else if (p->chan) {
01498 loginChan = ast_strdupa(p->chan->name);
01499 if (p->owner && p->owner->_bridge) {
01500 talkingto = p->chan->cid.cid_num;
01501 if (ast_bridged_channel(p->owner))
01502 talkingtoChan = ast_strdupa(ast_bridged_channel(p->owner)->name);
01503 else
01504 talkingtoChan = "n/a";
01505 status = "AGENT_ONCALL";
01506 } else {
01507 talkingto = "n/a";
01508 talkingtoChan = "n/a";
01509 status = "AGENT_IDLE";
01510 }
01511 } else {
01512 loginChan = "n/a";
01513 talkingto = "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 "TalkingToChan: %s\r\n"
01526 "%s"
01527 "\r\n",
01528 p->agent, username, status, loginChan, (int)p->loginstart, talkingto, talkingtoChan, idText);
01529 ast_mutex_unlock(&p->lock);
01530 }
01531 AST_LIST_UNLOCK(&agents);
01532 astman_append(s, "Event: AgentsComplete\r\n"
01533 "%s"
01534 "\r\n",idText);
01535 return 0;
01536 }
01537
01538 static void agent_logoff_maintenance(struct agent_pvt *p, char *loginchan, long logintime, const char *uniqueid, char *logcommand)
01539 {
01540 char *tmp = NULL;
01541 char agent[AST_MAX_AGENT];
01542
01543 if (!ast_strlen_zero(logcommand))
01544 tmp = logcommand;
01545 else
01546 tmp = ast_strdupa("");
01547
01548 snprintf(agent, sizeof(agent), "Agent/%s", p->agent);
01549
01550 if (!ast_strlen_zero(uniqueid)) {
01551 manager_event(EVENT_FLAG_AGENT, "Agentcallbacklogoff",
01552 "Agent: %s\r\n"
01553 "Reason: %s\r\n"
01554 "Loginchan: %s\r\n"
01555 "Logintime: %ld\r\n"
01556 "Uniqueid: %s\r\n",
01557 p->agent, tmp, loginchan, logintime, uniqueid);
01558 } else {
01559 manager_event(EVENT_FLAG_AGENT, "Agentcallbacklogoff",
01560 "Agent: %s\r\n"
01561 "Reason: %s\r\n"
01562 "Loginchan: %s\r\n"
01563 "Logintime: %ld\r\n",
01564 p->agent, tmp, loginchan, logintime);
01565 }
01566
01567 ast_queue_log("NONE", ast_strlen_zero(uniqueid) ? "NONE" : uniqueid, agent, "AGENTCALLBACKLOGOFF", "%s|%ld|%s", loginchan, logintime, tmp);
01568 set_agentbycallerid(p->logincallerid, NULL);
01569 p->loginchan[0] ='\0';
01570 p->logincallerid[0] = '\0';
01571 ast_devstate_changed(AST_DEVICE_UNAVAILABLE, "Agent/%s", p->agent);
01572 if (persistent_agents)
01573 dump_agents();
01574
01575 }
01576
01577 static int agent_logoff(const char *agent, int soft)
01578 {
01579 struct agent_pvt *p;
01580 long logintime;
01581 int ret = -1;
01582
01583 AST_LIST_LOCK(&agents);
01584 AST_LIST_TRAVERSE(&agents, p, list) {
01585 if (!strcasecmp(p->agent, agent)) {
01586 ret = 0;
01587 if (p->owner || p->chan) {
01588 if (!soft) {
01589 ast_mutex_lock(&p->lock);
01590
01591 while (p->owner && ast_channel_trylock(p->owner)) {
01592 DEADLOCK_AVOIDANCE(&p->lock);
01593 }
01594 if (p->owner) {
01595 ast_softhangup(p->owner, AST_SOFTHANGUP_EXPLICIT);
01596 ast_channel_unlock(p->owner);
01597 }
01598
01599 while (p->chan && ast_channel_trylock(p->chan)) {
01600 DEADLOCK_AVOIDANCE(&p->lock);
01601 }
01602 if (p->chan) {
01603 ast_softhangup(p->chan, AST_SOFTHANGUP_EXPLICIT);
01604 ast_channel_unlock(p->chan);
01605 }
01606
01607 ast_mutex_unlock(&p->lock);
01608 } else
01609 p->deferlogoff = 1;
01610 } else {
01611 logintime = time(NULL) - p->loginstart;
01612 p->loginstart = 0;
01613 agent_logoff_maintenance(p, p->loginchan, logintime, NULL, "CommandLogoff");
01614 }
01615 break;
01616 }
01617 }
01618 AST_LIST_UNLOCK(&agents);
01619
01620 return ret;
01621 }
01622
01623 static char *agent_logoff_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01624 {
01625 int ret;
01626 char *agent;
01627
01628 switch (cmd) {
01629 case CLI_INIT:
01630 e->command = "agent logoff";
01631 e->usage =
01632 "Usage: agent logoff <channel> [soft]\n"
01633 " Sets an agent as no longer logged in.\n"
01634 " If 'soft' is specified, do not hangup existing calls.\n";
01635 return NULL;
01636 case CLI_GENERATE:
01637 return complete_agent_logoff_cmd(a->line, a->word, a->pos, a->n);
01638 }
01639
01640 if (a->argc < 3 || a->argc > 4)
01641 return CLI_SHOWUSAGE;
01642 if (a->argc == 4 && strcasecmp(a->argv[3], "soft"))
01643 return CLI_SHOWUSAGE;
01644
01645 agent = a->argv[2] + 6;
01646 ret = agent_logoff(agent, a->argc == 4);
01647 if (ret == 0)
01648 ast_cli(a->fd, "Logging out %s\n", agent);
01649
01650 return CLI_SUCCESS;
01651 }
01652
01653
01654
01655
01656
01657
01658
01659
01660
01661 static int action_agent_logoff(struct mansession *s, const struct message *m)
01662 {
01663 const char *agent = astman_get_header(m, "Agent");
01664 const char *soft_s = astman_get_header(m, "Soft");
01665 int soft;
01666 int ret;
01667
01668 if (ast_strlen_zero(agent)) {
01669 astman_send_error(s, m, "No agent specified");
01670 return 0;
01671 }
01672
01673 soft = ast_true(soft_s) ? 1 : 0;
01674 ret = agent_logoff(agent, soft);
01675 if (ret == 0)
01676 astman_send_ack(s, m, "Agent logged out");
01677 else
01678 astman_send_error(s, m, "No such agent");
01679
01680 return 0;
01681 }
01682
01683 static char *complete_agent_logoff_cmd(const char *line, const char *word, int pos, int state)
01684 {
01685 char *ret = NULL;
01686
01687 if (pos == 2) {
01688 struct agent_pvt *p;
01689 char name[AST_MAX_AGENT];
01690 int which = 0, len = strlen(word);
01691
01692 AST_LIST_LOCK(&agents);
01693 AST_LIST_TRAVERSE(&agents, p, list) {
01694 snprintf(name, sizeof(name), "Agent/%s", p->agent);
01695 if (!strncasecmp(word, name, len) && p->loginstart && ++which > state) {
01696 ret = ast_strdup(name);
01697 break;
01698 }
01699 }
01700 AST_LIST_UNLOCK(&agents);
01701 } else if (pos == 3 && state == 0)
01702 return ast_strdup("soft");
01703
01704 return ret;
01705 }
01706
01707
01708
01709
01710 static char *agents_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01711 {
01712 struct agent_pvt *p;
01713 char username[AST_MAX_BUF];
01714 char location[AST_MAX_BUF] = "";
01715 char talkingto[AST_MAX_BUF] = "";
01716 char music[AST_MAX_BUF];
01717 int count_agents = 0;
01718 int online_agents = 0;
01719 int offline_agents = 0;
01720
01721 switch (cmd) {
01722 case CLI_INIT:
01723 e->command = "agent show";
01724 e->usage =
01725 "Usage: agent show\n"
01726 " Provides summary information on agents.\n";
01727 return NULL;
01728 case CLI_GENERATE:
01729 return NULL;
01730 }
01731
01732 if (a->argc != 2)
01733 return CLI_SHOWUSAGE;
01734
01735 AST_LIST_LOCK(&agents);
01736 AST_LIST_TRAVERSE(&agents, p, list) {
01737 ast_mutex_lock(&p->lock);
01738 if (p->pending) {
01739 if (p->group)
01740 ast_cli(a->fd, "-- Pending call to group %d\n", powerof(p->group));
01741 else
01742 ast_cli(a->fd, "-- Pending call to agent %s\n", p->agent);
01743 } else {
01744 if (!ast_strlen_zero(p->name))
01745 snprintf(username, sizeof(username), "(%s) ", p->name);
01746 else
01747 username[0] = '\0';
01748 if (p->chan) {
01749 snprintf(location, sizeof(location), "logged in on %s", p->chan->name);
01750 if (p->owner && ast_bridged_channel(p->owner))
01751 snprintf(talkingto, sizeof(talkingto), " talking to %s", ast_bridged_channel(p->owner)->name);
01752 else
01753 strcpy(talkingto, " is idle");
01754 online_agents++;
01755 } else if (!ast_strlen_zero(p->loginchan)) {
01756 if (ast_tvdiff_ms(ast_tvnow(), p->lastdisc) > 0 || !(p->lastdisc.tv_sec))
01757 snprintf(location, sizeof(location) - 20, "available at '%s'", p->loginchan);
01758 else
01759 snprintf(location, sizeof(location) - 20, "wrapping up at '%s'", p->loginchan);
01760 talkingto[0] = '\0';
01761 online_agents++;
01762 if (p->acknowledged)
01763 strncat(location, " (Confirmed)", sizeof(location) - strlen(location) - 1);
01764 } else {
01765 strcpy(location, "not logged in");
01766 talkingto[0] = '\0';
01767 offline_agents++;
01768 }
01769 if (!ast_strlen_zero(p->moh))
01770 snprintf(music, sizeof(music), " (musiconhold is '%s')", p->moh);
01771 ast_cli(a->fd, "%-12.12s %s%s%s%s\n", p->agent,
01772 username, location, talkingto, music);
01773 count_agents++;
01774 }
01775 ast_mutex_unlock(&p->lock);
01776 }
01777 AST_LIST_UNLOCK(&agents);
01778 if ( !count_agents )
01779 ast_cli(a->fd, "No Agents are configured in %s\n",config);
01780 else
01781 ast_cli(a->fd, "%d agents configured [%d online , %d offline]\n",count_agents, online_agents, offline_agents);
01782 ast_cli(a->fd, "\n");
01783
01784 return CLI_SUCCESS;
01785 }
01786
01787
01788 static char *agents_show_online(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01789 {
01790 struct agent_pvt *p;
01791 char username[AST_MAX_BUF];
01792 char location[AST_MAX_BUF] = "";
01793 char talkingto[AST_MAX_BUF] = "";
01794 char music[AST_MAX_BUF];
01795 int count_agents = 0;
01796 int online_agents = 0;
01797 int agent_status = 0;
01798
01799 switch (cmd) {
01800 case CLI_INIT:
01801 e->command = "agent show online";
01802 e->usage =
01803 "Usage: agent show online\n"
01804 " Provides a list of all online agents.\n";
01805 return NULL;
01806 case CLI_GENERATE:
01807 return NULL;
01808 }
01809
01810 if (a->argc != 3)
01811 return CLI_SHOWUSAGE;
01812
01813 AST_LIST_LOCK(&agents);
01814 AST_LIST_TRAVERSE(&agents, p, list) {
01815 agent_status = 0;
01816 ast_mutex_lock(&p->lock);
01817 if (!ast_strlen_zero(p->name))
01818 snprintf(username, sizeof(username), "(%s) ", p->name);
01819 else
01820 username[0] = '\0';
01821 if (p->chan) {
01822 snprintf(location, sizeof(location), "logged in on %s", p->chan->name);
01823 if (p->owner && ast_bridged_channel(p->owner))
01824 snprintf(talkingto, sizeof(talkingto), " talking to %s", ast_bridged_channel(p->owner)->name);
01825 else
01826 strcpy(talkingto, " is idle");
01827 agent_status = 1;
01828 online_agents++;
01829 } else if (!ast_strlen_zero(p->loginchan)) {
01830 snprintf(location, sizeof(location) - 20, "available at '%s'", p->loginchan);
01831 talkingto[0] = '\0';
01832 agent_status = 1;
01833 online_agents++;
01834 if (p->acknowledged)
01835 strncat(location, " (Confirmed)", sizeof(location) - strlen(location) - 1);
01836 }
01837 if (!ast_strlen_zero(p->moh))
01838 snprintf(music, sizeof(music), " (musiconhold is '%s')", p->moh);
01839 if (agent_status)
01840 ast_cli(a->fd, "%-12.12s %s%s%s%s\n", p->agent, username, location, talkingto, music);
01841 count_agents++;
01842 ast_mutex_unlock(&p->lock);
01843 }
01844 AST_LIST_UNLOCK(&agents);
01845 if (!count_agents)
01846 ast_cli(a->fd, "No Agents are configured in %s\n", config);
01847 else
01848 ast_cli(a->fd, "%d agents online\n", online_agents);
01849 ast_cli(a->fd, "\n");
01850 return CLI_SUCCESS;
01851 }
01852
01853 static const char agent_logoff_usage[] =
01854 "Usage: agent logoff <channel> [soft]\n"
01855 " Sets an agent as no longer logged in.\n"
01856 " If 'soft' is specified, do not hangup existing calls.\n";
01857
01858 static struct ast_cli_entry cli_agents[] = {
01859 AST_CLI_DEFINE(agents_show, "Show status of agents"),
01860 AST_CLI_DEFINE(agents_show_online, "Show all online agents"),
01861 AST_CLI_DEFINE(agent_logoff_cmd, "Sets an agent offline"),
01862 };
01863
01864
01865
01866
01867
01868
01869
01870
01871
01872
01873
01874 static int login_exec(struct ast_channel *chan, void *data)
01875 {
01876 int res=0;
01877 int tries = 0;
01878 int max_login_tries = maxlogintries;
01879 struct agent_pvt *p;
01880 struct ast_module_user *u;
01881 int login_state = 0;
01882 char user[AST_MAX_AGENT] = "";
01883 char pass[AST_MAX_AGENT];
01884 char agent[AST_MAX_AGENT] = "";
01885 char xpass[AST_MAX_AGENT] = "";
01886 char *errmsg;
01887 char *parse;
01888 AST_DECLARE_APP_ARGS(args,
01889 AST_APP_ARG(agent_id);
01890 AST_APP_ARG(options);
01891 AST_APP_ARG(extension);
01892 );
01893 const char *tmpoptions = NULL;
01894 int play_announcement = 1;
01895 char agent_goodbye[AST_MAX_FILENAME_LEN];
01896 int update_cdr = updatecdr;
01897 char *filename = "agent-loginok";
01898
01899 u = ast_module_user_add(chan);
01900
01901 parse = ast_strdupa(data);
01902
01903 AST_STANDARD_APP_ARGS(args, parse);
01904
01905 ast_copy_string(agent_goodbye, agentgoodbye, sizeof(agent_goodbye));
01906
01907 ast_channel_lock(chan);
01908
01909 if (!ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENTLMAXLOGINTRIES"))) {
01910 max_login_tries = atoi(pbx_builtin_getvar_helper(chan, "AGENTMAXLOGINTRIES"));
01911 if (max_login_tries < 0)
01912 max_login_tries = 0;
01913 tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTMAXLOGINTRIES");
01914 ast_verb(3, "Saw variable AGENTMAXLOGINTRIES=%s, setting max_login_tries to: %d on Channel '%s'.\n",tmpoptions,max_login_tries,chan->name);
01915 }
01916 if (!ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENTUPDATECDR"))) {
01917 if (ast_true(pbx_builtin_getvar_helper(chan, "AGENTUPDATECDR")))
01918 update_cdr = 1;
01919 else
01920 update_cdr = 0;
01921 tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTUPDATECDR");
01922 ast_verb(3, "Saw variable AGENTUPDATECDR=%s, setting update_cdr to: %d on Channel '%s'.\n",tmpoptions,update_cdr,chan->name);
01923 }
01924 if (!ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENTGOODBYE"))) {
01925 strcpy(agent_goodbye, pbx_builtin_getvar_helper(chan, "AGENTGOODBYE"));
01926 tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTGOODBYE");
01927 ast_verb(3, "Saw variable AGENTGOODBYE=%s, setting agent_goodbye to: %s on Channel '%s'.\n",tmpoptions,agent_goodbye,chan->name);
01928 }
01929 ast_channel_unlock(chan);
01930
01931
01932 if (!ast_strlen_zero(args.options)) {
01933 if (strchr(args.options, 's')) {
01934 play_announcement = 0;
01935 }
01936 }
01937
01938 if (chan->_state != AST_STATE_UP)
01939 res = ast_answer(chan);
01940 if (!res) {
01941 if (!ast_strlen_zero(args.agent_id))
01942 ast_copy_string(user, args.agent_id, AST_MAX_AGENT);
01943 else
01944 res = ast_app_getdata(chan, "agent-user", user, sizeof(user) - 1, 0);
01945 }
01946 while (!res && (max_login_tries==0 || tries < max_login_tries)) {
01947 tries++;
01948
01949 AST_LIST_LOCK(&agents);
01950 AST_LIST_TRAVERSE(&agents, p, list) {
01951 if (!strcmp(p->agent, user) && !p->pending)
01952 ast_copy_string(xpass, p->password, sizeof(xpass));
01953 }
01954 AST_LIST_UNLOCK(&agents);
01955 if (!res) {
01956 if (!ast_strlen_zero(xpass))
01957 res = ast_app_getdata(chan, "agent-pass", pass, sizeof(pass) - 1, 0);
01958 else
01959 pass[0] = '\0';
01960 }
01961 errmsg = "agent-incorrect";
01962
01963 #if 0
01964 ast_log(LOG_NOTICE, "user: %s, pass: %s\n", user, pass);
01965 #endif
01966
01967
01968 AST_LIST_LOCK(&agents);
01969 AST_LIST_TRAVERSE(&agents, p, list) {
01970 int unlock_channel = 1;
01971 ast_channel_lock(chan);
01972 ast_mutex_lock(&p->lock);
01973 if (!strcmp(p->agent, user) &&
01974 !strcmp(p->password, pass) && !p->pending) {
01975 login_state = 1;
01976
01977
01978 p->lastdisc = ast_tvnow();
01979 p->lastdisc.tv_sec++;
01980
01981
01982 if (!ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENTACKCALL"))) {
01983 if (!strcasecmp(pbx_builtin_getvar_helper(chan, "AGENTACKCALL"), "always"))
01984 p->ackcall = 2;
01985 else if (ast_true(pbx_builtin_getvar_helper(chan, "AGENTACKCALL")))
01986 p->ackcall = 1;
01987 else
01988 p->ackcall = 0;
01989 tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTACKCALL");
01990 ast_verb(3, "Saw variable AGENTACKCALL=%s, setting ackcall to: %d for Agent '%s'.\n", tmpoptions, p->ackcall, p->agent);
01991 ast_set_flag(p, AGENT_FLAG_ACKCALL);
01992 } else {
01993 p->ackcall = ackcall;
01994 }
01995 if (!ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF"))) {
01996 p->autologoff = atoi(pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF"));
01997 if (p->autologoff < 0)
01998 p->autologoff = 0;
01999 tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF");
02000 ast_verb(3, "Saw variable AGENTAUTOLOGOFF=%s, setting autologff to: %d for Agent '%s'.\n", tmpoptions, p->autologoff, p->agent);
02001 ast_set_flag(p, AGENT_FLAG_AUTOLOGOFF);
02002 } else {
02003 p->autologoff = autologoff;
02004 }
02005 if (!ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME"))) {
02006 p->wrapuptime = atoi(pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME"));
02007 if (p->wrapuptime < 0)
02008 p->wrapuptime = 0;
02009 tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME");
02010 ast_verb(3, "Saw variable AGENTWRAPUPTIME=%s, setting wrapuptime to: %d for Agent '%s'.\n", tmpoptions, p->wrapuptime, p->agent);
02011 ast_set_flag(p, AGENT_FLAG_WRAPUPTIME);
02012 } else {
02013 p->wrapuptime = wrapuptime;
02014 }
02015 tmpoptions = pbx_builtin_getvar_helper(chan, "AGENTACCEPTDMTF");
02016 if (!ast_strlen_zero(tmpoptions)) {
02017 p->acceptdtmf = *tmpoptions;
02018 ast_verb(3, "Saw variable AGENTACCEPTDTMF=%s, setting acceptdtmf to: %c for Agent '%s'.\n", tmpoptions, p->acceptdtmf, p->agent);
02019 ast_set_flag(p, AGENT_FLAG_ACCEPTDTMF);
02020 }
02021 tmpoptions = pbx_builtin_getvar_helper(chan, "AGENTENDDTMF");
02022 if (!ast_strlen_zero(tmpoptions)) {
02023 p->enddtmf = *tmpoptions;
02024 ast_verb(3, "Saw variable AGENTENDDTMF=%s, setting enddtmf to: %c for Agent '%s'.\n", tmpoptions, p->enddtmf, p->agent);
02025 ast_set_flag(p, AGENT_FLAG_ENDDTMF);
02026 }
02027 ast_channel_unlock(chan);
02028 unlock_channel = 0;
02029
02030 if (!p->chan) {
02031 long logintime;
02032 snprintf(agent, sizeof(agent), "Agent/%s", p->agent);
02033
02034 p->loginchan[0] = '\0';
02035 p->logincallerid[0] = '\0';
02036 p->acknowledged = 0;
02037
02038 ast_mutex_unlock(&p->lock);
02039 AST_LIST_UNLOCK(&agents);
02040 if( !res && play_announcement==1 )
02041 res = ast_streamfile(chan, filename, chan->language);
02042 if (!res)
02043 ast_waitstream(chan, "");
02044 AST_LIST_LOCK(&agents);
02045 ast_mutex_lock(&p->lock);
02046 if (!res) {
02047 res = ast_set_read_format(chan, ast_best_codec(chan->nativeformats));
02048 if (res)
02049 ast_log(LOG_WARNING, "Unable to set read format to %d\n", ast_best_codec(chan->nativeformats));
02050 }
02051 if (!res) {
02052 res = ast_set_write_format(chan, ast_best_codec(chan->nativeformats));
02053 if (res)
02054 ast_log(LOG_WARNING, "Unable to set write format to %d\n", ast_best_codec(chan->nativeformats));
02055 }
02056
02057 if (p->chan)
02058 res = -1;
02059 if (!res) {
02060 ast_indicate_data(chan, AST_CONTROL_HOLD,
02061 S_OR(p->moh, NULL),
02062 !ast_strlen_zero(p->moh) ? strlen(p->moh) + 1 : 0);
02063 if (p->loginstart == 0)
02064 time(&p->loginstart);
02065 manager_event(EVENT_FLAG_AGENT, "Agentlogin",
02066 "Agent: %s\r\n"
02067 "Channel: %s\r\n"
02068 "Uniqueid: %s\r\n",
02069 p->agent, chan->name, chan->uniqueid);
02070 if (update_cdr && chan->cdr)
02071 snprintf(chan->cdr->channel, sizeof(chan->cdr->channel), "Agent/%s", p->agent);
02072 ast_queue_log("NONE", chan->uniqueid, agent, "AGENTLOGIN", "%s", chan->name);
02073 ast_verb(2, "Agent '%s' logged in (format %s/%s)\n", p->agent,
02074 ast_getformatname(chan->readformat), ast_getformatname(chan->writeformat));
02075
02076 p->chan = chan;
02077 if (p->ackcall > 1)
02078 check_beep(p, 0);
02079 else
02080 check_availability(p, 0);
02081 ast_mutex_unlock(&p->lock);
02082 AST_LIST_UNLOCK(&agents);
02083 ast_devstate_changed(AST_DEVICE_NOT_INUSE, "Agent/%s", p->agent);
02084 while (res >= 0) {
02085 ast_mutex_lock(&p->lock);
02086 if (p->deferlogoff && p->chan) {
02087 ast_softhangup(p->chan, AST_SOFTHANGUP_EXPLICIT);
02088 p->deferlogoff = 0;
02089 }
02090 if (p->chan != chan)
02091 res = -1;
02092 ast_mutex_unlock(&p->lock);
02093
02094 sched_yield();
02095 if (res)
02096 break;
02097
02098 AST_LIST_LOCK(&agents);
02099 ast_mutex_lock(&p->lock);
02100 if (p->lastdisc.tv_sec) {
02101 if (ast_tvdiff_ms(ast_tvnow(), p->lastdisc) > 0) {
02102 ast_debug(1, "Wrapup time for %s expired!\n", p->agent);
02103 p->lastdisc = ast_tv(0, 0);
02104 ast_devstate_changed(AST_DEVICE_NOT_INUSE, "Agent/%s", p->agent);
02105 if (p->ackcall > 1)
02106 check_beep(p, 0);
02107 else
02108 check_availability(p, 0);
02109 }
02110 }
02111 ast_mutex_unlock(&p->lock);
02112 AST_LIST_UNLOCK(&agents);
02113
02114 ast_mutex_lock(&p->app_lock);
02115 if (p->app_lock_flag == 1) {
02116 ast_cond_wait(&p->app_complete_cond, &p->app_lock);
02117 }
02118 ast_mutex_unlock(&p->app_lock);
02119 ast_mutex_lock(&p->lock);
02120 ast_mutex_unlock(&p->lock);
02121 if (p->ackcall > 1)
02122 res = agent_ack_sleep(p);
02123 else
02124 res = ast_safe_sleep_conditional( chan, 1000, agent_cont_sleep, p );
02125 if ((p->ackcall > 1) && (res == 1)) {
02126 AST_LIST_LOCK(&agents);
02127 ast_mutex_lock(&p->lock);
02128 check_availability(p, 0);
02129 ast_mutex_unlock(&p->lock);
02130 AST_LIST_UNLOCK(&agents);
02131 res = 0;
02132 }
02133 sched_yield();
02134 }
02135 ast_mutex_lock(&p->lock);
02136 if (res && p->owner)
02137 ast_log(LOG_WARNING, "Huh? We broke out when there was still an owner?\n");
02138
02139 if (p->chan == chan) {
02140 p->chan = NULL;
02141 }
02142 p->acknowledged = 0;
02143 logintime = time(NULL) - p->loginstart;
02144 p->loginstart = 0;
02145 ast_mutex_unlock(&p->lock);
02146 manager_event(EVENT_FLAG_AGENT, "Agentlogoff",
02147 "Agent: %s\r\n"
02148 "Logintime: %ld\r\n"
02149 "Uniqueid: %s\r\n",
02150 p->agent, logintime, chan->uniqueid);
02151 ast_queue_log("NONE", chan->uniqueid, agent, "AGENTLOGOFF", "%s|%ld", chan->name, logintime);
02152 ast_verb(2, "Agent '%s' logged out\n", p->agent);
02153
02154 ast_devstate_changed(AST_DEVICE_UNAVAILABLE, "Agent/%s", p->agent);
02155 if (p->dead && !p->owner) {
02156 ast_mutex_destroy(&p->lock);
02157 ast_mutex_destroy(&p->app_lock);
02158 ast_cond_destroy(&p->app_complete_cond);
02159 ast_free(p);
02160 }
02161 }
02162 else {
02163 ast_mutex_unlock(&p->lock);
02164 p = NULL;
02165 }
02166 res = -1;
02167 } else {
02168 ast_mutex_unlock(&p->lock);
02169 errmsg = "agent-alreadyon";
02170 p = NULL;
02171 }
02172 break;
02173 }
02174 ast_mutex_unlock(&p->lock);
02175 if (unlock_channel) {
02176 ast_channel_unlock(chan);
02177 }
02178 }
02179 if (!p)
02180 AST_LIST_UNLOCK(&agents);
02181
02182 if (!res && (max_login_tries==0 || tries < max_login_tries))
02183 res = ast_app_getdata(chan, errmsg, user, sizeof(user) - 1, 0);
02184 }
02185
02186 if (!res)
02187 res = ast_safe_sleep(chan, 500);
02188
02189 ast_module_user_remove(u);
02190
02191 return -1;
02192 }
02193
02194
02195
02196
02197
02198
02199
02200
02201
02202 static int agentmonitoroutgoing_exec(struct ast_channel *chan, void *data)
02203 {
02204 int exitifnoagentid = 0;
02205 int nowarnings = 0;
02206 int changeoutgoing = 0;
02207 int res = 0;
02208 char agent[AST_MAX_AGENT];
02209
02210 if (data) {
02211 if (strchr(data, 'd'))
02212 exitifnoagentid = 1;
02213 if (strchr(data, 'n'))
02214 nowarnings = 1;
02215 if (strchr(data, 'c'))
02216 changeoutgoing = 1;
02217 }
02218 if (chan->cid.cid_num) {
02219 const char *tmp;
02220 char agentvar[AST_MAX_BUF];
02221 snprintf(agentvar, sizeof(agentvar), "%s_%s", GETAGENTBYCALLERID, chan->cid.cid_num);
02222 if ((tmp = pbx_builtin_getvar_helper(NULL, agentvar))) {
02223 struct agent_pvt *p;
02224 ast_copy_string(agent, tmp, sizeof(agent));
02225 AST_LIST_LOCK(&agents);
02226 AST_LIST_TRAVERSE(&agents, p, list) {
02227 if (!strcasecmp(p->agent, tmp)) {
02228 if (changeoutgoing) snprintf(chan->cdr->channel, sizeof(chan->cdr->channel), "Agent/%s", p->agent);
02229 __agent_start_monitoring(chan, p, 1);
02230 break;
02231 }
02232 }
02233 AST_LIST_UNLOCK(&agents);
02234
02235 } else {
02236 res = -1;
02237 if (!nowarnings)
02238 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);
02239 }
02240 } else {
02241 res = -1;
02242 if (!nowarnings)
02243 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");
02244 }
02245 if (res) {
02246 if (exitifnoagentid)
02247 return res;
02248 }
02249 return 0;
02250 }
02251
02252
02253
02254
02255 static void dump_agents(void)
02256 {
02257 struct agent_pvt *cur_agent = NULL;
02258 char buf[256];
02259
02260 AST_LIST_TRAVERSE(&agents, cur_agent, list) {
02261 if (cur_agent->chan)
02262 continue;
02263
02264 if (!ast_strlen_zero(cur_agent->loginchan)) {
02265 snprintf(buf, sizeof(buf), "%s;%s", cur_agent->loginchan, cur_agent->logincallerid);
02266 if (ast_db_put(pa_family, cur_agent->agent, buf))
02267 ast_log(LOG_WARNING, "failed to create persistent entry in ASTdb for %s!\n", buf);
02268 else
02269 ast_debug(1, "Saved Agent: %s on %s\n", cur_agent->agent, cur_agent->loginchan);
02270 } else {
02271
02272 ast_db_del(pa_family, cur_agent->agent);
02273 }
02274 }
02275 }
02276
02277
02278
02279
02280 static void reload_agents(void)
02281 {
02282 char *agent_num;
02283 struct ast_db_entry *db_tree;
02284 struct ast_db_entry *entry;
02285 struct agent_pvt *cur_agent;
02286 char agent_data[256];
02287 char *parse;
02288 char *agent_chan;
02289 char *agent_callerid;
02290
02291 db_tree = ast_db_gettree(pa_family, NULL);
02292
02293 AST_LIST_LOCK(&agents);
02294 for (entry = db_tree; entry; entry = entry->next) {
02295 agent_num = entry->key + strlen(pa_family) + 2;
02296 AST_LIST_TRAVERSE(&agents, cur_agent, list) {
02297 ast_mutex_lock(&cur_agent->lock);
02298 if (strcmp(agent_num, cur_agent->agent) == 0)
02299 break;
02300 ast_mutex_unlock(&cur_agent->lock);
02301 }
02302 if (!cur_agent) {
02303 ast_db_del(pa_family, agent_num);
02304 continue;
02305 } else
02306 ast_mutex_unlock(&cur_agent->lock);
02307 if (!ast_db_get(pa_family, agent_num, agent_data, sizeof(agent_data)-1)) {
02308 ast_debug(1, "Reload Agent from AstDB: %s on %s\n", cur_agent->agent, agent_data);
02309 parse = agent_data;
02310 agent_chan = strsep(&parse, ";");
02311 agent_callerid = strsep(&parse, ";");
02312 ast_copy_string(cur_agent->loginchan, agent_chan, sizeof(cur_agent->loginchan));
02313 if (agent_callerid) {
02314 ast_copy_string(cur_agent->logincallerid, agent_callerid, sizeof(cur_agent->logincallerid));
02315 set_agentbycallerid(cur_agent->logincallerid, cur_agent->agent);
02316 } else
02317 cur_agent->logincallerid[0] = '\0';
02318 if (cur_agent->loginstart == 0)
02319 time(&cur_agent->loginstart);
02320 ast_devstate_changed(AST_DEVICE_UNKNOWN, "Agent/%s", cur_agent->agent);
02321 }
02322 }
02323 AST_LIST_UNLOCK(&agents);
02324 if (db_tree) {
02325 ast_log(LOG_NOTICE, "Agents successfully reloaded from database.\n");
02326 ast_db_freetree(db_tree);
02327 }
02328 }
02329
02330
02331 static int agent_devicestate(void *data)
02332 {
02333 struct agent_pvt *p;
02334 char *s;
02335 ast_group_t groupmatch;
02336 int groupoff;
02337 int res = AST_DEVICE_INVALID;
02338
02339 s = data;
02340 if ((s[0] == '@') && (sscanf(s + 1, "%d", &groupoff) == 1))
02341 groupmatch = (1 << groupoff);
02342 else if ((s[0] == ':') && (sscanf(s + 1, "%d", &groupoff) == 1)) {
02343 groupmatch = (1 << groupoff);
02344 } else
02345 groupmatch = 0;
02346
02347
02348 AST_LIST_LOCK(&agents);
02349 AST_LIST_TRAVERSE(&agents, p, list) {
02350 ast_mutex_lock(&p->lock);
02351 if (!p->pending && ((groupmatch && (p->group & groupmatch)) || !strcmp(data, p->agent))) {
02352 if (p->owner) {
02353 if (res != AST_DEVICE_INUSE)
02354 res = AST_DEVICE_BUSY;
02355 } else {
02356 if (res == AST_DEVICE_BUSY)
02357 res = AST_DEVICE_INUSE;
02358 if (p->chan || !ast_strlen_zero(p->loginchan)) {
02359 if (res == AST_DEVICE_INVALID)
02360 res = AST_DEVICE_UNKNOWN;
02361 } else if (res == AST_DEVICE_INVALID)
02362 res = AST_DEVICE_UNAVAILABLE;
02363 }
02364 if (!strcmp(data, p->agent)) {
02365 ast_mutex_unlock(&p->lock);
02366 break;
02367 }
02368 }
02369 ast_mutex_unlock(&p->lock);
02370 }
02371 AST_LIST_UNLOCK(&agents);
02372 return res;
02373 }
02374
02375
02376
02377
02378 static struct agent_pvt *find_agent(char *agentid)
02379 {
02380 struct agent_pvt *cur;
02381
02382 AST_LIST_TRAVERSE(&agents, cur, list) {
02383 if (!strcmp(cur->agent, agentid))
02384 break;
02385 }
02386
02387 return cur;
02388 }
02389
02390 static int function_agent(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
02391 {
02392 char *parse;
02393 AST_DECLARE_APP_ARGS(args,
02394 AST_APP_ARG(agentid);
02395 AST_APP_ARG(item);
02396 );
02397 char *tmp;
02398 struct agent_pvt *agent;
02399
02400 buf[0] = '\0';
02401
02402 if (ast_strlen_zero(data)) {
02403 ast_log(LOG_WARNING, "The AGENT function requires an argument - agentid!\n");
02404 return -1;
02405 }
02406
02407 parse = ast_strdupa(data);
02408
02409 AST_NONSTANDARD_APP_ARGS(args, parse, ':');
02410 if (!args.item)
02411 args.item = "status";
02412
02413 AST_LIST_LOCK(&agents);
02414
02415 if (!(agent = find_agent(args.agentid))) {
02416 AST_LIST_UNLOCK(&agents);
02417 ast_log(LOG_WARNING, "Agent '%s' not found!\n", args.agentid);
02418 return -1;
02419 }
02420
02421 if (!strcasecmp(args.item, "status")) {
02422 char *status = "LOGGEDOUT";
02423 if (agent->chan || !ast_strlen_zero(agent->loginchan))
02424 status = "LOGGEDIN";
02425 ast_copy_string(buf, status, len);
02426 } else if (!strcasecmp(args.item, "password"))
02427 ast_copy_string(buf, agent->password, len);
02428 else if (!strcasecmp(args.item, "name"))
02429 ast_copy_string(buf, agent->name, len);
02430 else if (!strcasecmp(args.item, "mohclass"))
02431 ast_copy_string(buf, agent->moh, len);
02432 else if (!strcasecmp(args.item, "channel")) {
02433 if (agent->chan) {
02434 ast_copy_string(buf, agent->chan->name, len);
02435 tmp = strrchr(buf, '-');
02436 if (tmp)
02437 *tmp = '\0';
02438 }
02439 } else if (!strcasecmp(args.item, "exten"))
02440 ast_copy_string(buf, agent->loginchan, len);
02441
02442 AST_LIST_UNLOCK(&agents);
02443
02444 return 0;
02445 }
02446
02447 struct ast_custom_function agent_function = {
02448 .name = "AGENT",
02449 .synopsis = "Gets information about an Agent",
02450 .syntax = "AGENT(<agentid>[:item])",
02451 .read = function_agent,
02452 .desc = "The valid items to retrieve are:\n"
02453 "- status (default) The status of the agent\n"
02454 " LOGGEDIN | LOGGEDOUT\n"
02455 "- password The password of the agent\n"
02456 "- name The name of the agent\n"
02457 "- mohclass MusicOnHold class\n"
02458 "- exten The callback extension for the Agent (AgentCallbackLogin)\n"
02459 "- channel The name of the active channel for the Agent (AgentLogin)\n"
02460 };
02461
02462
02463
02464
02465
02466
02467
02468
02469
02470 static int load_module(void)
02471 {
02472
02473 if (ast_channel_register(&agent_tech)) {
02474 ast_log(LOG_ERROR, "Unable to register channel class 'Agent'\n");
02475 return AST_MODULE_LOAD_FAILURE;
02476 }
02477
02478 if (!read_agent_config(0))
02479 return AST_MODULE_LOAD_DECLINE;
02480 if (persistent_agents)
02481 reload_agents();
02482
02483 ast_register_application(app, login_exec, synopsis, descrip);
02484 ast_register_application(app3, agentmonitoroutgoing_exec, synopsis3, descrip3);
02485
02486
02487 ast_manager_register2("Agents", EVENT_FLAG_AGENT, action_agents, "Lists agents and their status", mandescr_agents);
02488 ast_manager_register2("AgentLogoff", EVENT_FLAG_AGENT, action_agent_logoff, "Sets an agent as no longer logged in", mandescr_agent_logoff);
02489
02490
02491 ast_cli_register_multiple(cli_agents, sizeof(cli_agents) / sizeof(struct ast_cli_entry));
02492
02493
02494 ast_custom_function_register(&agent_function);
02495
02496 return AST_MODULE_LOAD_SUCCESS;
02497 }
02498
02499 static int reload(void)
02500 {
02501 if (!read_agent_config(1)) {
02502 if (persistent_agents)
02503 reload_agents();
02504 }
02505 return 0;
02506 }
02507
02508 static int unload_module(void)
02509 {
02510 struct agent_pvt *p;
02511
02512 ast_channel_unregister(&agent_tech);
02513
02514 ast_custom_function_unregister(&agent_function);
02515
02516 ast_cli_unregister_multiple(cli_agents, sizeof(cli_agents) / sizeof(struct ast_cli_entry));
02517
02518 ast_unregister_application(app);
02519 ast_unregister_application(app3);
02520
02521 ast_manager_unregister("Agents");
02522 ast_manager_unregister("AgentLogoff");
02523
02524 AST_LIST_LOCK(&agents);
02525
02526 while ((p = AST_LIST_REMOVE_HEAD(&agents, list))) {
02527 if (p->owner)
02528 ast_softhangup(p->owner, AST_SOFTHANGUP_APPUNLOAD);
02529 ast_free(p);
02530 }
02531 AST_LIST_UNLOCK(&agents);
02532 return 0;
02533 }
02534
02535 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Agent Proxy Channel",
02536 .load = load_module,
02537 .unload = unload_module,
02538 .reload = reload,
02539 );