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