Wed Aug 18 22:33:44 2010

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

Generated on Wed Aug 18 22:33:44 2010 for Asterisk - the Open Source PBX by  doxygen 1.4.7