Sat Sep 16 05:47:41 2006

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

Generated on Sat Sep 16 05:47:41 2006 for Asterisk - the Open Source PBX by  doxygen 1.4.7