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