Mon Sep 20 2010 00:20:25

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