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