Fri Sep 25 19:28:06 2009

Asterisk developer's documentation


chan_agent.c

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

Generated on Fri Sep 25 19:28:06 2009 for Asterisk - the Open Source PBX by  doxygen 1.5.5