Mon May 14 04:42:54 2007

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

Generated on Mon May 14 04:42:55 2007 for Asterisk - the Open Source PBX by  doxygen 1.5.1