Sat Aug 6 00:39:23 2011

Asterisk developer's documentation


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

Generated on Sat Aug 6 00:39:23 2011 for Asterisk - the Open Source PBX by  doxygen 1.4.7