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

Generated on Fri Apr 24 16:25:51 2009 for Asterisk - the Open Source PBX by  doxygen 1.4.7