Thu Dec 17 23:51:05 2009

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

Generated on Thu Dec 17 23:51:05 2009 for Asterisk - the Open Source PBX by  doxygen 1.4.7