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