Wed Aug 15 01:24:16 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: 76654 $")
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 && !p->chan->generator)  \
00216          ast_set_read_format(p->chan, ast->rawreadformat); \
00217       if (p->chan->writeformat != ast->rawwriteformat && !p->chan->generator) \
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, filename);
00415       ast_monitor_start(ast, recordformat, tmp, needlock);
00416       ast_monitor_setjoinfiles(ast, 1);
00417       snprintf(tmp2, sizeof(tmp2), "%s%s.%s", 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_tvadd(ast_tvnow(), ast_samp2tv(p->wrapuptime, 1000));
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) > 0) 
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    if (p->chan)
00997       ast_indicate(p->chan, AST_CONTROL_UNHOLD);
00998    p->owning_app = pthread_self();
00999    /* After the above step, there should not be any blockers. */
01000    if (p->chan) {
01001       if (ast_test_flag(p->chan, AST_FLAG_BLOCKING)) {
01002          ast_log( LOG_ERROR, "A blocker exists after agent channel ownership acquired\n" );
01003          CRASH;
01004       }
01005    }
01006    return tmp;
01007 }
01008 
01009 
01010 /*!
01011  * Read configuration data. The file named agents.conf.
01012  *
01013  * \returns Always 0, or so it seems.
01014  */
01015 static int read_agent_config(void)
01016 {
01017    struct ast_config *cfg;
01018    struct ast_config *ucfg;
01019    struct ast_variable *v;
01020    struct agent_pvt *p;
01021    const char *general_val;
01022    const char *catname;
01023    const char *hasagent;
01024    int genhasagent;
01025 
01026    group = 0;
01027    autologoff = 0;
01028    wrapuptime = 0;
01029    ackcall = 0;
01030    endcall = 1;
01031    cfg = ast_config_load(config);
01032    if (!cfg) {
01033       ast_log(LOG_NOTICE, "No agent configuration found -- agent support disabled\n");
01034       return 0;
01035    }
01036    AST_LIST_LOCK(&agents);
01037    AST_LIST_TRAVERSE(&agents, p, list) {
01038       p->dead = 1;
01039    }
01040    strcpy(moh, "default");
01041    /* set the default recording values */
01042    recordagentcalls = 0;
01043    strcpy(recordformat, "wav");
01044    strcpy(recordformatext, "wav");
01045    urlprefix[0] = '\0';
01046    savecallsin[0] = '\0';
01047 
01048    /* Read in [general] section for persistence */
01049    if ((general_val = ast_variable_retrieve(cfg, "general", "persistentagents")))
01050       persistent_agents = ast_true(general_val);
01051    multiplelogin = ast_true(ast_variable_retrieve(cfg, "general", "multiplelogin"));
01052 
01053    /* Read in the [agents] section */
01054    v = ast_variable_browse(cfg, "agents");
01055    while(v) {
01056       /* Create the interface list */
01057       if (!strcasecmp(v->name, "agent")) {
01058          add_agent(v->value, 0);
01059       } else if (!strcasecmp(v->name, "group")) {
01060          group = ast_get_group(v->value);
01061       } else if (!strcasecmp(v->name, "autologoff")) {
01062          autologoff = atoi(v->value);
01063          if (autologoff < 0)
01064             autologoff = 0;
01065       } else if (!strcasecmp(v->name, "ackcall")) {
01066          if (!strcasecmp(v->value, "always"))
01067             ackcall = 2;
01068          else if (ast_true(v->value))
01069             ackcall = 1;
01070          else
01071             ackcall = 0;
01072       } else if (!strcasecmp(v->name, "endcall")) {
01073          endcall = ast_true(v->value);
01074       } else if (!strcasecmp(v->name, "wrapuptime")) {
01075          wrapuptime = atoi(v->value);
01076          if (wrapuptime < 0)
01077             wrapuptime = 0;
01078       } else if (!strcasecmp(v->name, "maxlogintries") && !ast_strlen_zero(v->value)) {
01079          maxlogintries = atoi(v->value);
01080          if (maxlogintries < 0)
01081             maxlogintries = 0;
01082       } else if (!strcasecmp(v->name, "goodbye") && !ast_strlen_zero(v->value)) {
01083          strcpy(agentgoodbye,v->value);
01084       } else if (!strcasecmp(v->name, "musiconhold")) {
01085          ast_copy_string(moh, v->value, sizeof(moh));
01086       } else if (!strcasecmp(v->name, "updatecdr")) {
01087          if (ast_true(v->value))
01088             updatecdr = 1;
01089          else
01090             updatecdr = 0;
01091       } else if (!strcasecmp(v->name, "autologoffunavail")) {
01092          if (ast_true(v->value))
01093             autologoffunavail = 1;
01094          else
01095             autologoffunavail = 0;
01096       } else if (!strcasecmp(v->name, "recordagentcalls")) {
01097          recordagentcalls = ast_true(v->value);
01098       } else if (!strcasecmp(v->name, "recordformat")) {
01099          ast_copy_string(recordformat, v->value, sizeof(recordformat));
01100          if (!strcasecmp(v->value, "wav49"))
01101             strcpy(recordformatext, "WAV");
01102          else
01103             ast_copy_string(recordformatext, v->value, sizeof(recordformatext));
01104       } else if (!strcasecmp(v->name, "urlprefix")) {
01105          ast_copy_string(urlprefix, v->value, sizeof(urlprefix));
01106          if (urlprefix[strlen(urlprefix) - 1] != '/')
01107             strncat(urlprefix, "/", sizeof(urlprefix) - strlen(urlprefix) - 1);
01108       } else if (!strcasecmp(v->name, "savecallsin")) {
01109          if (v->value[0] == '/')
01110             ast_copy_string(savecallsin, v->value, sizeof(savecallsin));
01111          else
01112             snprintf(savecallsin, sizeof(savecallsin) - 2, "/%s", v->value);
01113          if (savecallsin[strlen(savecallsin) - 1] != '/')
01114             strncat(savecallsin, "/", sizeof(savecallsin) - strlen(savecallsin) - 1);
01115       } else if (!strcasecmp(v->name, "custom_beep")) {
01116          ast_copy_string(beep, v->value, sizeof(beep));
01117       }
01118       v = v->next;
01119    }
01120    if ((ucfg = ast_config_load("users.conf"))) {
01121       genhasagent = ast_true(ast_variable_retrieve(ucfg, "general", "hasagent"));
01122       catname = ast_category_browse(ucfg, NULL);
01123       while(catname) {
01124          if (strcasecmp(catname, "general")) {
01125             hasagent = ast_variable_retrieve(ucfg, catname, "hasagent");
01126             if (ast_true(hasagent) || (!hasagent && genhasagent)) {
01127                char tmp[256];
01128                const char *fullname = ast_variable_retrieve(ucfg, catname, "fullname");
01129                const char *secret = ast_variable_retrieve(ucfg, catname, "secret");
01130                if (!fullname)
01131                   fullname = "";
01132                if (!secret)
01133                   secret = "";
01134                snprintf(tmp, sizeof(tmp), "%s,%s,%s", catname, secret,fullname);
01135                add_agent(tmp, 0);
01136             }
01137          }
01138          catname = ast_category_browse(ucfg, catname);
01139       }
01140       ast_config_destroy(ucfg);
01141    }
01142    AST_LIST_TRAVERSE_SAFE_BEGIN(&agents, p, list) {
01143       if (p->dead) {
01144          AST_LIST_REMOVE_CURRENT(&agents, list);
01145          /* Destroy if  appropriate */
01146          if (!p->owner) {
01147             if (!p->chan) {
01148                ast_mutex_destroy(&p->lock);
01149                ast_mutex_destroy(&p->app_lock);
01150                free(p);
01151             } else {
01152                /* Cause them to hang up */
01153                ast_softhangup(p->chan, AST_SOFTHANGUP_EXPLICIT);
01154             }
01155          }
01156       }
01157    }
01158    AST_LIST_TRAVERSE_SAFE_END
01159    AST_LIST_UNLOCK(&agents);
01160    ast_config_destroy(cfg);
01161    return 1;
01162 }
01163 
01164 static int check_availability(struct agent_pvt *newlyavailable, int needlock)
01165 {
01166    struct ast_channel *chan=NULL, *parent=NULL;
01167    struct agent_pvt *p;
01168    int res;
01169 
01170    if (option_debug)
01171       ast_log(LOG_DEBUG, "Checking availability of '%s'\n", newlyavailable->agent);
01172    if (needlock)
01173       AST_LIST_LOCK(&agents);
01174    AST_LIST_TRAVERSE(&agents, p, list) {
01175       if (p == newlyavailable) {
01176          continue;
01177       }
01178       ast_mutex_lock(&p->lock);
01179       if (!p->abouttograb && p->pending && ((p->group && (newlyavailable->group & p->group)) || !strcmp(p->agent, newlyavailable->agent))) {
01180          if (option_debug)
01181             ast_log(LOG_DEBUG, "Call '%s' looks like a winner for agent '%s'\n", p->owner->name, newlyavailable->agent);
01182          /* We found a pending call, time to merge */
01183          chan = agent_new(newlyavailable, AST_STATE_DOWN);
01184          parent = p->owner;
01185          p->abouttograb = 1;
01186          ast_mutex_unlock(&p->lock);
01187          break;
01188       }
01189       ast_mutex_unlock(&p->lock);
01190    }
01191    if (needlock)
01192       AST_LIST_UNLOCK(&agents);
01193    if (parent && chan)  {
01194       if (newlyavailable->ackcall > 1) {
01195          /* Don't do beep here */
01196          res = 0;
01197       } else {
01198          if (option_debug > 2)
01199             ast_log( LOG_DEBUG, "Playing beep, lang '%s'\n", newlyavailable->chan->language);
01200          res = ast_streamfile(newlyavailable->chan, beep, newlyavailable->chan->language);
01201          if (option_debug > 2)
01202             ast_log( LOG_DEBUG, "Played beep, result '%d'\n", res);
01203          if (!res) {
01204             res = ast_waitstream(newlyavailable->chan, "");
01205             ast_log( LOG_DEBUG, "Waited for stream, result '%d'\n", res);
01206          }
01207       }
01208       if (!res) {
01209          /* Note -- parent may have disappeared */
01210          if (p->abouttograb) {
01211             newlyavailable->acknowledged = 1;
01212             /* Safe -- agent lock already held */
01213             ast_setstate(parent, AST_STATE_UP);
01214             ast_setstate(chan, AST_STATE_UP);
01215             ast_copy_string(parent->context, chan->context, sizeof(parent->context));
01216             /* Go ahead and mark the channel as a zombie so that masquerade will
01217                destroy it for us, and we need not call ast_hangup */
01218             ast_mutex_lock(&parent->lock);
01219             ast_set_flag(chan, AST_FLAG_ZOMBIE);
01220             ast_channel_masquerade(parent, chan);
01221             ast_mutex_unlock(&parent->lock);
01222             p->abouttograb = 0;
01223          } else {
01224             if (option_debug)
01225                ast_log(LOG_DEBUG, "Sneaky, parent disappeared in the mean time...\n");
01226             agent_cleanup(newlyavailable);
01227          }
01228       } else {
01229          if (option_debug)
01230             ast_log(LOG_DEBUG, "Ugh...  Agent hung up at exactly the wrong time\n");
01231          agent_cleanup(newlyavailable);
01232       }
01233    }
01234    return 0;
01235 }
01236 
01237 static int check_beep(struct agent_pvt *newlyavailable, int needlock)
01238 {
01239    struct agent_pvt *p;
01240    int res=0;
01241 
01242    ast_log(LOG_DEBUG, "Checking beep availability of '%s'\n", newlyavailable->agent);
01243    if (needlock)
01244       AST_LIST_LOCK(&agents);
01245    AST_LIST_TRAVERSE(&agents, p, list) {
01246       if (p == newlyavailable) {
01247          continue;
01248       }
01249       ast_mutex_lock(&p->lock);
01250       if (!p->abouttograb && p->pending && ((p->group && (newlyavailable->group & p->group)) || !strcmp(p->agent, newlyavailable->agent))) {
01251          if (option_debug)
01252             ast_log(LOG_DEBUG, "Call '%s' looks like a would-be winner for agent '%s'\n", p->owner->name, newlyavailable->agent);
01253          ast_mutex_unlock(&p->lock);
01254          break;
01255       }
01256       ast_mutex_unlock(&p->lock);
01257    }
01258    if (needlock)
01259       AST_LIST_UNLOCK(&agents);
01260    if (p) {
01261       ast_mutex_unlock(&newlyavailable->lock);
01262       if (option_debug > 2)
01263          ast_log( LOG_DEBUG, "Playing beep, lang '%s'\n", newlyavailable->chan->language);
01264       res = ast_streamfile(newlyavailable->chan, beep, newlyavailable->chan->language);
01265       if (option_debug > 2)
01266          ast_log( LOG_DEBUG, "Played beep, result '%d'\n", res);
01267       if (!res) {
01268          res = ast_waitstream(newlyavailable->chan, "");
01269          if (option_debug)
01270             ast_log( LOG_DEBUG, "Waited for stream, result '%d'\n", res);
01271       }
01272       ast_mutex_lock(&newlyavailable->lock);
01273    }
01274    return res;
01275 }
01276 
01277 /* 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. */
01278 static int allow_multiple_login(char *chan, char *context)
01279 {
01280    struct agent_pvt *p;
01281    char loginchan[80];
01282 
01283    if(multiplelogin)
01284       return 1;
01285    if(!chan) 
01286       return 0;
01287 
01288    snprintf(loginchan, sizeof(loginchan), "%s@%s", chan, S_OR(context, "default"));
01289    
01290    AST_LIST_TRAVERSE(&agents, p, list) {
01291       if(!strcasecmp(chan, p->loginchan))
01292          return 0;
01293    }
01294    return -1;
01295 }
01296 
01297 /*! \brief Part of the Asterisk PBX interface */
01298 static struct ast_channel *agent_request(const char *type, int format, void *data, int *cause)
01299 {
01300    struct agent_pvt *p;
01301    struct ast_channel *chan = NULL;
01302    char *s;
01303    ast_group_t groupmatch;
01304    int groupoff;
01305    int waitforagent=0;
01306    int hasagent = 0;
01307    struct timeval tv;
01308 
01309    s = data;
01310    if ((s[0] == '@') && (sscanf(s + 1, "%d", &groupoff) == 1)) {
01311       groupmatch = (1 << groupoff);
01312    } else if ((s[0] == ':') && (sscanf(s + 1, "%d", &groupoff) == 1)) {
01313       groupmatch = (1 << groupoff);
01314       waitforagent = 1;
01315    } else 
01316       groupmatch = 0;
01317 
01318    /* Check actual logged in agents first */
01319    AST_LIST_LOCK(&agents);
01320    AST_LIST_TRAVERSE(&agents, p, list) {
01321       ast_mutex_lock(&p->lock);
01322       if (!p->pending && ((groupmatch && (p->group & groupmatch)) || !strcmp(data, p->agent)) &&
01323           ast_strlen_zero(p->loginchan)) {
01324          if (p->chan)
01325             hasagent++;
01326          tv = ast_tvnow();
01327          if (!p->lastdisc.tv_sec || (tv.tv_sec >= p->lastdisc.tv_sec)) {
01328             p->lastdisc = ast_tv(0, 0);
01329             /* Agent must be registered, but not have any active call, and not be in a waiting state */
01330             if (!p->owner && p->chan) {
01331                /* Fixed agent */
01332                chan = agent_new(p, AST_STATE_DOWN);
01333             }
01334             if (chan) {
01335                ast_mutex_unlock(&p->lock);
01336                break;
01337             }
01338          }
01339       }
01340       ast_mutex_unlock(&p->lock);
01341    }
01342    if (!p) {
01343       AST_LIST_TRAVERSE(&agents, p, list) {
01344          ast_mutex_lock(&p->lock);
01345          if (!p->pending && ((groupmatch && (p->group & groupmatch)) || !strcmp(data, p->agent))) {
01346             if (p->chan || !ast_strlen_zero(p->loginchan))
01347                hasagent++;
01348             tv = ast_tvnow();
01349 #if 0
01350             ast_log(LOG_NOTICE, "Time now: %ld, Time of lastdisc: %ld\n", tv.tv_sec, p->lastdisc.tv_sec);
01351 #endif
01352             if (!p->lastdisc.tv_sec || (tv.tv_sec >= p->lastdisc.tv_sec)) {
01353                p->lastdisc = ast_tv(0, 0);
01354                /* Agent must be registered, but not have any active call, and not be in a waiting state */
01355                if (!p->owner && p->chan) {
01356                   /* Could still get a fixed agent */
01357                   chan = agent_new(p, AST_STATE_DOWN);
01358                } else if (!p->owner && !ast_strlen_zero(p->loginchan)) {
01359                   /* Adjustable agent */
01360                   p->chan = ast_request("Local", format, p->loginchan, cause);
01361                   if (p->chan)
01362                      chan = agent_new(p, AST_STATE_DOWN);
01363                }
01364                if (chan) {
01365                   ast_mutex_unlock(&p->lock);
01366                   break;
01367                }
01368             }
01369          }
01370          ast_mutex_unlock(&p->lock);
01371       }
01372    }
01373 
01374    if (!chan && waitforagent) {
01375       /* No agent available -- but we're requesting to wait for one.
01376          Allocate a place holder */
01377       if (hasagent) {
01378          if (option_debug)
01379             ast_log(LOG_DEBUG, "Creating place holder for '%s'\n", s);
01380          p = add_agent(data, 1);
01381          p->group = groupmatch;
01382          chan = agent_new(p, AST_STATE_DOWN);
01383          if (!chan) 
01384             ast_log(LOG_WARNING, "Weird...  Fix this to drop the unused pending agent\n");
01385       } else
01386          ast_log(LOG_DEBUG, "Not creating place holder for '%s' since nobody logged in\n", s);
01387    }
01388    *cause = hasagent ? AST_CAUSE_BUSY : AST_CAUSE_UNREGISTERED;
01389    AST_LIST_UNLOCK(&agents);
01390    return chan;
01391 }
01392 
01393 static force_inline int powerof(unsigned int d)
01394 {
01395    int x = ffs(d);
01396 
01397    if (x)
01398       return x - 1;
01399 
01400    return 0;
01401 }
01402 
01403 /*!
01404  * Lists agents and their status to the Manager API.
01405  * It is registered on load_module() and it gets called by the manager backend.
01406  * \param s
01407  * \param m
01408  * \returns 
01409  * \sa action_agent_logoff(), action_agent_callback_login(), load_module().
01410  */
01411 static int action_agents(struct mansession *s, const struct message *m)
01412 {
01413    const char *id = astman_get_header(m,"ActionID");
01414    char idText[256] = "";
01415    char chanbuf[256];
01416    struct agent_pvt *p;
01417    char *username = NULL;
01418    char *loginChan = NULL;
01419    char *talkingtoChan = NULL;
01420    char *status = NULL;
01421 
01422    if (!ast_strlen_zero(id))
01423       snprintf(idText, sizeof(idText) ,"ActionID: %s\r\n", id);
01424    astman_send_ack(s, m, "Agents will follow");
01425    AST_LIST_LOCK(&agents);
01426    AST_LIST_TRAVERSE(&agents, p, list) {
01427          ast_mutex_lock(&p->lock);
01428 
01429       /* Status Values:
01430          AGENT_LOGGEDOFF - Agent isn't logged in
01431          AGENT_IDLE      - Agent is logged in, and waiting for call
01432          AGENT_ONCALL    - Agent is logged in, and on a call
01433          AGENT_UNKNOWN   - Don't know anything about agent. Shouldn't ever get this. */
01434 
01435       username = S_OR(p->name, "None");
01436 
01437       /* Set a default status. It 'should' get changed. */
01438       status = "AGENT_UNKNOWN";
01439 
01440       if (!ast_strlen_zero(p->loginchan) && !p->chan) {
01441          loginChan = p->loginchan;
01442          talkingtoChan = "n/a";
01443          status = "AGENT_IDLE";
01444          if (p->acknowledged) {
01445             snprintf(chanbuf, sizeof(chanbuf), " %s (Confirmed)", p->loginchan);
01446             loginChan = chanbuf;
01447          }
01448       } else if (p->chan) {
01449          loginChan = ast_strdupa(p->chan->name);
01450          if (p->owner && p->owner->_bridge) {
01451                talkingtoChan = p->chan->cid.cid_num;
01452                status = "AGENT_ONCALL";
01453          } else {
01454                talkingtoChan = "n/a";
01455                status = "AGENT_IDLE";
01456          }
01457       } else {
01458          loginChan = "n/a";
01459          talkingtoChan = "n/a";
01460          status = "AGENT_LOGGEDOFF";
01461       }
01462 
01463       astman_append(s, "Event: Agents\r\n"
01464          "Agent: %s\r\n"
01465          "Name: %s\r\n"
01466          "Status: %s\r\n"
01467          "LoggedInChan: %s\r\n"
01468          "LoggedInTime: %d\r\n"
01469          "TalkingTo: %s\r\n"
01470          "%s"
01471          "\r\n",
01472          p->agent, username, status, loginChan, (int)p->loginstart, talkingtoChan, idText);
01473       ast_mutex_unlock(&p->lock);
01474    }
01475    AST_LIST_UNLOCK(&agents);
01476    astman_append(s, "Event: AgentsComplete\r\n"
01477       "%s"
01478       "\r\n",idText);
01479    return 0;
01480 }
01481 
01482 static void agent_logoff_maintenance(struct agent_pvt *p, char *loginchan, long logintime, const char *uniqueid, char *logcommand)
01483 {
01484    char *tmp = NULL;
01485    char agent[AST_MAX_AGENT];
01486 
01487    if (!ast_strlen_zero(logcommand))
01488       tmp = logcommand;
01489    else
01490       tmp = ast_strdupa("");
01491 
01492    snprintf(agent, sizeof(agent), "Agent/%s", p->agent);
01493 
01494    if (!ast_strlen_zero(uniqueid)) {
01495       manager_event(EVENT_FLAG_AGENT, "Agentcallbacklogoff",
01496             "Agent: %s\r\n"
01497             "Reason: %s\r\n"
01498             "Loginchan: %s\r\n"
01499             "Logintime: %ld\r\n"
01500             "Uniqueid: %s\r\n", 
01501             p->agent, tmp, loginchan, logintime, uniqueid);
01502    } else {
01503       manager_event(EVENT_FLAG_AGENT, "Agentcallbacklogoff",
01504             "Agent: %s\r\n"
01505             "Reason: %s\r\n"
01506             "Loginchan: %s\r\n"
01507             "Logintime: %ld\r\n",
01508             p->agent, tmp, loginchan, logintime);
01509    }
01510 
01511    ast_queue_log("NONE", ast_strlen_zero(uniqueid) ? "NONE" : uniqueid, agent, "AGENTCALLBACKLOGOFF", "%s|%ld|%s", loginchan, logintime, tmp);
01512    set_agentbycallerid(p->logincallerid, NULL);
01513    p->loginchan[0] ='\0';
01514    p->logincallerid[0] = '\0';
01515    ast_device_state_changed("Agent/%s", p->agent);
01516    if (persistent_agents)
01517       dump_agents(); 
01518 
01519 }
01520 
01521 static int agent_logoff(const char *agent, int soft)
01522 {
01523    struct agent_pvt *p;
01524    long logintime;
01525    int ret = -1; /* Return -1 if no agent if found */
01526 
01527    AST_LIST_TRAVERSE(&agents, p, list) {
01528       if (!strcasecmp(p->agent, agent)) {
01529          ret = 0;
01530          if (p->owner || p->chan) {
01531             if (!soft) {
01532                if (p->owner)
01533                   ast_softhangup(p->owner, AST_SOFTHANGUP_EXPLICIT);
01534                if (p->chan)
01535                   ast_softhangup(p->chan, AST_SOFTHANGUP_EXPLICIT);
01536             } else
01537                p->deferlogoff = 1;
01538          } else {
01539             logintime = time(NULL) - p->loginstart;
01540             p->loginstart = 0;
01541             agent_logoff_maintenance(p, p->loginchan, logintime, NULL, "CommandLogoff");
01542          }
01543          break;
01544       }
01545    }
01546 
01547    return ret;
01548 }
01549 
01550 static int agent_logoff_cmd(int fd, int argc, char **argv)
01551 {
01552    int ret;
01553    char *agent;
01554 
01555    if (argc < 3 || argc > 4)
01556       return RESULT_SHOWUSAGE;
01557    if (argc == 4 && strcasecmp(argv[3], "soft"))
01558       return RESULT_SHOWUSAGE;
01559 
01560    agent = argv[2] + 6;
01561    ret = agent_logoff(agent, argc == 4);
01562    if (ret == 0)
01563       ast_cli(fd, "Logging out %s\n", agent);
01564 
01565    return RESULT_SUCCESS;
01566 }
01567 
01568 /*!
01569  * Sets an agent as no longer logged in in the Manager API.
01570  * It is registered on load_module() and it gets called by the manager backend.
01571  * \param s
01572  * \param m
01573  * \returns 
01574  * \sa action_agents(), action_agent_callback_login(), load_module().
01575  */
01576 static int action_agent_logoff(struct mansession *s, const struct message *m)
01577 {
01578    const char *agent = astman_get_header(m, "Agent");
01579    const char *soft_s = astman_get_header(m, "Soft"); /* "true" is don't hangup */
01580    int soft;
01581    int ret; /* return value of agent_logoff */
01582 
01583    if (ast_strlen_zero(agent)) {
01584       astman_send_error(s, m, "No agent specified");
01585       return 0;
01586    }
01587 
01588    soft = ast_true(soft_s) ? 1 : 0;
01589    ret = agent_logoff(agent, soft);
01590    if (ret == 0)
01591       astman_send_ack(s, m, "Agent logged out");
01592    else
01593       astman_send_error(s, m, "No such agent");
01594 
01595    return 0;
01596 }
01597 
01598 static char *complete_agent_logoff_cmd(const char *line, const char *word, int pos, int state)
01599 {
01600    if (pos == 2) {
01601       struct agent_pvt *p;
01602       char name[AST_MAX_AGENT];
01603       int which = 0, len = strlen(word);
01604 
01605       AST_LIST_TRAVERSE(&agents, p, list) {
01606          snprintf(name, sizeof(name), "Agent/%s", p->agent);
01607          if (!strncasecmp(word, name, len) && ++which > state)
01608             return ast_strdup(name);
01609       }
01610    } else if (pos == 3 && state == 0) 
01611       return ast_strdup("soft");
01612    
01613    return NULL;
01614 }
01615 
01616 /*!
01617  * Show agents in cli.
01618  */
01619 static int agents_show(int fd, int argc, char **argv)
01620 {
01621    struct agent_pvt *p;
01622    char username[AST_MAX_BUF];
01623    char location[AST_MAX_BUF] = "";
01624    char talkingto[AST_MAX_BUF] = "";
01625    char moh[AST_MAX_BUF];
01626    int count_agents = 0;      /*!< Number of agents configured */
01627    int online_agents = 0;     /*!< Number of online agents */
01628    int offline_agents = 0;    /*!< Number of offline agents */
01629    if (argc != 2)
01630       return RESULT_SHOWUSAGE;
01631    AST_LIST_LOCK(&agents);
01632    AST_LIST_TRAVERSE(&agents, p, list) {
01633       ast_mutex_lock(&p->lock);
01634       if (p->pending) {
01635          if (p->group)
01636             ast_cli(fd, "-- Pending call to group %d\n", powerof(p->group));
01637          else
01638             ast_cli(fd, "-- Pending call to agent %s\n", p->agent);
01639       } else {
01640          if (!ast_strlen_zero(p->name))
01641             snprintf(username, sizeof(username), "(%s) ", p->name);
01642          else
01643             username[0] = '\0';
01644          if (p->chan) {
01645             snprintf(location, sizeof(location), "logged in on %s", p->chan->name);
01646             if (p->owner && ast_bridged_channel(p->owner))
01647                snprintf(talkingto, sizeof(talkingto), " talking to %s", ast_bridged_channel(p->owner)->name);
01648              else 
01649                strcpy(talkingto, " is idle");
01650             online_agents++;
01651          } else if (!ast_strlen_zero(p->loginchan)) {
01652             if (ast_tvdiff_ms(ast_tvnow(), p->lastdisc) > 0 || !(p->lastdisc.tv_sec)) 
01653                snprintf(location, sizeof(location) - 20, "available at '%s'", p->loginchan);
01654             else 
01655                snprintf(location, sizeof(location) - 20, "wrapping up at '%s'", p->loginchan);
01656             talkingto[0] = '\0';
01657             online_agents++;
01658             if (p->acknowledged)
01659                strncat(location, " (Confirmed)", sizeof(location) - strlen(location) - 1);
01660          } else {
01661             strcpy(location, "not logged in");
01662             talkingto[0] = '\0';
01663             offline_agents++;
01664          }
01665          if (!ast_strlen_zero(p->moh))
01666             snprintf(moh, sizeof(moh), " (musiconhold is '%s')", p->moh);
01667          ast_cli(fd, "%-12.12s %s%s%s%s\n", p->agent, 
01668             username, location, talkingto, moh);
01669          count_agents++;
01670       }
01671       ast_mutex_unlock(&p->lock);
01672    }
01673    AST_LIST_UNLOCK(&agents);
01674    if ( !count_agents ) 
01675       ast_cli(fd, "No Agents are configured in %s\n",config);
01676    else 
01677       ast_cli(fd, "%d agents configured [%d online , %d offline]\n",count_agents, online_agents, offline_agents);
01678    ast_cli(fd, "\n");
01679                    
01680    return RESULT_SUCCESS;
01681 }
01682 
01683 
01684 static int agents_show_online(int fd, int argc, char **argv)
01685 {
01686    struct agent_pvt *p;
01687    char username[AST_MAX_BUF];
01688    char location[AST_MAX_BUF] = "";
01689    char talkingto[AST_MAX_BUF] = "";
01690    char moh[AST_MAX_BUF];
01691    int count_agents = 0;           /* Number of agents configured */
01692    int online_agents = 0;          /* Number of online agents */
01693    int agent_status = 0;           /* 0 means offline, 1 means online */
01694    if (argc != 3)
01695       return RESULT_SHOWUSAGE;
01696    AST_LIST_LOCK(&agents);
01697    AST_LIST_TRAVERSE(&agents, p, list) {
01698       agent_status = 0;       /* reset it to offline */
01699       ast_mutex_lock(&p->lock);
01700       if (!ast_strlen_zero(p->name))
01701          snprintf(username, sizeof(username), "(%s) ", p->name);
01702       else
01703          username[0] = '\0';
01704       if (p->chan) {
01705          snprintf(location, sizeof(location), "logged in on %s", p->chan->name);
01706          if (p->owner && ast_bridged_channel(p->owner)) 
01707             snprintf(talkingto, sizeof(talkingto), " talking to %s", ast_bridged_channel(p->owner)->name);
01708          else 
01709             strcpy(talkingto, " is idle");
01710          agent_status = 1;
01711          online_agents++;
01712       } else if (!ast_strlen_zero(p->loginchan)) {
01713          snprintf(location, sizeof(location) - 20, "available at '%s'", p->loginchan);
01714          talkingto[0] = '\0';
01715          agent_status = 1;
01716          online_agents++;
01717          if (p->acknowledged)
01718             strncat(location, " (Confirmed)", sizeof(location) - strlen(location) - 1);
01719       }
01720       if (!ast_strlen_zero(p->moh))
01721          snprintf(moh, sizeof(moh), " (musiconhold is '%s')", p->moh);
01722       if (agent_status)
01723          ast_cli(fd, "%-12.12s %s%s%s%s\n", p->agent, username, location, talkingto, moh);
01724       count_agents++;
01725       ast_mutex_unlock(&p->lock);
01726    }
01727    AST_LIST_UNLOCK(&agents);
01728    if (!count_agents) 
01729       ast_cli(fd, "No Agents are configured in %s\n", config);
01730    else
01731       ast_cli(fd, "%d agents online\n", online_agents);
01732    ast_cli(fd, "\n");
01733    return RESULT_SUCCESS;
01734 }
01735 
01736 
01737 
01738 static char show_agents_usage[] = 
01739 "Usage: agent show\n"
01740 "       Provides summary information on agents.\n";
01741 
01742 static char show_agents_online_usage[] =
01743 "Usage: agent show online\n"
01744 "  Provides a list of all online agents.\n";
01745 
01746 static char agent_logoff_usage[] =
01747 "Usage: agent logoff <channel> [soft]\n"
01748 "       Sets an agent as no longer logged in.\n"
01749 "       If 'soft' is specified, do not hangup existing calls.\n";
01750 
01751 static struct ast_cli_entry cli_show_agents_deprecated = {
01752    { "show", "agents", NULL },
01753    agents_show, NULL,
01754    NULL, NULL };
01755 
01756 static struct ast_cli_entry cli_show_agents_online_deprecated = {
01757    { "show", "agents", "online" },
01758    agents_show_online, NULL,
01759    NULL, NULL };
01760 
01761 static struct ast_cli_entry cli_agents[] = {
01762    { { "agent", "show", NULL },
01763    agents_show, "Show status of agents",
01764    show_agents_usage, NULL, &cli_show_agents_deprecated },
01765 
01766    { { "agent", "show", "online" },
01767    agents_show_online, "Show all online agents",
01768    show_agents_online_usage, NULL, &cli_show_agents_online_deprecated },
01769 
01770    { { "agent", "logoff", NULL },
01771    agent_logoff_cmd, "Sets an agent offline",
01772    agent_logoff_usage, complete_agent_logoff_cmd },
01773 };
01774 
01775 /*!
01776  * \brief Log in agent application.
01777  *
01778  * \param chan
01779  * \param data
01780  * \param callbackmode non-zero for AgentCallbackLogin
01781  */
01782 static int __login_exec(struct ast_channel *chan, void *data, int callbackmode)
01783 {
01784    int res=0;
01785    int tries = 0;
01786    int max_login_tries = maxlogintries;
01787    struct agent_pvt *p;
01788    struct ast_module_user *u;
01789    int login_state = 0;
01790    char user[AST_MAX_AGENT] = "";
01791    char pass[AST_MAX_AGENT];
01792    char agent[AST_MAX_AGENT] = "";
01793    char xpass[AST_MAX_AGENT] = "";
01794    char *errmsg;
01795    char *parse;
01796    AST_DECLARE_APP_ARGS(args,
01797               AST_APP_ARG(agent_id);
01798               AST_APP_ARG(options);
01799               AST_APP_ARG(extension);
01800       );
01801    const char *tmpoptions = NULL;
01802    char *context = NULL;
01803    int play_announcement = 1;
01804    char agent_goodbye[AST_MAX_FILENAME_LEN];
01805    int update_cdr = updatecdr;
01806    char *filename = "agent-loginok";
01807    char tmpchan[AST_MAX_BUF] = "";
01808 
01809    u = ast_module_user_add(chan);
01810 
01811    parse = ast_strdupa(data);
01812 
01813    AST_STANDARD_APP_ARGS(args, parse);
01814 
01815    ast_copy_string(agent_goodbye, agentgoodbye, sizeof(agent_goodbye));
01816 
01817    /* Set Channel Specific Login Overrides */
01818    if (pbx_builtin_getvar_helper(chan, "AGENTLMAXLOGINTRIES") && strlen(pbx_builtin_getvar_helper(chan, "AGENTLMAXLOGINTRIES"))) {
01819       max_login_tries = atoi(pbx_builtin_getvar_helper(chan, "AGENTMAXLOGINTRIES"));
01820       if (max_login_tries < 0)
01821          max_login_tries = 0;
01822       tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTMAXLOGINTRIES");
01823       if (option_verbose > 2)
01824          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);
01825    }
01826    if (pbx_builtin_getvar_helper(chan, "AGENTUPDATECDR") && !ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENTUPDATECDR"))) {
01827       if (ast_true(pbx_builtin_getvar_helper(chan, "AGENTUPDATECDR")))
01828          update_cdr = 1;
01829       else
01830          update_cdr = 0;
01831       tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTUPDATECDR");
01832       if (option_verbose > 2)
01833          ast_verbose(VERBOSE_PREFIX_3 "Saw variable AGENTUPDATECDR=%s, setting update_cdr to: %d on Channel '%s'.\n",tmpoptions,update_cdr,chan->name);
01834    }
01835    if (pbx_builtin_getvar_helper(chan, "AGENTGOODBYE") && !ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENTGOODBYE"))) {
01836       strcpy(agent_goodbye, pbx_builtin_getvar_helper(chan, "AGENTGOODBYE"));
01837       tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTGOODBYE");
01838       if (option_verbose > 2)
01839          ast_verbose(VERBOSE_PREFIX_3 "Saw variable AGENTGOODBYE=%s, setting agent_goodbye to: %s on Channel '%s'.\n",tmpoptions,agent_goodbye,chan->name);
01840    }
01841    /* End Channel Specific Login Overrides */
01842    
01843    if (callbackmode && args.extension) {
01844       parse = args.extension;
01845       args.extension = strsep(&parse, "@");
01846       context = parse;
01847    }
01848 
01849    if (!ast_strlen_zero(args.options)) {
01850       if (strchr(args.options, 's')) {
01851          play_announcement = 0;
01852       }
01853    }
01854 
01855    if (chan->_state != AST_STATE_UP)
01856       res = ast_answer(chan);
01857    if (!res) {
01858       if (!ast_strlen_zero(args.agent_id))
01859          ast_copy_string(user, args.agent_id, AST_MAX_AGENT);
01860       else
01861          res = ast_app_getdata(chan, "agent-user", user, sizeof(user) - 1, 0);
01862    }
01863    while (!res && (max_login_tries==0 || tries < max_login_tries)) {
01864       tries++;
01865       /* Check for password */
01866       AST_LIST_LOCK(&agents);
01867       AST_LIST_TRAVERSE(&agents, p, list) {
01868          if (!strcmp(p->agent, user) && !p->pending)
01869             ast_copy_string(xpass, p->password, sizeof(xpass));
01870       }
01871       AST_LIST_UNLOCK(&agents);
01872       if (!res) {
01873          if (!ast_strlen_zero(xpass))
01874             res = ast_app_getdata(chan, "agent-pass", pass, sizeof(pass) - 1, 0);
01875          else
01876             pass[0] = '\0';
01877       }
01878       errmsg = "agent-incorrect";
01879 
01880 #if 0
01881       ast_log(LOG_NOTICE, "user: %s, pass: %s\n", user, pass);
01882 #endif      
01883 
01884       /* Check again for accuracy */
01885       AST_LIST_LOCK(&agents);
01886       AST_LIST_TRAVERSE(&agents, p, list) {
01887          ast_mutex_lock(&p->lock);
01888          if (!strcmp(p->agent, user) &&
01889              !strcmp(p->password, pass) && !p->pending) {
01890             login_state = 1; /* Successful Login */
01891 
01892             /* Ensure we can't be gotten until we're done */
01893             gettimeofday(&p->lastdisc, NULL);
01894             p->lastdisc.tv_sec++;
01895 
01896             /* Set Channel Specific Agent Overrides */
01897             if (pbx_builtin_getvar_helper(chan, "AGENTACKCALL") && strlen(pbx_builtin_getvar_helper(chan, "AGENTACKCALL"))) {
01898                if (!strcasecmp(pbx_builtin_getvar_helper(chan, "AGENTACKCALL"), "always"))
01899                   p->ackcall = 2;
01900                else if (ast_true(pbx_builtin_getvar_helper(chan, "AGENTACKCALL")))
01901                   p->ackcall = 1;
01902                else
01903                   p->ackcall = 0;
01904                tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTACKCALL");
01905                if (option_verbose > 2)
01906                   ast_verbose(VERBOSE_PREFIX_3 "Saw variable AGENTACKCALL=%s, setting ackcall to: %d for Agent '%s'.\n",tmpoptions,p->ackcall,p->agent);
01907             }
01908             if (pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF") && strlen(pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF"))) {
01909                p->autologoff = atoi(pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF"));
01910                if (p->autologoff < 0)
01911                   p->autologoff = 0;
01912                tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF");
01913                if (option_verbose > 2)
01914                   ast_verbose(VERBOSE_PREFIX_3 "Saw variable AGENTAUTOLOGOFF=%s, setting autologff to: %d for Agent '%s'.\n",tmpoptions,p->autologoff,p->agent);
01915             }
01916             if (pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME") && strlen(pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME"))) {
01917                p->wrapuptime = atoi(pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME"));
01918                if (p->wrapuptime < 0)
01919                   p->wrapuptime = 0;
01920                tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME");
01921                if (option_verbose > 2)
01922                   ast_verbose(VERBOSE_PREFIX_3 "Saw variable AGENTWRAPUPTIME=%s, setting wrapuptime to: %d for Agent '%s'.\n",tmpoptions,p->wrapuptime,p->agent);
01923             }
01924             /* End Channel Specific Agent Overrides */
01925             if (!p->chan) {
01926                char last_loginchan[80] = "";
01927                long logintime;
01928                snprintf(agent, sizeof(agent), "Agent/%s", p->agent);
01929 
01930                if (callbackmode) {
01931                   int pos = 0;
01932                   /* Retrieve login chan */
01933                   for (;;) {
01934                      if (!ast_strlen_zero(args.extension)) {
01935                         ast_copy_string(tmpchan, args.extension, sizeof(tmpchan));
01936                         res = 0;
01937                      } else
01938                         res = ast_app_getdata(chan, "agent-newlocation", tmpchan+pos, sizeof(tmpchan) - 2, 0);
01939                      if (ast_strlen_zero(tmpchan) )
01940                         break;
01941                      if(ast_exists_extension(chan, S_OR(context,"default"), tmpchan,1, NULL) ) {
01942                         if(!allow_multiple_login(tmpchan,context) ) {
01943                            args.extension = NULL;
01944                            pos = 0;
01945                         } else
01946                            break;
01947                      }
01948                      if (args.extension) {
01949                         ast_log(LOG_WARNING, "Extension '%s' is not valid for automatic login of agent '%s'\n", args.extension, p->agent);
01950                         args.extension = NULL;
01951                         pos = 0;
01952                      } else {
01953                         ast_log(LOG_WARNING, "Extension '%s@%s' is not valid for automatic login of agent '%s'\n", tmpchan, S_OR(context, "default"), p->agent);
01954                         res = ast_streamfile(chan, "invalid", chan->language);
01955                         if (!res)
01956                            res = ast_waitstream(chan, AST_DIGIT_ANY);
01957                         if (res > 0) {
01958                            tmpchan[0] = res;
01959                            tmpchan[1] = '\0';
01960                            pos = 1;
01961                         } else {
01962                            tmpchan[0] = '\0';
01963                            pos = 0;
01964                         }
01965                      }
01966                   }
01967                   args.extension = tmpchan;
01968                   if (!res) {
01969                      set_agentbycallerid(p->logincallerid, NULL);
01970                      if (!ast_strlen_zero(context) && !ast_strlen_zero(tmpchan))
01971                         snprintf(p->loginchan, sizeof(p->loginchan), "%s@%s", tmpchan, context);
01972                      else {
01973                         ast_copy_string(last_loginchan, p->loginchan, sizeof(last_loginchan));
01974                         ast_copy_string(p->loginchan, tmpchan, sizeof(p->loginchan));
01975                      }
01976                      p->acknowledged = 0;
01977                      if (ast_strlen_zero(p->loginchan)) {
01978                         login_state = 2;
01979                         filename = "agent-loggedoff";
01980                      } else {
01981                         if (chan->cid.cid_num) {
01982                            ast_copy_string(p->logincallerid, chan->cid.cid_num, sizeof(p->logincallerid));
01983                            set_agentbycallerid(p->logincallerid, p->agent);
01984                         } else
01985                            p->logincallerid[0] = '\0';
01986                      }
01987 
01988                      if(update_cdr && chan->cdr)
01989                         snprintf(chan->cdr->channel, sizeof(chan->cdr->channel), "Agent/%s", p->agent);
01990 
01991                   }
01992                } else {
01993                   p->loginchan[0] = '\0';
01994                   p->logincallerid[0] = '\0';
01995                   p->acknowledged = 0;
01996                }
01997                ast_mutex_unlock(&p->lock);
01998                AST_LIST_UNLOCK(&agents);
01999                if( !res && play_announcement==1 )
02000                   res = ast_streamfile(chan, filename, chan->language);
02001                if (!res)
02002                   ast_waitstream(chan, "");
02003                AST_LIST_LOCK(&agents);
02004                ast_mutex_lock(&p->lock);
02005                if (!res) {
02006                   res = ast_set_read_format(chan, ast_best_codec(chan->nativeformats));
02007                   if (res)
02008                      ast_log(LOG_WARNING, "Unable to set read format to %d\n", ast_best_codec(chan->nativeformats));
02009                }
02010                if (!res) {
02011                   res = ast_set_write_format(chan, ast_best_codec(chan->nativeformats));
02012                   if (res)
02013                      ast_log(LOG_WARNING, "Unable to set write format to %d\n", ast_best_codec(chan->nativeformats));
02014                }
02015                /* Check once more just in case */
02016                if (p->chan)
02017                   res = -1;
02018                if (callbackmode && !res) {
02019                   /* Just say goodbye and be done with it */
02020                   if (!ast_strlen_zero(p->loginchan)) {
02021                      if (p->loginstart == 0)
02022                         time(&p->loginstart);
02023                      manager_event(EVENT_FLAG_AGENT, "Agentcallbacklogin",
02024                               "Agent: %s\r\n"
02025                               "Loginchan: %s\r\n"
02026                               "Uniqueid: %s\r\n",
02027                               p->agent, p->loginchan, chan->uniqueid);
02028                      ast_queue_log("NONE", chan->uniqueid, agent, "AGENTCALLBACKLOGIN", "%s", p->loginchan);
02029                      if (option_verbose > 1)
02030                         ast_verbose(VERBOSE_PREFIX_2 "Callback Agent '%s' logged in on %s\n", p->agent, p->loginchan);
02031                      ast_device_state_changed("Agent/%s", p->agent);
02032                      if (persistent_agents)
02033                         dump_agents();
02034                   } else {
02035                      logintime = time(NULL) - p->loginstart;
02036                      p->loginstart = 0;
02037 
02038                      agent_logoff_maintenance(p, last_loginchan, logintime, chan->uniqueid, NULL);
02039                      if (option_verbose > 1)
02040                         ast_verbose(VERBOSE_PREFIX_2 "Callback Agent '%s' logged out\n", p->agent);
02041                   }
02042                   AST_LIST_UNLOCK(&agents);
02043                   if (!res)
02044                      res = ast_safe_sleep(chan, 500);
02045                   ast_mutex_unlock(&p->lock);
02046                } else if (!res) {
02047                   ast_indicate_data(chan, AST_CONTROL_HOLD, 
02048                      S_OR(p->moh, NULL), 
02049                      !ast_strlen_zero(p->moh) ? strlen(p->moh) + 1 : 0);
02050                   if (p->loginstart == 0)
02051                      time(&p->loginstart);
02052                   manager_event(EVENT_FLAG_AGENT, "Agentlogin",
02053                            "Agent: %s\r\n"
02054                            "Channel: %s\r\n"
02055                            "Uniqueid: %s\r\n",
02056                            p->agent, chan->name, chan->uniqueid);
02057                   if (update_cdr && chan->cdr)
02058                      snprintf(chan->cdr->channel, sizeof(chan->cdr->channel), "Agent/%s", p->agent);
02059                   ast_queue_log("NONE", chan->uniqueid, agent, "AGENTLOGIN", "%s", chan->name);
02060                   if (option_verbose > 1)
02061                      ast_verbose(VERBOSE_PREFIX_2 "Agent '%s' logged in (format %s/%s)\n", p->agent,
02062                             ast_getformatname(chan->readformat), ast_getformatname(chan->writeformat));
02063                   /* Login this channel and wait for it to go away */
02064                   p->chan = chan;
02065                   if (p->ackcall > 1)
02066                      check_beep(p, 0);
02067                   else
02068                      check_availability(p, 0);
02069                   ast_mutex_unlock(&p->lock);
02070                   AST_LIST_UNLOCK(&agents);
02071                   ast_device_state_changed("Agent/%s", p->agent);
02072                   while (res >= 0) {
02073                      ast_mutex_lock(&p->lock);
02074                      if (p->deferlogoff && p->chan) {
02075                         ast_softhangup(p->chan, AST_SOFTHANGUP_EXPLICIT);
02076                         p->deferlogoff = 0;
02077                      }
02078                      if (p->chan != chan)
02079                         res = -1;
02080                      ast_mutex_unlock(&p->lock);
02081                      /* Yield here so other interested threads can kick in. */
02082                      sched_yield();
02083                      if (res)
02084                         break;
02085 
02086                      AST_LIST_LOCK(&agents);
02087                      ast_mutex_lock(&p->lock);
02088                      if (p->lastdisc.tv_sec) {
02089                         if (ast_tvdiff_ms(ast_tvnow(), p->lastdisc) > 0) {
02090                            if (option_debug)
02091                               ast_log(LOG_DEBUG, "Wrapup time for %s expired!\n", p->agent);
02092                            p->lastdisc = ast_tv(0, 0);
02093                            if (p->ackcall > 1)
02094                               check_beep(p, 0);
02095                            else
02096                               check_availability(p, 0);
02097                         }
02098                      }
02099                      ast_mutex_unlock(&p->lock);
02100                      AST_LIST_UNLOCK(&agents);
02101                      /* Synchronize channel ownership between call to agent and itself. */
02102                      ast_mutex_lock( &p->app_lock );
02103                      ast_mutex_lock(&p->lock);
02104                      p->owning_app = pthread_self();
02105                      ast_mutex_unlock(&p->lock);
02106                      if (p->ackcall > 1) 
02107                         res = agent_ack_sleep(p);
02108                      else
02109                         res = ast_safe_sleep_conditional( chan, 1000, agent_cont_sleep, p );
02110                      ast_mutex_unlock( &p->app_lock );
02111                      if ((p->ackcall > 1)  && (res == 1)) {
02112                         AST_LIST_LOCK(&agents);
02113                         ast_mutex_lock(&p->lock);
02114                         check_availability(p, 0);
02115                         ast_mutex_unlock(&p->lock);
02116                         AST_LIST_UNLOCK(&agents);
02117                         res = 0;
02118                      }
02119                      sched_yield();
02120                   }
02121                   ast_mutex_lock(&p->lock);
02122                   if (res && p->owner) 
02123                      ast_log(LOG_WARNING, "Huh?  We broke out when there was still an owner?\n");
02124                   /* Log us off if appropriate */
02125                   if (p->chan == chan)
02126                      p->chan = NULL;
02127                   p->acknowledged = 0;
02128                   logintime = time(NULL) - p->loginstart;
02129                   p->loginstart = 0;
02130                   ast_mutex_unlock(&p->lock);
02131                   manager_event(EVENT_FLAG_AGENT, "Agentlogoff",
02132                            "Agent: %s\r\n"
02133                            "Logintime: %ld\r\n"
02134                            "Uniqueid: %s\r\n",
02135                            p->agent, logintime, chan->uniqueid);
02136                   ast_queue_log("NONE", chan->uniqueid, agent, "AGENTLOGOFF", "%s|%ld", chan->name, logintime);
02137                   if (option_verbose > 1)
02138                      ast_verbose(VERBOSE_PREFIX_2 "Agent '%s' logged out\n", p->agent);
02139                   /* If there is no owner, go ahead and kill it now */
02140                   ast_device_state_changed("Agent/%s", p->agent);
02141                   if (p->dead && !p->owner) {
02142                      ast_mutex_destroy(&p->lock);
02143                      ast_mutex_destroy(&p->app_lock);
02144                      free(p);
02145                   }
02146                }
02147                else {
02148                   ast_mutex_unlock(&p->lock);
02149                   p = NULL;
02150                }
02151                res = -1;
02152             } else {
02153                ast_mutex_unlock(&p->lock);
02154                errmsg = "agent-alreadyon";
02155                p = NULL;
02156             }
02157             break;
02158          }
02159          ast_mutex_unlock(&p->lock);
02160       }
02161       if (!p)
02162          AST_LIST_UNLOCK(&agents);
02163 
02164       if (!res && (max_login_tries==0 || tries < max_login_tries))
02165          res = ast_app_getdata(chan, errmsg, user, sizeof(user) - 1, 0);
02166    }
02167       
02168    if (!res)
02169       res = ast_safe_sleep(chan, 500);
02170 
02171    /* AgentLogin() exit */
02172    if (!callbackmode) {
02173       ast_module_user_remove(u);
02174       return -1;
02175    } else { /* AgentCallbackLogin() exit*/
02176       /* Set variables */
02177       if (login_state > 0) {
02178          pbx_builtin_setvar_helper(chan, "AGENTNUMBER", user);
02179          if (login_state==1) {
02180             pbx_builtin_setvar_helper(chan, "AGENTSTATUS", "on");
02181             pbx_builtin_setvar_helper(chan, "AGENTEXTEN", args.extension);
02182          } else 
02183             pbx_builtin_setvar_helper(chan, "AGENTSTATUS", "off");
02184       } else {
02185          pbx_builtin_setvar_helper(chan, "AGENTSTATUS", "fail");
02186       }
02187       if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 1, chan->cid.cid_num)) {
02188          ast_module_user_remove(u);
02189          return 0;
02190       }
02191       /* Do we need to play agent-goodbye now that we will be hanging up? */
02192       if (play_announcement) {
02193          if (!res)
02194             res = ast_safe_sleep(chan, 1000);
02195          res = ast_streamfile(chan, agent_goodbye, chan->language);
02196          if (!res)
02197             res = ast_waitstream(chan, "");
02198          if (!res)
02199             res = ast_safe_sleep(chan, 1000);
02200       }
02201    }
02202 
02203    ast_module_user_remove(u);
02204    
02205    /* We should never get here if next priority exists when in callbackmode */
02206    return -1;
02207 }
02208 
02209 /*!
02210  * Called by the AgentLogin application (from the dial plan).
02211  * 
02212  * \param chan
02213  * \param data
02214  * \returns
02215  * \sa callback_login_exec(), agentmonitoroutgoing_exec(), load_module().
02216  */
02217 static int login_exec(struct ast_channel *chan, void *data)
02218 {
02219    return __login_exec(chan, data, 0);
02220 }
02221 
02222 static void callback_deprecated(void)
02223 {
02224    static int depwarning = 0;
02225 
02226    if (!depwarning) {
02227       depwarning = 1;
02228 
02229       ast_log(LOG_WARNING, "AgentCallbackLogin is deprecated and will be removed in a future release.\n");
02230       ast_log(LOG_WARNING, "See doc/queues-with-callback-members.txt for an example of how to achieve\n");
02231       ast_log(LOG_WARNING, "the same functionality using only dialplan logic.\n");
02232    }
02233 }
02234 
02235 /*!
02236  *  Called by the AgentCallbackLogin application (from the dial plan).
02237  * 
02238  * \param chan
02239  * \param data
02240  * \returns
02241  * \sa login_exec(), agentmonitoroutgoing_exec(), load_module().
02242  */
02243 static int callback_exec(struct ast_channel *chan, void *data)
02244 {
02245    callback_deprecated();
02246 
02247    return __login_exec(chan, data, 1);
02248 }
02249 
02250 /*!
02251  * Sets an agent as logged in by callback in the Manager API.
02252  * It is registered on load_module() and it gets called by the manager backend.
02253  * \param s
02254  * \param m
02255  * \returns 
02256  * \sa action_agents(), action_agent_logoff(), load_module().
02257  */
02258 static int action_agent_callback_login(struct mansession *s, const struct message *m)
02259 {
02260    const char *agent = astman_get_header(m, "Agent");
02261    const char *exten = astman_get_header(m, "Exten");
02262    const char *context = astman_get_header(m, "Context");
02263    const char *wrapuptime_s = astman_get_header(m, "WrapupTime");
02264    const char *ackcall_s = astman_get_header(m, "AckCall");
02265    struct agent_pvt *p;
02266    int login_state = 0;
02267 
02268    callback_deprecated();
02269 
02270    if (ast_strlen_zero(agent)) {
02271       astman_send_error(s, m, "No agent specified");
02272       return 0;
02273    }
02274 
02275    if (ast_strlen_zero(exten)) {
02276       astman_send_error(s, m, "No extension specified");
02277       return 0;
02278    }
02279 
02280    AST_LIST_LOCK(&agents);
02281    AST_LIST_TRAVERSE(&agents, p, list) {
02282       if (strcmp(p->agent, agent) || p->pending) 
02283          continue;
02284       if (p->chan) {
02285          login_state = 2; /* already logged in (and on the phone)*/
02286          break;
02287       }
02288       ast_mutex_lock(&p->lock);
02289       login_state = 1; /* Successful Login */
02290       
02291       if (ast_strlen_zero(context))
02292          ast_copy_string(p->loginchan, exten, sizeof(p->loginchan));
02293       else
02294          snprintf(p->loginchan, sizeof(p->loginchan), "%s@%s", exten, context);
02295 
02296       if (!ast_strlen_zero(wrapuptime_s)) {
02297          p->wrapuptime = atoi(wrapuptime_s);
02298          if (p->wrapuptime < 0)
02299             p->wrapuptime = 0;
02300       }
02301 
02302       if (ast_true(ackcall_s))
02303          p->ackcall = 1;
02304       else
02305          p->ackcall = 0;
02306 
02307       if (p->loginstart == 0)
02308          time(&p->loginstart);
02309       manager_event(EVENT_FLAG_AGENT, "Agentcallbacklogin",
02310                "Agent: %s\r\n"
02311                "Loginchan: %s\r\n",
02312                p->agent, p->loginchan);
02313       ast_queue_log("NONE", "NONE", agent, "AGENTCALLBACKLOGIN", "%s", p->loginchan);
02314       if (option_verbose > 1)
02315          ast_verbose(VERBOSE_PREFIX_2 "Callback Agent '%s' logged in on %s\n", p->agent, p->loginchan);
02316       ast_device_state_changed("Agent/%s", p->agent);
02317       ast_mutex_unlock(&p->lock);
02318       if (persistent_agents)
02319          dump_agents();
02320    }
02321    AST_LIST_UNLOCK(&agents);
02322 
02323    if (login_state == 1)
02324       astman_send_ack(s, m, "Agent logged in");
02325    else if (login_state == 0)
02326       astman_send_error(s, m, "No such agent");
02327    else if (login_state == 2)
02328       astman_send_error(s, m, "Agent already logged in");
02329 
02330    return 0;
02331 }
02332 
02333 /*!
02334  *  \brief Called by the AgentMonitorOutgoing application (from the dial plan).
02335  *
02336  * \param chan
02337  * \param data
02338  * \returns
02339  * \sa login_exec(), callback_login_exec(), load_module().
02340  */
02341 static int agentmonitoroutgoing_exec(struct ast_channel *chan, void *data)
02342 {
02343    int exitifnoagentid = 0;
02344    int nowarnings = 0;
02345    int changeoutgoing = 0;
02346    int res = 0;
02347    char agent[AST_MAX_AGENT];
02348 
02349    if (data) {
02350       if (strchr(data, 'd'))
02351          exitifnoagentid = 1;
02352       if (strchr(data, 'n'))
02353          nowarnings = 1;
02354       if (strchr(data, 'c'))
02355          changeoutgoing = 1;
02356    }
02357    if (chan->cid.cid_num) {
02358       const char *tmp;
02359       char agentvar[AST_MAX_BUF];
02360       snprintf(agentvar, sizeof(agentvar), "%s_%s", GETAGENTBYCALLERID, chan->cid.cid_num);
02361       if ((tmp = pbx_builtin_getvar_helper(NULL, agentvar))) {
02362          struct agent_pvt *p;
02363          ast_copy_string(agent, tmp, sizeof(agent));
02364          AST_LIST_LOCK(&agents);
02365          AST_LIST_TRAVERSE(&agents, p, list) {
02366             if (!strcasecmp(p->agent, tmp)) {
02367                if (changeoutgoing) snprintf(chan->cdr->channel, sizeof(chan->cdr->channel), "Agent/%s", p->agent);
02368                __agent_start_monitoring(chan, p, 1);
02369                break;
02370             }
02371          }
02372          AST_LIST_UNLOCK(&agents);
02373          
02374       } else {
02375          res = -1;
02376          if (!nowarnings)
02377             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);
02378       }
02379    } else {
02380       res = -1;
02381       if (!nowarnings)
02382          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");
02383    }
02384    /* check if there is n + 101 priority */
02385    /*! \todo XXX Needs to check option priorityjump etc etc */
02386    if (res) {
02387       if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 101, chan->cid.cid_num)) {
02388          chan->priority+=100;
02389          if (option_verbose > 2)
02390             ast_verbose(VERBOSE_PREFIX_3 "Going to %d priority because there is no callerid or the agentid cannot be found.\n",chan->priority);
02391       } else if (exitifnoagentid)
02392          return res;
02393    }
02394    return 0;
02395 }
02396 
02397 /*!
02398  * \brief Dump AgentCallbackLogin agents to the ASTdb database for persistence
02399  */
02400 static void dump_agents(void)
02401 {
02402    struct agent_pvt *cur_agent = NULL;
02403    char buf[256];
02404 
02405    AST_LIST_TRAVERSE(&agents, cur_agent, list) {
02406       if (cur_agent->chan)
02407          continue;
02408 
02409       if (!ast_strlen_zero(cur_agent->loginchan)) {
02410          snprintf(buf, sizeof(buf), "%s;%s", cur_agent->loginchan, cur_agent->logincallerid);
02411          if (ast_db_put(pa_family, cur_agent->agent, buf))
02412             ast_log(LOG_WARNING, "failed to create persistent entry in ASTdb for %s!\n", buf);
02413          else if (option_debug)
02414             ast_log(LOG_DEBUG, "Saved Agent: %s on %s\n", cur_agent->agent, cur_agent->loginchan);
02415       } else {
02416          /* Delete -  no agent or there is an error */
02417          ast_db_del(pa_family, cur_agent->agent);
02418       }
02419    }
02420 }
02421 
02422 /*!
02423  * \brief Reload the persistent agents from astdb.
02424  */
02425 static void reload_agents(void)
02426 {
02427    char *agent_num;
02428    struct ast_db_entry *db_tree;
02429    struct ast_db_entry *entry;
02430    struct agent_pvt *cur_agent;
02431    char agent_data[256];
02432    char *parse;
02433    char *agent_chan;
02434    char *agent_callerid;
02435 
02436    db_tree = ast_db_gettree(pa_family, NULL);
02437 
02438    AST_LIST_LOCK(&agents);
02439    for (entry = db_tree; entry; entry = entry->next) {
02440       agent_num = entry->key + strlen(pa_family) + 2;
02441       AST_LIST_TRAVERSE(&agents, cur_agent, list) {
02442          ast_mutex_lock(&cur_agent->lock);
02443          if (strcmp(agent_num, cur_agent->agent) == 0)
02444             break;
02445          ast_mutex_unlock(&cur_agent->lock);
02446       }
02447       if (!cur_agent) {
02448          ast_db_del(pa_family, agent_num);
02449          continue;
02450       } else
02451          ast_mutex_unlock(&cur_agent->lock);
02452       if (!ast_db_get(pa_family, agent_num, agent_data, sizeof(agent_data)-1)) {
02453          if (option_debug)
02454             ast_log(LOG_DEBUG, "Reload Agent from AstDB: %s on %s\n", cur_agent->agent, agent_data);
02455          parse = agent_data;
02456          agent_chan = strsep(&parse, ";");
02457          agent_callerid = strsep(&parse, ";");
02458          ast_copy_string(cur_agent->loginchan, agent_chan, sizeof(cur_agent->loginchan));
02459          if (agent_callerid) {
02460             ast_copy_string(cur_agent->logincallerid, agent_callerid, sizeof(cur_agent->logincallerid));
02461             set_agentbycallerid(cur_agent->logincallerid, cur_agent->agent);
02462          } else
02463             cur_agent->logincallerid[0] = '\0';
02464          if (cur_agent->loginstart == 0)
02465             time(&cur_agent->loginstart);
02466          ast_device_state_changed("Agent/%s", cur_agent->agent);  
02467       }
02468    }
02469    AST_LIST_UNLOCK(&agents);
02470    if (db_tree) {
02471       ast_log(LOG_NOTICE, "Agents successfully reloaded from database.\n");
02472       ast_db_freetree(db_tree);
02473    }
02474 }
02475 
02476 /*! \brief Part of PBX channel interface */
02477 static int agent_devicestate(void *data)
02478 {
02479    struct agent_pvt *p;
02480    char *s;
02481    ast_group_t groupmatch;
02482    int groupoff;
02483    int waitforagent=0;
02484    int res = AST_DEVICE_INVALID;
02485    
02486    s = data;
02487    if ((s[0] == '@') && (sscanf(s + 1, "%d", &groupoff) == 1))
02488       groupmatch = (1 << groupoff);
02489    else if ((s[0] == ':') && (sscanf(s + 1, "%d", &groupoff) == 1)) {
02490       groupmatch = (1 << groupoff);
02491       waitforagent = 1;
02492    } else 
02493       groupmatch = 0;
02494 
02495    /* Check actual logged in agents first */
02496    AST_LIST_LOCK(&agents);
02497    AST_LIST_TRAVERSE(&agents, p, list) {
02498       ast_mutex_lock(&p->lock);
02499       if (!p->pending && ((groupmatch && (p->group & groupmatch)) || !strcmp(data, p->agent))) {
02500          if (p->owner) {
02501             if (res != AST_DEVICE_INUSE)
02502                res = AST_DEVICE_BUSY;
02503          } else {
02504             if (res == AST_DEVICE_BUSY)
02505                res = AST_DEVICE_INUSE;
02506             if (p->chan || !ast_strlen_zero(p->loginchan)) {
02507                if (res == AST_DEVICE_INVALID)
02508                   res = AST_DEVICE_UNKNOWN;
02509             } else if (res == AST_DEVICE_INVALID)  
02510                res = AST_DEVICE_UNAVAILABLE;
02511          }
02512          if (!strcmp(data, p->agent)) {
02513             ast_mutex_unlock(&p->lock);
02514             break;
02515          }
02516       }
02517       ast_mutex_unlock(&p->lock);
02518    }
02519    AST_LIST_UNLOCK(&agents);
02520    return res;
02521 }
02522 
02523 static struct agent_pvt *find_agent(char *agentid)
02524 {
02525    struct agent_pvt *cur;
02526 
02527    AST_LIST_TRAVERSE(&agents, cur, list) {
02528       if (!strcmp(cur->agent, agentid))
02529          break;   
02530    }
02531 
02532    return cur; 
02533 }
02534 
02535 static int function_agent(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
02536 {
02537    char *parse;    
02538    AST_DECLARE_APP_ARGS(args,
02539       AST_APP_ARG(agentid);
02540       AST_APP_ARG(item);
02541    );
02542    char *tmp;
02543    struct agent_pvt *agent;
02544 
02545    buf[0] = '\0';
02546 
02547    if (ast_strlen_zero(data)) {
02548       ast_log(LOG_WARNING, "The AGENT function requires an argument - agentid!\n");
02549       return -1;
02550    }
02551 
02552    parse = ast_strdupa(data);
02553 
02554    AST_NONSTANDARD_APP_ARGS(args, parse, ':');
02555    if (!args.item)
02556       args.item = "status";
02557 
02558    if (!(agent = find_agent(args.agentid))) {
02559       ast_log(LOG_WARNING, "Agent '%s' not found!\n", args.agentid);
02560       return -1;
02561    }
02562 
02563    if (!strcasecmp(args.item, "status")) {
02564       char *status = "LOGGEDOUT";
02565       if (agent->chan || !ast_strlen_zero(agent->loginchan)) 
02566          status = "LOGGEDIN"; 
02567       ast_copy_string(buf, status, len);
02568    } else if (!strcasecmp(args.item, "password")) 
02569       ast_copy_string(buf, agent->password, len);
02570    else if (!strcasecmp(args.item, "name"))
02571       ast_copy_string(buf, agent->name, len);
02572    else if (!strcasecmp(args.item, "mohclass"))
02573       ast_copy_string(buf, agent->moh, len);
02574    else if (!strcasecmp(args.item, "channel")) {
02575       if (agent->chan) {
02576          ast_copy_string(buf, agent->chan->name, len);
02577          tmp = strrchr(buf, '-');
02578          if (tmp)
02579             *tmp = '\0';
02580       } 
02581    } else if (!strcasecmp(args.item, "exten"))
02582       ast_copy_string(buf, agent->loginchan, len); 
02583 
02584    return 0;
02585 }
02586 
02587 struct ast_custom_function agent_function = {
02588    .name = "AGENT",
02589    .synopsis = "Gets information about an Agent",
02590    .syntax = "AGENT(<agentid>[:item])",
02591    .read = function_agent,
02592    .desc = "The valid items to retrieve are:\n"
02593    "- status (default)      The status of the agent\n"
02594    "                          LOGGEDIN | LOGGEDOUT\n"
02595    "- password              The password of the agent\n"
02596    "- name                  The name of the agent\n"
02597    "- mohclass              MusicOnHold class\n"
02598    "- exten                 The callback extension for the Agent (AgentCallbackLogin)\n"
02599    "- channel               The name of the active channel for the Agent (AgentLogin)\n"
02600 };
02601 
02602 
02603 /*!
02604  * \brief Initialize the Agents module.
02605  * This function is being called by Asterisk when loading the module. 
02606  * Among other things it registers applications, cli commands and reads the cofiguration file.
02607  *
02608  * \returns int Always 0.
02609  */
02610 static int load_module(void)
02611 {
02612    /* Make sure we can register our agent channel type */
02613    if (ast_channel_register(&agent_tech)) {
02614       ast_log(LOG_ERROR, "Unable to register channel class 'Agent'\n");
02615       return -1;
02616    }
02617    /* Read in the config */
02618    if (!read_agent_config())
02619       return AST_MODULE_LOAD_DECLINE;
02620    if (persistent_agents)
02621       reload_agents();
02622    /* Dialplan applications */
02623    ast_register_application(app, login_exec, synopsis, descrip);
02624    ast_register_application(app2, callback_exec, synopsis2, descrip2);
02625    ast_register_application(app3, agentmonitoroutgoing_exec, synopsis3, descrip3);
02626 
02627    /* Manager commands */
02628    ast_manager_register2("Agents", EVENT_FLAG_AGENT, action_agents, "Lists agents and their status", mandescr_agents);
02629    ast_manager_register2("AgentLogoff", EVENT_FLAG_AGENT, action_agent_logoff, "Sets an agent as no longer logged in", mandescr_agent_logoff);
02630    ast_manager_register2("AgentCallbackLogin", EVENT_FLAG_AGENT, action_agent_callback_login, "Sets an agent as logged in by callback", mandescr_agent_callback_login);
02631 
02632    /* CLI Commands */
02633    ast_cli_register_multiple(cli_agents, sizeof(cli_agents) / sizeof(struct ast_cli_entry));
02634 
02635    /* Dialplan Functions */
02636    ast_custom_function_register(&agent_function);
02637 
02638    return 0;
02639 }
02640 
02641 static int reload(void)
02642 {
02643    read_agent_config();
02644    if (persistent_agents)
02645       reload_agents();
02646    return 0;
02647 }
02648 
02649 static int unload_module(void)
02650 {
02651    struct agent_pvt *p;
02652    /* First, take us out of the channel loop */
02653    ast_channel_unregister(&agent_tech);
02654    /* Unregister dialplan functions */
02655    ast_custom_function_unregister(&agent_function);   
02656    /* Unregister CLI commands */
02657    ast_cli_unregister_multiple(cli_agents, sizeof(cli_agents) / sizeof(struct ast_cli_entry));
02658    /* Unregister dialplan applications */
02659    ast_unregister_application(app);
02660    ast_unregister_application(app2);
02661    ast_unregister_application(app3);
02662    /* Unregister manager command */
02663    ast_manager_unregister("Agents");
02664    ast_manager_unregister("AgentLogoff");
02665    ast_manager_unregister("AgentCallbackLogin");
02666    /* Unregister channel */
02667    AST_LIST_LOCK(&agents);
02668    /* Hangup all interfaces if they have an owner */
02669    while ((p = AST_LIST_REMOVE_HEAD(&agents, list))) {
02670       if (p->owner)
02671          ast_softhangup(p->owner, AST_SOFTHANGUP_APPUNLOAD);
02672       free(p);
02673    }
02674    AST_LIST_UNLOCK(&agents);
02675    AST_LIST_HEAD_DESTROY(&agents);
02676    return 0;
02677 }
02678 
02679 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Agent Proxy Channel",
02680       .load = load_module,
02681       .unload = unload_module,
02682       .reload = reload,
02683           );

Generated on Wed Aug 15 01:24:16 2007 for Asterisk - the Open Source PBX by  doxygen 1.5.3