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

Generated on Thu Feb 5 16:25:41 2009 for Asterisk - the Open Source PBX by  doxygen 1.4.7