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