Wed Oct 28 15:47:49 2009

Asterisk developer's documentation


chan_agent.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 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: 211526 $")
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 && !p->chan->generator)  \
00221          ast_set_read_format(p->chan, ast->rawreadformat); \
00222       if (p->chan->writeformat != ast->rawwriteformat && !p->chan->generator) \
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 = p->chan->tech->indicate ? p->chan->tech->indicate(p->chan, condition) : -1;
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          } else if (!p->loginstart) {
00794             p->loginchan[0] = '\0';
00795             p->logincallerid[0] = '\0';
00796             if (persistent_agents)
00797                dump_agents();
00798          }
00799       } else if (p->dead) {
00800          ast_mutex_lock(&p->chan->lock);
00801          ast_softhangup(p->chan, AST_SOFTHANGUP_EXPLICIT);
00802          ast_mutex_unlock(&p->chan->lock);
00803       } else if (p->loginstart) {
00804          ast_mutex_lock(&p->chan->lock);
00805          ast_moh_start(p->chan, p->moh);
00806          ast_mutex_unlock(&p->chan->lock);
00807       }
00808    }
00809    ast_mutex_unlock(&p->lock);
00810 
00811    /* Only register a device state change if the agent is still logged in */
00812    if (!p->loginstart) {
00813       p->loginchan[0] = '\0';
00814       p->logincallerid[0] = '\0';
00815       if (persistent_agents)
00816          dump_agents();
00817    } else {
00818       ast_device_state_changed("Agent/%s", p->agent);
00819    }
00820 
00821    if (p->pending) {
00822       ast_mutex_lock(&agentlock);
00823       agent_unlink(p);
00824       ast_mutex_unlock(&agentlock);
00825    }
00826    if (p->abouttograb) {
00827       /* Let the "about to grab" thread know this isn't valid anymore, and let it
00828          kill it later */
00829       p->abouttograb = 0;
00830    } else if (p->dead) {
00831       ast_mutex_destroy(&p->lock);
00832       ast_mutex_destroy(&p->app_lock);
00833       free(p);
00834    } else {
00835       if (p->chan) {
00836          /* Not dead -- check availability now */
00837          ast_mutex_lock(&p->lock);
00838          /* Store last disconnect time */
00839          p->lastdisc = ast_tvadd(ast_tvnow(), ast_samp2tv(p->wrapuptime, 1000));
00840          ast_mutex_unlock(&p->lock);
00841       }
00842       /* Release ownership of the agent to other threads (presumably running the login app). */
00843       if (ast_strlen_zero(p->loginchan))
00844          ast_mutex_unlock(&p->app_lock);
00845    }
00846    return 0;
00847 }
00848 
00849 static int agent_cont_sleep( void *data )
00850 {
00851    struct agent_pvt *p;
00852    int res;
00853 
00854    p = (struct agent_pvt *)data;
00855 
00856    ast_mutex_lock(&p->lock);
00857    res = p->app_sleep_cond;
00858    if (p->lastdisc.tv_sec) {
00859       if (ast_tvdiff_ms(ast_tvnow(), p->lastdisc) > 0) 
00860          res = 1;
00861    }
00862    ast_mutex_unlock(&p->lock);
00863 #if 0
00864    if( !res )
00865       ast_log( LOG_DEBUG, "agent_cont_sleep() returning %d\n", res );
00866 #endif      
00867    return res;
00868 }
00869 
00870 static int agent_ack_sleep( void *data )
00871 {
00872    struct agent_pvt *p;
00873    int res=0;
00874    int to = 1000;
00875    struct ast_frame *f;
00876 
00877    /* Wait a second and look for something */
00878 
00879    p = (struct agent_pvt *)data;
00880    if (p->chan) {
00881       for(;;) {
00882          to = ast_waitfor(p->chan, to);
00883          if (to < 0) {
00884             res = -1;
00885             break;
00886          }
00887          if (!to) {
00888             res = 0;
00889             break;
00890          }
00891          f = ast_read(p->chan);
00892          if (!f) {
00893             res = -1;
00894             break;
00895          }
00896          if (f->frametype == AST_FRAME_DTMF)
00897             res = f->subclass;
00898          else
00899             res = 0;
00900          ast_frfree(f);
00901          ast_mutex_lock(&p->lock);
00902          if (!p->app_sleep_cond) {
00903             ast_mutex_unlock(&p->lock);
00904             res = 0;
00905             break;
00906          } else if (res == '#') {
00907             ast_mutex_unlock(&p->lock);
00908             res = 1;
00909             break;
00910          }
00911          ast_mutex_unlock(&p->lock);
00912          res = 0;
00913       }
00914    } else
00915       res = -1;
00916    return res;
00917 }
00918 
00919 static struct ast_channel *agent_bridgedchannel(struct ast_channel *chan, struct ast_channel *bridge)
00920 {
00921    struct agent_pvt *p = bridge->tech_pvt;
00922    struct ast_channel *ret=NULL;
00923 
00924    if (p) {
00925       if (chan == p->chan)
00926          ret = bridge->_bridge;
00927       else if (chan == bridge->_bridge)
00928          ret = p->chan;
00929    }
00930 
00931    if (option_debug)
00932       ast_log(LOG_DEBUG, "Asked for bridged channel on '%s'/'%s', returning '%s'\n", chan->name, bridge->name, ret ? ret->name : "<none>");
00933    return ret;
00934 }
00935 
00936 /*--- agent_new: Create new agent channel ---*/
00937 static struct ast_channel *agent_new(struct agent_pvt *p, int state)
00938 {
00939    struct ast_channel *tmp;
00940    struct ast_frame null_frame = { AST_FRAME_NULL };
00941 #if 0
00942    if (!p->chan) {
00943       ast_log(LOG_WARNING, "No channel? :(\n");
00944       return NULL;
00945    }
00946 #endif   
00947    tmp = ast_channel_alloc(0);
00948    if (tmp) {
00949       tmp->tech = &agent_tech;
00950       if (p->chan) {
00951          tmp->nativeformats = p->chan->nativeformats;
00952          tmp->writeformat = p->chan->writeformat;
00953          tmp->rawwriteformat = p->chan->writeformat;
00954          tmp->readformat = p->chan->readformat;
00955          tmp->rawreadformat = p->chan->readformat;
00956          ast_copy_string(tmp->language, p->chan->language, sizeof(tmp->language));
00957          ast_copy_string(tmp->context, p->chan->context, sizeof(tmp->context));
00958          ast_copy_string(tmp->exten, p->chan->exten, sizeof(tmp->exten));
00959       } else {
00960          tmp->nativeformats = AST_FORMAT_SLINEAR;
00961          tmp->writeformat = AST_FORMAT_SLINEAR;
00962          tmp->rawwriteformat = AST_FORMAT_SLINEAR;
00963          tmp->readformat = AST_FORMAT_SLINEAR;
00964          tmp->rawreadformat = AST_FORMAT_SLINEAR;
00965       }
00966       if (p->pending)
00967          snprintf(tmp->name, sizeof(tmp->name), "Agent/P%s-%d", p->agent, rand() & 0xffff);
00968       else
00969          snprintf(tmp->name, sizeof(tmp->name), "Agent/%s", p->agent);
00970       tmp->type = channeltype;
00971       /* Safe, agentlock already held */
00972       ast_setstate(tmp, state);
00973       tmp->tech_pvt = p;
00974       p->owner = tmp;
00975       ast_mutex_lock(&usecnt_lock);
00976       usecnt++;
00977       ast_mutex_unlock(&usecnt_lock);
00978       ast_update_use_count();
00979       tmp->priority = 1;
00980       /* Wake up and wait for other applications (by definition the login app)
00981        * to release this channel). Takes ownership of the agent channel
00982        * to this thread only.
00983        * For signalling the other thread, ast_queue_frame is used until we
00984        * can safely use signals for this purpose. The pselect() needs to be
00985        * implemented in the kernel for this.
00986        */
00987       p->app_sleep_cond = 0;
00988       if( ast_strlen_zero(p->loginchan) && ast_mutex_trylock(&p->app_lock) )
00989       {
00990          if (p->chan) {
00991             ast_queue_frame(p->chan, &null_frame);
00992             ast_mutex_unlock(&p->lock);   /* For other thread to read the condition. */
00993             ast_mutex_lock(&p->app_lock);
00994             ast_mutex_lock(&p->lock);
00995          }
00996          if( !p->chan )
00997          {
00998             ast_log(LOG_WARNING, "Agent disconnected while we were connecting the call\n");
00999             p->owner = NULL;
01000             tmp->tech_pvt = NULL;
01001             p->app_sleep_cond = 1;
01002             ast_channel_free( tmp );
01003             ast_mutex_unlock(&p->lock);   /* For other thread to read the condition. */
01004             ast_mutex_unlock(&p->app_lock);
01005             return NULL;
01006          }
01007       } else if (!ast_strlen_zero(p->loginchan)) {
01008          if (p->chan)
01009             ast_queue_frame(p->chan, &null_frame);
01010          if (!p->chan) {
01011             ast_log(LOG_WARNING, "Agent disconnected while we were connecting the call\n");
01012             p->owner = NULL;
01013             tmp->tech_pvt = NULL;
01014             p->app_sleep_cond = 1;
01015             ast_channel_free( tmp );
01016             ast_mutex_unlock(&p->lock);     /* For other thread to read the condition. */
01017                                 return NULL;
01018          }  
01019       }
01020       p->owning_app = pthread_self();
01021       /* After the above step, there should not be any blockers. */
01022       if (p->chan) {
01023          if (ast_test_flag(p->chan, AST_FLAG_BLOCKING)) {
01024             ast_log( LOG_ERROR, "A blocker exists after agent channel ownership acquired\n" );
01025             CRASH;
01026          }
01027          ast_moh_stop(p->chan);
01028       }
01029    } else
01030       ast_log(LOG_WARNING, "Unable to allocate agent channel structure\n");
01031    return tmp;
01032 }
01033 
01034 
01035 /**
01036  * Read configuration data. The file named agents.conf.
01037  *
01038  * @returns Always 0, or so it seems.
01039  */
01040 static int read_agent_config(void)
01041 {
01042    struct ast_config *cfg;
01043    struct ast_variable *v;
01044    struct agent_pvt *p, *pl, *pn;
01045    char *general_val;
01046 
01047    group = 0;
01048    autologoff = 0;
01049    wrapuptime = 0;
01050    ackcall = 0;
01051    cfg = ast_config_load(config);
01052    if (!cfg) {
01053       ast_log(LOG_NOTICE, "No agent configuration found -- agent support disabled\n");
01054       return 0;
01055    }
01056    ast_mutex_lock(&agentlock);
01057    p = agents;
01058    while(p) {
01059       p->dead = 1;
01060       p = p->next;
01061    }
01062    strcpy(moh, "default");
01063    /* set the default recording values */
01064    recordagentcalls = 0;
01065    createlink = 0;
01066    strcpy(recordformat, "wav");
01067    strcpy(recordformatext, "wav");
01068    urlprefix[0] = '\0';
01069    savecallsin[0] = '\0';
01070 
01071    /* Read in [general] section for persistence */
01072    if ((general_val = ast_variable_retrieve(cfg, "general", "persistentagents")))
01073       persistent_agents = ast_true(general_val);
01074 
01075    /* Read in the [agents] section */
01076    v = ast_variable_browse(cfg, "agents");
01077    while(v) {
01078       /* Create the interface list */
01079       if (!strcasecmp(v->name, "agent")) {
01080          add_agent(v->value, 0);
01081       } else if (!strcasecmp(v->name, "group")) {
01082          group = ast_get_group(v->value);
01083       } else if (!strcasecmp(v->name, "autologoff")) {
01084          autologoff = atoi(v->value);
01085          if (autologoff < 0)
01086             autologoff = 0;
01087       } else if (!strcasecmp(v->name, "ackcall")) {
01088          if (!strcasecmp(v->value, "always"))
01089             ackcall = 2;
01090          else if (ast_true(v->value))
01091             ackcall = 1;
01092          else
01093             ackcall = 0;
01094       } else if (!strcasecmp(v->name, "wrapuptime")) {
01095          wrapuptime = atoi(v->value);
01096          if (wrapuptime < 0)
01097             wrapuptime = 0;
01098       } else if (!strcasecmp(v->name, "maxlogintries") && !ast_strlen_zero(v->value)) {
01099          maxlogintries = atoi(v->value);
01100          if (maxlogintries < 0)
01101             maxlogintries = 0;
01102       } else if (!strcasecmp(v->name, "goodbye") && !ast_strlen_zero(v->value)) {
01103          strcpy(agentgoodbye,v->value);
01104       } else if (!strcasecmp(v->name, "musiconhold")) {
01105          ast_copy_string(moh, v->value, sizeof(moh));
01106       } else if (!strcasecmp(v->name, "updatecdr")) {
01107          if (ast_true(v->value))
01108             updatecdr = 1;
01109          else
01110             updatecdr = 0;
01111       } else if (!strcasecmp(v->name, "recordagentcalls")) {
01112          recordagentcalls = ast_true(v->value);
01113       } else if (!strcasecmp(v->name, "createlink")) {
01114          createlink = ast_true(v->value);
01115       } else if (!strcasecmp(v->name, "recordformat")) {
01116          ast_copy_string(recordformat, v->value, sizeof(recordformat));
01117          if (!strcasecmp(v->value, "wav49"))
01118             strcpy(recordformatext, "WAV");
01119          else
01120             ast_copy_string(recordformatext, v->value, sizeof(recordformatext));
01121       } else if (!strcasecmp(v->name, "urlprefix")) {
01122          ast_copy_string(urlprefix, v->value, sizeof(urlprefix));
01123          if (urlprefix[strlen(urlprefix) - 1] != '/')
01124             strncat(urlprefix, "/", sizeof(urlprefix) - strlen(urlprefix) - 1);
01125       } else if (!strcasecmp(v->name, "savecallsin")) {
01126          if (v->value[0] == '/')
01127             ast_copy_string(savecallsin, v->value, sizeof(savecallsin));
01128          else
01129             snprintf(savecallsin, sizeof(savecallsin) - 2, "/%s", v->value);
01130          if (savecallsin[strlen(savecallsin) - 1] != '/')
01131             strncat(savecallsin, "/", sizeof(savecallsin) - strlen(savecallsin) - 1);
01132       } else if (!strcasecmp(v->name, "custom_beep")) {
01133          ast_copy_string(beep, v->value, sizeof(beep));
01134       }
01135       v = v->next;
01136    }
01137    p = agents;
01138    pl = NULL;
01139    while(p) {
01140       pn = p->next;
01141       if (p->dead) {
01142          /* Unlink */
01143          if (pl)
01144             pl->next = p->next;
01145          else
01146             agents = p->next;
01147          /* Destroy if  appropriate */
01148          if (!p->owner) {
01149             if (!p->chan) {
01150                ast_mutex_destroy(&p->lock);
01151                ast_mutex_destroy(&p->app_lock);
01152                free(p);
01153             } else {
01154                /* Cause them to hang up */
01155                ast_softhangup(p->chan, AST_SOFTHANGUP_EXPLICIT);
01156             }
01157          }
01158       } else
01159          pl = p;
01160       p = pn;
01161    }
01162    ast_mutex_unlock(&agentlock);
01163    ast_config_destroy(cfg);
01164    return 0;
01165 }
01166 
01167 static int check_availability(struct agent_pvt *newlyavailable, int needlock)
01168 {
01169    struct ast_channel *chan=NULL, *parent=NULL;
01170    struct agent_pvt *p;
01171    int res;
01172 
01173    if (option_debug)
01174       ast_log(LOG_DEBUG, "Checking availability of '%s'\n", newlyavailable->agent);
01175    if (needlock)
01176       ast_mutex_lock(&agentlock);
01177    p = agents;
01178    while(p) {
01179       if (p == newlyavailable) {
01180          p = p->next;
01181          continue;
01182       }
01183       ast_mutex_lock(&p->lock);
01184       if (!p->abouttograb && p->pending && ((p->group && (newlyavailable->group & p->group)) || !strcmp(p->agent, newlyavailable->agent))) {
01185          if (option_debug)
01186             ast_log(LOG_DEBUG, "Call '%s' looks like a winner for agent '%s'\n", p->owner->name, newlyavailable->agent);
01187          /* We found a pending call, time to merge */
01188          chan = agent_new(newlyavailable, AST_STATE_DOWN);
01189          parent = p->owner;
01190          p->abouttograb = 1;
01191          ast_mutex_unlock(&p->lock);
01192          break;
01193       }
01194       ast_mutex_unlock(&p->lock);
01195       p = p->next;
01196    }
01197    if (needlock)
01198       ast_mutex_unlock(&agentlock);
01199    if (parent && chan)  {
01200       if (newlyavailable->ackcall > 1) {
01201          /* Don't do beep here */
01202          res = 0;
01203       } else {
01204          if (option_debug > 2)
01205             ast_log( LOG_DEBUG, "Playing beep, lang '%s'\n", newlyavailable->chan->language);
01206          res = ast_streamfile(newlyavailable->chan, beep, newlyavailable->chan->language);
01207          if (option_debug > 2)
01208             ast_log( LOG_DEBUG, "Played beep, result '%d'\n", res);
01209          if (!res) {
01210             res = ast_waitstream(newlyavailable->chan, "");
01211             ast_log( LOG_DEBUG, "Waited for stream, result '%d'\n", res);
01212          }
01213       }
01214       if (!res) {
01215          /* Note -- parent may have disappeared */
01216          if (p->abouttograb) {
01217             newlyavailable->acknowledged = 1;
01218             /* Safe -- agent lock already held */
01219             ast_setstate(parent, AST_STATE_UP);
01220             ast_setstate(chan, AST_STATE_UP);
01221             ast_copy_string(parent->context, chan->context, sizeof(parent->context));
01222             /* Go ahead and mark the channel as a zombie so that masquerade will
01223                destroy it for us, and we need not call ast_hangup */
01224             ast_mutex_lock(&parent->lock);
01225             ast_set_flag(chan, AST_FLAG_ZOMBIE);
01226             ast_channel_masquerade(parent, chan);
01227             ast_mutex_unlock(&parent->lock);
01228             p->abouttograb = 0;
01229          } else {
01230             if (option_debug)
01231                ast_log(LOG_DEBUG, "Sneaky, parent disappeared in the mean time...\n");
01232             agent_cleanup(newlyavailable);
01233          }
01234       } else {
01235          if (option_debug)
01236             ast_log(LOG_DEBUG, "Ugh...  Agent hung up at exactly the wrong time\n");
01237          agent_cleanup(newlyavailable);
01238       }
01239    }
01240    return 0;
01241 }
01242 
01243 static int check_beep(struct agent_pvt *newlyavailable, int needlock)
01244 {
01245    struct agent_pvt *p;
01246    int res=0;
01247 
01248    ast_log(LOG_DEBUG, "Checking beep availability of '%s'\n", newlyavailable->agent);
01249    if (needlock)
01250       ast_mutex_lock(&agentlock);
01251    p = agents;
01252    while(p) {
01253       if (p == newlyavailable) {
01254          p = p->next;
01255          continue;
01256       }
01257       ast_mutex_lock(&p->lock);
01258       if (!p->abouttograb && p->pending && ((p->group && (newlyavailable->group & p->group)) || !strcmp(p->agent, newlyavailable->agent))) {
01259          if (option_debug)
01260             ast_log(LOG_DEBUG, "Call '%s' looks like a would-be winner for agent '%s'\n", p->owner->name, newlyavailable->agent);
01261          ast_mutex_unlock(&p->lock);
01262          break;
01263       }
01264       ast_mutex_unlock(&p->lock);
01265       p = p->next;
01266    }
01267    if (needlock)
01268       ast_mutex_unlock(&agentlock);
01269    if (p) {
01270       ast_mutex_unlock(&newlyavailable->lock);
01271       if (option_debug > 2)
01272          ast_log( LOG_DEBUG, "Playing beep, lang '%s'\n", newlyavailable->chan->language);
01273       res = ast_streamfile(newlyavailable->chan, beep, newlyavailable->chan->language);
01274       if (option_debug > 2)
01275          ast_log( LOG_DEBUG, "Played beep, result '%d'\n", res);
01276       if (!res) {
01277          res = ast_waitstream(newlyavailable->chan, "");
01278          if (option_debug)
01279             ast_log( LOG_DEBUG, "Waited for stream, result '%d'\n", res);
01280       }
01281       ast_mutex_lock(&newlyavailable->lock);
01282    }
01283    return res;
01284 }
01285 
01286 /*--- agent_request: Part of the Asterisk PBX interface ---*/
01287 static struct ast_channel *agent_request(const char *type, int format, void *data, int *cause)
01288 {
01289    struct agent_pvt *p;
01290    struct ast_channel *chan = NULL;
01291    char *s;
01292    ast_group_t groupmatch;
01293    int groupoff;
01294    int waitforagent=0;
01295    int hasagent = 0;
01296    struct timeval tv;
01297 
01298    s = data;
01299    if ((s[0] == '@') && (sscanf(s + 1, "%30d", &groupoff) == 1)) {
01300       groupmatch = (1 << groupoff);
01301    } else if ((s[0] == ':') && (sscanf(s + 1, "%30d", &groupoff) == 1)) {
01302       groupmatch = (1 << groupoff);
01303       waitforagent = 1;
01304    } else {
01305       groupmatch = 0;
01306    }
01307 
01308    /* Check actual logged in agents first */
01309    ast_mutex_lock(&agentlock);
01310    p = agents;
01311    while(p) {
01312       ast_mutex_lock(&p->lock);
01313       if (!p->pending && ((groupmatch && (p->group & groupmatch)) || !strcmp(data, p->agent)) &&
01314           ast_strlen_zero(p->loginchan)) {
01315          if (p->chan)
01316             hasagent++;
01317          tv = ast_tvnow();
01318          if (!p->lastdisc.tv_sec || (tv.tv_sec >= p->lastdisc.tv_sec)) {
01319             p->lastdisc = ast_tv(0, 0);
01320             /* Agent must be registered, but not have any active call, and not be in a waiting state */
01321             if (!p->owner && p->chan) {
01322                /* Fixed agent */
01323                chan = agent_new(p, AST_STATE_DOWN);
01324             }
01325             if (chan) {
01326                ast_mutex_unlock(&p->lock);
01327                break;
01328             }
01329          }
01330       }
01331       ast_mutex_unlock(&p->lock);
01332       p = p->next;
01333    }
01334    if (!p) {
01335       p = agents;
01336       while(p) {
01337          ast_mutex_lock(&p->lock);
01338          if (!p->pending && ((groupmatch && (p->group & groupmatch)) || !strcmp(data, p->agent))) {
01339             if (p->chan || !ast_strlen_zero(p->loginchan))
01340                hasagent++;
01341             tv = ast_tvnow();
01342 #if 0
01343             ast_log(LOG_NOTICE, "Time now: %ld, Time of lastdisc: %ld\n", tv.tv_sec, p->lastdisc.tv_sec);
01344 #endif
01345             if (!p->lastdisc.tv_sec || (tv.tv_sec >= p->lastdisc.tv_sec)) {
01346                p->lastdisc = ast_tv(0, 0);
01347                /* Agent must be registered, but not have any active call, and not be in a waiting state */
01348                if (!p->owner && p->chan) {
01349                   /* Could still get a fixed agent */
01350                   chan = agent_new(p, AST_STATE_DOWN);
01351                } else if (!p->owner && !ast_strlen_zero(p->loginchan)) {
01352                   /* Adjustable agent */
01353                   p->chan = ast_request("Local", format, p->loginchan, cause);
01354                   if (p->chan)
01355                      chan = agent_new(p, AST_STATE_DOWN);
01356                }
01357                if (chan) {
01358                   ast_mutex_unlock(&p->lock);
01359                   break;
01360                }
01361             }
01362          }
01363          ast_mutex_unlock(&p->lock);
01364          p = p->next;
01365       }
01366    }
01367 
01368    if (!chan && waitforagent) {
01369       /* No agent available -- but we're requesting to wait for one.
01370          Allocate a place holder */
01371       if (hasagent) {
01372          if (option_debug)
01373             ast_log(LOG_DEBUG, "Creating place holder for '%s'\n", s);
01374          p = add_agent(data, 1);
01375          p->group = groupmatch;
01376          chan = agent_new(p, AST_STATE_DOWN);
01377          if (!chan) {
01378             ast_log(LOG_WARNING, "Weird...  Fix this to drop the unused pending agent\n");
01379          }
01380       } else
01381          ast_log(LOG_DEBUG, "Not creating place holder for '%s' since nobody logged in\n", s);
01382    }
01383    if (hasagent)
01384       *cause = AST_CAUSE_BUSY;
01385    else
01386       *cause = AST_CAUSE_UNREGISTERED;
01387    ast_mutex_unlock(&agentlock);
01388    return chan;
01389 }
01390 
01391 static int powerof(unsigned int v)
01392 {
01393    int x;
01394    for (x=0;x<32;x++) {
01395       if (v & (1 << x)) return x;
01396    }
01397    return 0;
01398 }
01399 
01400 /**
01401  * Lists agents and their status to the Manager API.
01402  * It is registered on load_module() and it gets called by the manager backend.
01403  * @param s
01404  * @param m
01405  * @returns 
01406  * @sa action_agent_logoff(), action_agent_callback_login(), load_module().
01407  */
01408 static int action_agents(struct mansession *s, struct message *m)
01409 {
01410    char *id = astman_get_header(m,"ActionID");
01411    char idText[256] = "";
01412    char chanbuf[256];
01413    struct agent_pvt *p;
01414    char *username = NULL;
01415    char *loginChan = NULL;
01416    char *talkingtoChan = NULL;
01417    char *status = NULL;
01418 
01419    if (!ast_strlen_zero(id))
01420       snprintf(idText, sizeof(idText) ,"ActionID: %s\r\n", id);
01421    astman_send_ack(s, m, "Agents will follow");
01422    ast_mutex_lock(&agentlock);
01423    p = agents;
01424    while(p) {
01425          ast_mutex_lock(&p->lock);
01426 
01427       /* Status Values:
01428          AGENT_LOGGEDOFF - Agent isn't logged in
01429          AGENT_IDLE      - Agent is logged in, and waiting for call
01430          AGENT_ONCALL    - Agent is logged in, and on a call
01431          AGENT_UNKNOWN   - Don't know anything about agent. Shouldn't ever get this. */
01432 
01433       if(!ast_strlen_zero(p->name)) {
01434          username = p->name;
01435       } else {
01436          username = "None";
01437       }
01438 
01439       /* Set a default status. It 'should' get changed. */
01440       status = "AGENT_UNKNOWN";
01441 
01442       if (!ast_strlen_zero(p->loginchan) && !p->chan) {
01443          loginChan = p->loginchan;
01444          talkingtoChan = "n/a";
01445          status = "AGENT_IDLE";
01446          if (p->acknowledged) {
01447             snprintf(chanbuf, sizeof(chanbuf), " %s (Confirmed)", p->loginchan);
01448             loginChan = chanbuf;
01449          }
01450       } else if (p->chan) {
01451          loginChan = ast_strdupa(p->chan->name);
01452          if (p->owner && p->owner->_bridge) {
01453                talkingtoChan = p->chan->cid.cid_num;
01454                status = "AGENT_ONCALL";
01455          } else {
01456                talkingtoChan = "n/a";
01457                status = "AGENT_IDLE";
01458          }
01459       } else {
01460          loginChan = "n/a";
01461          talkingtoChan = "n/a";
01462          status = "AGENT_LOGGEDOFF";
01463       }
01464 
01465       ast_cli(s->fd, "Event: Agents\r\n"
01466          "Agent: %s\r\n"
01467          "Name: %s\r\n"
01468          "Status: %s\r\n"
01469          "LoggedInChan: %s\r\n"
01470          "LoggedInTime: %d\r\n"
01471          "TalkingTo: %s\r\n"
01472          "%s"
01473          "\r\n",
01474          p->agent, username, status, loginChan, (int)p->loginstart, talkingtoChan, idText);
01475       ast_mutex_unlock(&p->lock);
01476       p = p->next;
01477    }
01478    ast_mutex_unlock(&agentlock);
01479    ast_cli(s->fd, "Event: AgentsComplete\r\n"
01480       "%s"
01481       "\r\n",idText);
01482    return 0;
01483 }
01484 
01485 static int agent_logoff(char *agent, int soft)
01486 {
01487    struct agent_pvt *p;
01488    long logintime;
01489    int ret = -1; /* Return -1 if no agent if found */
01490    int defer = 0;
01491 
01492    for (p=agents; p; p=p->next) {
01493       if (!strcasecmp(p->agent, agent)) {
01494          if (p->owner || p->chan)
01495             defer = 1;
01496          if (!soft) {
01497             if (p->owner)
01498                ast_softhangup(p->owner, AST_SOFTHANGUP_EXPLICIT);
01499             if (p->chan)
01500                ast_softhangup(p->chan, AST_SOFTHANGUP_EXPLICIT);
01501          }
01502          ret = 0; /* found an agent => return 0 */
01503          logintime = time(NULL) - p->loginstart;
01504          p->loginstart = 0;
01505          
01506          manager_event(EVENT_FLAG_AGENT, "Agentcallbacklogoff",
01507                   "Agent: %s\r\n"
01508                   "Loginchan: %s\r\n"
01509                   "Logintime: %ld\r\n",
01510                   p->agent, p->loginchan, logintime);
01511          ast_queue_log("NONE", "NONE", agent, "AGENTCALLBACKLOGOFF", "%s|%ld|%s", p->loginchan, logintime, "CommandLogoff");
01512          set_agentbycallerid(p->logincallerid, NULL);
01513          if (!defer) {
01514             p->loginchan[0] = '\0';
01515             p->logincallerid[0] = '\0';
01516          }
01517          ast_device_state_changed("Agent/%s", p->agent);
01518          if (persistent_agents)
01519             dump_agents();
01520          break;
01521       }
01522    }
01523 
01524    return ret;
01525 }
01526 
01527 static int agent_logoff_cmd(int fd, int argc, char **argv)
01528 {
01529    int ret;
01530    char *agent;
01531 
01532    if (argc < 3 || argc > 4)
01533       return RESULT_SHOWUSAGE;
01534    if (argc == 4 && strcasecmp(argv[3], "soft"))
01535       return RESULT_SHOWUSAGE;
01536 
01537    agent = argv[2] + 6;
01538    ret = agent_logoff(agent, argc == 4);
01539    if (ret == 0)
01540       ast_cli(fd, "Logging out %s\n", agent);
01541 
01542    return RESULT_SUCCESS;
01543 }
01544 
01545 /**
01546  * Sets an agent as no longer logged in in the Manager API.
01547  * It is registered on load_module() and it gets called by the manager backend.
01548  * @param s
01549  * @param m
01550  * @returns 
01551  * @sa action_agents(), action_agent_callback_login(), load_module().
01552  */
01553 static int action_agent_logoff(struct mansession *s, struct message *m)
01554 {
01555    char *agent = astman_get_header(m, "Agent");
01556    char *soft_s = astman_get_header(m, "Soft"); /* "true" is don't hangup */
01557    int soft;
01558    int ret; /* return value of agent_logoff */
01559 
01560    if (ast_strlen_zero(agent)) {
01561       astman_send_error(s, m, "No agent specified");
01562       return 0;
01563    }
01564 
01565    if (ast_true(soft_s))
01566       soft = 1;
01567    else
01568       soft = 0;
01569 
01570    ret = agent_logoff(agent, soft);
01571    if (ret == 0)
01572       astman_send_ack(s, m, "Agent logged out");
01573    else
01574       astman_send_error(s, m, "No such agent");
01575 
01576    return 0;
01577 }
01578 
01579 static char *complete_agent_logoff_cmd(char *line, char *word, int pos, int state)
01580 {
01581    struct agent_pvt *p;
01582    char name[AST_MAX_AGENT];
01583    int which = 0;
01584 
01585    if (pos == 2) {
01586       for (p=agents; p; p=p->next) {
01587          snprintf(name, sizeof(name), "Agent/%s", p->agent);
01588          if (!strncasecmp(word, name, strlen(word))) {
01589             if (++which > state) {
01590                return strdup(name);
01591             }
01592          }
01593       }
01594    } else if (pos == 3 && state == 0) {
01595       return strdup("soft");
01596    }
01597    return NULL;
01598 }
01599 
01600 /**
01601  * Show agents in cli.
01602  */
01603 static int agents_show(int fd, int argc, char **argv)
01604 {
01605    struct agent_pvt *p;
01606    char username[AST_MAX_BUF];
01607    char location[AST_MAX_BUF] = "";
01608    char talkingto[AST_MAX_BUF] = "";
01609    char moh[AST_MAX_BUF];
01610    int count_agents = 0;      /* Number of agents configured */
01611    int online_agents = 0;     /* Number of online agents */
01612    int offline_agents = 0;    /* Number of offline agents */
01613    if (argc != 2)
01614       return RESULT_SHOWUSAGE;
01615    ast_mutex_lock(&agentlock);
01616    p = agents;
01617    while(p) {
01618       ast_mutex_lock(&p->lock);
01619       if (p->pending) {
01620          if (p->group)
01621             ast_cli(fd, "-- Pending call to group %d\n", powerof(p->group));
01622          else
01623             ast_cli(fd, "-- Pending call to agent %s\n", p->agent);
01624       } else {
01625          if (!ast_strlen_zero(p->name))
01626             snprintf(username, sizeof(username), "(%s) ", p->name);
01627          else
01628             username[0] = '\0';
01629          if (p->chan) {
01630             snprintf(location, sizeof(location), "logged in on %s", p->chan->name);
01631             if (p->owner && ast_bridged_channel(p->owner)) {
01632                snprintf(talkingto, sizeof(talkingto), " talking to %s", ast_bridged_channel(p->owner)->name);
01633             } else {
01634                strcpy(talkingto, " is idle");
01635             }
01636             online_agents++;
01637          } else if (!ast_strlen_zero(p->loginchan)) {
01638             if (ast_tvdiff_ms(ast_tvnow(), p->lastdisc) > 0 || !(p->lastdisc.tv_sec)) 
01639                snprintf(location, sizeof(location) - 20, "available at '%s'", p->loginchan);
01640             else 
01641                snprintf(location, sizeof(location) - 20, "wrapping up at '%s'", p->loginchan);
01642             talkingto[0] = '\0';
01643             online_agents++;
01644             if (p->acknowledged)
01645                strncat(location, " (Confirmed)", sizeof(location) - strlen(location) - 1);
01646          } else {
01647             strcpy(location, "not logged in");
01648             talkingto[0] = '\0';
01649             offline_agents++;
01650          }
01651          if (!ast_strlen_zero(p->moh))
01652             snprintf(moh, sizeof(moh), " (musiconhold is '%s')", p->moh);
01653          ast_cli(fd, "%-12.12s %s%s%s%s\n", p->agent, 
01654             username, location, talkingto, moh);
01655          count_agents++;
01656       }
01657       ast_mutex_unlock(&p->lock);
01658       p = p->next;
01659    }
01660    ast_mutex_unlock(&agentlock);
01661    if ( !count_agents ) {
01662       ast_cli(fd, "No Agents are configured in %s\n",config);
01663    } else {
01664       ast_cli(fd, "%d agents configured [%d online , %d offline]\n",count_agents, online_agents, offline_agents);
01665    }
01666    ast_cli(fd, "\n");
01667                    
01668    return RESULT_SUCCESS;
01669 }
01670 
01671 static char show_agents_usage[] = 
01672 "Usage: show agents\n"
01673 "       Provides summary information on agents.\n";
01674 
01675 static char agent_logoff_usage[] =
01676 "Usage: agent logoff <channel> [soft]\n"
01677 "       Sets an agent as no longer logged in.\n"
01678 "       If 'soft' is specified, do not hangup existing calls.\n";
01679 
01680 static struct ast_cli_entry cli_show_agents = {
01681    { "show", "agents", NULL }, agents_show, 
01682    "Show status of agents", show_agents_usage, NULL };
01683 
01684 static struct ast_cli_entry cli_agent_logoff = {
01685    { "agent", "logoff", NULL }, agent_logoff_cmd, 
01686    "Sets an agent offline", agent_logoff_usage, complete_agent_logoff_cmd };
01687 
01688 STANDARD_LOCAL_USER;
01689 LOCAL_USER_DECL;
01690 
01691 /*!
01692  * \brief Log in agent application.
01693  *
01694  * \param chan
01695  * \param data
01696  * \param callbackmode non-zero for AgentCallbackLogin
01697  */
01698 static int __login_exec(struct ast_channel *chan, void *data, int callbackmode)
01699 {
01700    int res=0;
01701    int tries = 0;
01702    int max_login_tries = maxlogintries;
01703    struct agent_pvt *p;
01704    struct localuser *u;
01705    int login_state = 0;
01706    char user[AST_MAX_AGENT] = "";
01707    char pass[AST_MAX_AGENT];
01708    char agent[AST_MAX_AGENT] = "";
01709    char xpass[AST_MAX_AGENT] = "";
01710    char *errmsg;
01711    char *parse;
01712    AST_DECLARE_APP_ARGS(args,
01713               AST_APP_ARG(agent_id);
01714               AST_APP_ARG(options);
01715               AST_APP_ARG(extension);
01716       );
01717    char *tmpoptions = NULL;
01718    char *context = NULL;
01719    int play_announcement = 1;
01720    char agent_goodbye[AST_MAX_FILENAME_LEN];
01721    int update_cdr = updatecdr;
01722    char *filename = "agent-loginok";
01723    char tmpchan[AST_MAX_BUF] = "";
01724 
01725    LOCAL_USER_ADD(u);
01726 
01727    if (!(parse = ast_strdupa(data))) {
01728       ast_log(LOG_ERROR, "Out of memory!\n");
01729       LOCAL_USER_REMOVE(u);
01730       return -1;
01731    }
01732 
01733    AST_STANDARD_APP_ARGS(args, parse);
01734 
01735    ast_copy_string(agent_goodbye, agentgoodbye, sizeof(agent_goodbye));
01736 
01737    /* Set Channel Specific Login Overrides */
01738    if (pbx_builtin_getvar_helper(chan, "AGENTLMAXLOGINTRIES") && strlen(pbx_builtin_getvar_helper(chan, "AGENTLMAXLOGINTRIES"))) {
01739       max_login_tries = atoi(pbx_builtin_getvar_helper(chan, "AGENTMAXLOGINTRIES"));
01740       if (max_login_tries < 0)
01741          max_login_tries = 0;
01742       tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTMAXLOGINTRIES");
01743       if (option_verbose > 2)
01744          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);
01745    }
01746    if (pbx_builtin_getvar_helper(chan, "AGENTUPDATECDR") && !ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENTUPDATECDR"))) {
01747       if (ast_true(pbx_builtin_getvar_helper(chan, "AGENTUPDATECDR")))
01748          update_cdr = 1;
01749       else
01750          update_cdr = 0;
01751       tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTUPDATECDR");
01752       if (option_verbose > 2)
01753          ast_verbose(VERBOSE_PREFIX_3 "Saw variable AGENTUPDATECDR=%s, setting update_cdr to: %d on Channel '%s'.\n",tmpoptions,update_cdr,chan->name);
01754    }
01755    if (pbx_builtin_getvar_helper(chan, "AGENTGOODBYE") && !ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENTGOODBYE"))) {
01756       strcpy(agent_goodbye, pbx_builtin_getvar_helper(chan, "AGENTGOODBYE"));
01757       tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTGOODBYE");
01758       if (option_verbose > 2)
01759          ast_verbose(VERBOSE_PREFIX_3 "Saw variable AGENTGOODBYE=%s, setting agent_goodbye to: %s on Channel '%s'.\n",tmpoptions,agent_goodbye,chan->name);
01760    }
01761    /* End Channel Specific Login Overrides */
01762    
01763    if (callbackmode && args.extension) {
01764       parse = args.extension;
01765       args.extension = strsep(&parse, "@");
01766       context = parse;
01767    }
01768 
01769    if (!ast_strlen_zero(args.options)) {
01770       if (strchr(args.options, 's')) {
01771          play_announcement = 0;
01772       }
01773    }
01774 
01775    if (chan->_state != AST_STATE_UP)
01776       res = ast_answer(chan);
01777    if (!res) {
01778       if (!ast_strlen_zero(args.agent_id))
01779          ast_copy_string(user, args.agent_id, AST_MAX_AGENT);
01780       else
01781          res = ast_app_getdata(chan, "agent-user", user, sizeof(user) - 1, 0);
01782    }
01783    while (!res && (max_login_tries==0 || tries < max_login_tries)) {
01784       tries++;
01785       /* Check for password */
01786       ast_mutex_lock(&agentlock);
01787       p = agents;
01788       while(p) {
01789          if (!strcmp(p->agent, user) && !p->pending)
01790             ast_copy_string(xpass, p->password, sizeof(xpass));
01791          p = p->next;
01792       }
01793       ast_mutex_unlock(&agentlock);
01794       if (!res) {
01795          if (!ast_strlen_zero(xpass))
01796             res = ast_app_getdata(chan, "agent-pass", pass, sizeof(pass) - 1, 0);
01797          else
01798             pass[0] = '\0';
01799       }
01800       errmsg = "agent-incorrect";
01801 
01802 #if 0
01803       ast_log(LOG_NOTICE, "user: %s, pass: %s\n", user, pass);
01804 #endif      
01805 
01806       /* Check again for accuracy */
01807       ast_mutex_lock(&agentlock);
01808       p = agents;
01809       while(p) {
01810          ast_mutex_lock(&p->lock);
01811          if (!strcmp(p->agent, user) &&
01812              !strcmp(p->password, pass) && !p->pending) {
01813             login_state = 1; /* Successful Login */
01814 
01815             /* Ensure we can't be gotten until we're done */
01816             gettimeofday(&p->lastdisc, NULL);
01817             p->lastdisc.tv_sec++;
01818 
01819             /* Set Channel Specific Agent Overrides */
01820             if (pbx_builtin_getvar_helper(chan, "AGENTACKCALL") && strlen(pbx_builtin_getvar_helper(chan, "AGENTACKCALL"))) {
01821                if (!strcasecmp(pbx_builtin_getvar_helper(chan, "AGENTACKCALL"), "always"))
01822                   p->ackcall = 2;
01823                else if (ast_true(pbx_builtin_getvar_helper(chan, "AGENTACKCALL")))
01824                   p->ackcall = 1;
01825                else
01826                   p->ackcall = 0;
01827                tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTACKCALL");
01828                if (option_verbose > 2)
01829                   ast_verbose(VERBOSE_PREFIX_3 "Saw variable AGENTACKCALL=%s, setting ackcall to: %d for Agent '%s'.\n",tmpoptions,p->ackcall,p->agent);
01830             }
01831             if (pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF") && strlen(pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF"))) {
01832                p->autologoff = atoi(pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF"));
01833                if (p->autologoff < 0)
01834                   p->autologoff = 0;
01835                tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTAUTOLOGOFF");
01836                if (option_verbose > 2)
01837                   ast_verbose(VERBOSE_PREFIX_3 "Saw variable AGENTAUTOLOGOFF=%s, setting autologff to: %d for Agent '%s'.\n",tmpoptions,p->autologoff,p->agent);
01838             }
01839             if (pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME") && strlen(pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME"))) {
01840                p->wrapuptime = atoi(pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME"));
01841                if (p->wrapuptime < 0)
01842                   p->wrapuptime = 0;
01843                tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTWRAPUPTIME");
01844                if (option_verbose > 2)
01845                   ast_verbose(VERBOSE_PREFIX_3 "Saw variable AGENTWRAPUPTIME=%s, setting wrapuptime to: %d for Agent '%s'.\n",tmpoptions,p->wrapuptime,p->agent);
01846             }
01847             /* End Channel Specific Agent Overrides */
01848             if (!p->chan) {
01849                char last_loginchan[80] = "";
01850                long logintime;
01851                snprintf(agent, sizeof(agent), "Agent/%s", p->agent);
01852 
01853                if (callbackmode) {
01854                   int pos = 0;
01855                   /* Retrieve login chan */
01856                   for (;;) {
01857                      if (!ast_strlen_zero(args.extension)) {
01858                         ast_copy_string(tmpchan, args.extension, sizeof(tmpchan));
01859                         res = 0;
01860                      } else
01861                         res = ast_app_getdata(chan, "agent-newlocation", tmpchan+pos, sizeof(tmpchan) - 2, 0);
01862                      if (ast_strlen_zero(tmpchan) || ast_exists_extension(chan, !ast_strlen_zero(context) ? context : "default", tmpchan,
01863                                             1, NULL))
01864                         break;
01865                      if (args.extension) {
01866                         ast_log(LOG_WARNING, "Extension '%s' is not valid for automatic login of agent '%s'\n", args.extension, p->agent);
01867                         args.extension = NULL;
01868                         pos = 0;
01869                      } else {
01870                         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);
01871                         res = ast_streamfile(chan, "invalid", chan->language);
01872                         if (!res)
01873                            res = ast_waitstream(chan, AST_DIGIT_ANY);
01874                         if (res > 0) {
01875                            tmpchan[0] = res;
01876                            tmpchan[1] = '\0';
01877                            pos = 1;
01878                         } else {
01879                            tmpchan[0] = '\0';
01880                            pos = 0;
01881                         }
01882                      }
01883                   }
01884                   args.extension = tmpchan;
01885                   if (!res) {
01886                      set_agentbycallerid(p->logincallerid, NULL);
01887                      if (!ast_strlen_zero(context) && !ast_strlen_zero(tmpchan))
01888                         snprintf(p->loginchan, sizeof(p->loginchan), "%s@%s", tmpchan, context);
01889                      else {
01890                         ast_copy_string(last_loginchan, p->loginchan, sizeof(last_loginchan));
01891                         ast_copy_string(p->loginchan, tmpchan, sizeof(p->loginchan));
01892                      }
01893                      p->acknowledged = 0;
01894                      if (ast_strlen_zero(p->loginchan)) {
01895                         login_state = 2;
01896                         filename = "agent-loggedoff";
01897                      } else {
01898                         if (chan->cid.cid_num) {
01899                            ast_copy_string(p->logincallerid, chan->cid.cid_num, sizeof(p->logincallerid));
01900                            set_agentbycallerid(p->logincallerid, p->agent);
01901                         } else
01902                            p->logincallerid[0] = '\0';
01903                      }
01904 
01905                      if(update_cdr && chan->cdr)
01906                         snprintf(chan->cdr->channel, sizeof(chan->cdr->channel), "Agent/%s", p->agent);
01907 
01908                   }
01909                } else {
01910                   p->loginchan[0] = '\0';
01911                   p->logincallerid[0] = '\0';
01912                   p->acknowledged = 0;
01913                }
01914                ast_mutex_unlock(&p->lock);
01915                ast_mutex_unlock(&agentlock);
01916                if( !res && play_announcement==1 )
01917                   res = ast_streamfile(chan, filename, chan->language);
01918                if (!res)
01919                   ast_waitstream(chan, "");
01920                ast_mutex_lock(&agentlock);
01921                ast_mutex_lock(&p->lock);
01922                if (!res) {
01923                   res = ast_set_read_format(chan, ast_best_codec(chan->nativeformats));
01924                   if (res)
01925                      ast_log(LOG_WARNING, "Unable to set read format to %d\n", ast_best_codec(chan->nativeformats));
01926                }
01927                if (!res) {
01928                   res = ast_set_write_format(chan, ast_best_codec(chan->nativeformats));
01929                   if (res)
01930                      ast_log(LOG_WARNING, "Unable to set write format to %d\n", ast_best_codec(chan->nativeformats));
01931                }
01932                /* Check once more just in case */
01933                if (p->chan)
01934                   res = -1;
01935                if (callbackmode && !res) {
01936                   /* Just say goodbye and be done with it */
01937                   if (!ast_strlen_zero(p->loginchan)) {
01938                      if (p->loginstart == 0)
01939                         time(&p->loginstart);
01940                      manager_event(EVENT_FLAG_AGENT, "Agentcallbacklogin",
01941                               "Agent: %s\r\n"
01942                               "Loginchan: %s\r\n"
01943                               "Uniqueid: %s\r\n",
01944                               p->agent, p->loginchan, chan->uniqueid);
01945                      ast_queue_log("NONE", chan->uniqueid, agent, "AGENTCALLBACKLOGIN", "%s", p->loginchan);
01946                      if (option_verbose > 1)
01947                         ast_verbose(VERBOSE_PREFIX_2 "Callback Agent '%s' logged in on %s\n", p->agent, p->loginchan);
01948                      ast_device_state_changed("Agent/%s", p->agent);
01949                   } else {
01950                      logintime = time(NULL) - p->loginstart;
01951                      p->loginstart = 0;
01952                      manager_event(EVENT_FLAG_AGENT, "Agentcallbacklogoff",
01953                               "Agent: %s\r\n"
01954                               "Loginchan: %s\r\n"
01955                               "Logintime: %ld\r\n"
01956                               "Uniqueid: %s\r\n",
01957                               p->agent, last_loginchan, logintime, chan->uniqueid);
01958                      ast_queue_log("NONE", chan->uniqueid, agent, "AGENTCALLBACKLOGOFF", "%s|%ld|", last_loginchan, logintime);
01959                      if (option_verbose > 1)
01960                         ast_verbose(VERBOSE_PREFIX_2 "Callback Agent '%s' logged out\n", p->agent);
01961                      ast_device_state_changed("Agent/%s", p->agent);
01962                   }
01963                   ast_mutex_unlock(&agentlock);
01964                   if (!res)
01965                      res = ast_safe_sleep(chan, 500);
01966                   ast_mutex_unlock(&p->lock);
01967                   if (persistent_agents)
01968                      dump_agents();
01969                } else if (!res) {
01970 #ifdef HONOR_MUSIC_CLASS
01971                   /* check if the moh class was changed with setmusiconhold */
01972                   if (*(chan->musicclass))
01973                      ast_copy_string(p->moh, chan->musicclass, sizeof(p->moh));
01974 #endif                        
01975                   ast_moh_start(chan, p->moh);
01976                   if (p->loginstart == 0)
01977                      time(&p->loginstart);
01978                   manager_event(EVENT_FLAG_AGENT, "Agentlogin",
01979                            "Agent: %s\r\n"
01980                            "Channel: %s\r\n"
01981                            "Uniqueid: %s\r\n",
01982                            p->agent, chan->name, chan->uniqueid);
01983                   if (update_cdr && chan->cdr)
01984                      snprintf(chan->cdr->channel, sizeof(chan->cdr->channel), "Agent/%s", p->agent);
01985                   ast_queue_log("NONE", chan->uniqueid, agent, "AGENTLOGIN", "%s", chan->name);
01986                   if (option_verbose > 1)
01987                      ast_verbose(VERBOSE_PREFIX_2 "Agent '%s' logged in (format %s/%s)\n", p->agent,
01988                             ast_getformatname(chan->readformat), ast_getformatname(chan->writeformat));
01989                   /* Login this channel and wait for it to
01990                      go away */
01991                   p->chan = chan;
01992                   if (p->ackcall > 1)
01993                      check_beep(p, 0);
01994                   else
01995                      check_availability(p, 0);
01996                   ast_mutex_unlock(&p->lock);
01997                   ast_mutex_unlock(&agentlock);
01998                   ast_device_state_changed("Agent/%s", p->agent);
01999                   while (res >= 0) {
02000                      ast_mutex_lock(&p->lock);
02001                      if (!p->loginstart && p->chan)
02002                         ast_softhangup(p->chan, AST_SOFTHANGUP_EXPLICIT);
02003                      if (p->chan != chan)
02004                         res = -1;
02005                      ast_mutex_unlock(&p->lock);
02006                      /* Yield here so other interested threads can kick in. */
02007                      sched_yield();
02008                      if (res)
02009                         break;
02010 
02011                      ast_mutex_lock(&agentlock);
02012                      ast_mutex_lock(&p->lock);
02013                      if (p->lastdisc.tv_sec) {
02014                         if (ast_tvdiff_ms(ast_tvnow(), p->lastdisc) > 0) {
02015                            if (option_debug)
02016                               ast_log(LOG_DEBUG, "Wrapup time for %s expired!\n", p->agent);
02017                            p->lastdisc = ast_tv(0, 0);
02018                            if (p->ackcall > 1)
02019                               check_beep(p, 0);
02020                            else
02021                               check_availability(p, 0);
02022                         }
02023                      }
02024                      ast_mutex_unlock(&p->lock);
02025                      ast_mutex_unlock(&agentlock);
02026                      /* Synchronize channel ownership between call to agent and itself. */
02027                      ast_mutex_lock( &p->app_lock );
02028                      ast_mutex_lock(&p->lock);
02029                      p->owning_app = pthread_self();
02030                      ast_mutex_unlock(&p->lock);
02031                      if (p->ackcall > 1) 
02032                         res = agent_ack_sleep(p);
02033                      else
02034                         res = ast_safe_sleep_conditional( chan, 1000,
02035                                       agent_cont_sleep, p );
02036                      ast_mutex_unlock( &p->app_lock );
02037                      if ((p->ackcall > 1)  && (res == 1)) {
02038                         ast_mutex_lock(&agentlock);
02039                         ast_mutex_lock(&p->lock);
02040                         check_availability(p, 0);
02041                         ast_mutex_unlock(&p->lock);
02042                         ast_mutex_unlock(&agentlock);
02043                         res = 0;
02044                      }
02045                      sched_yield();
02046                   }
02047                   ast_mutex_lock(&p->lock);
02048                   if (res && p->owner) 
02049                      ast_log(LOG_WARNING, "Huh?  We broke out when there was still an owner?\n");
02050                   /* Log us off if appropriate */
02051                   if (p->chan == chan)
02052                      p->chan = NULL;
02053                   p->acknowledged = 0;
02054                   logintime = time(NULL) - p->loginstart;
02055                   p->loginstart = 0;
02056                   ast_mutex_unlock(&p->lock);
02057                   manager_event(EVENT_FLAG_AGENT, "Agentlogoff",
02058                            "Agent: %s\r\n"
02059                            "Logintime: %ld\r\n"
02060                            "Uniqueid: %s\r\n",
02061                            p->agent, logintime, chan->uniqueid);
02062                   ast_queue_log("NONE", chan->uniqueid, agent, "AGENTLOGOFF", "%s|%ld", chan->name, logintime);
02063                   if (option_verbose > 1)
02064                      ast_verbose(VERBOSE_PREFIX_2 "Agent '%s' logged out\n", p->agent);
02065                   /* If there is no owner, go ahead and kill it now */
02066                   ast_device_state_changed("Agent/%s", p->agent);
02067                   if (p->dead && !p->owner) {
02068                      ast_mutex_destroy(&p->lock);
02069                      ast_mutex_destroy(&p->app_lock);
02070                      free(p);
02071                   }
02072                }
02073                else {
02074                   ast_mutex_unlock(&p->lock);
02075                   p = NULL;
02076                }
02077                res = -1;
02078             } else {
02079                ast_mutex_unlock(&p->lock);
02080                errmsg = "agent-alreadyon";
02081                p = NULL;
02082             }
02083             break;
02084          }
02085          ast_mutex_unlock(&p->lock);
02086          p = p->next;
02087       }
02088       if (!p)
02089          ast_mutex_unlock(&agentlock);
02090 
02091       if (!res && (max_login_tries==0 || tries < max_login_tries))
02092          res = ast_app_getdata(chan, errmsg, user, sizeof(user) - 1, 0);
02093    }
02094       
02095    if (!res)
02096       res = ast_safe_sleep(chan, 500);
02097 
02098    /* AgentLogin() exit */
02099    if (!callbackmode) {
02100       LOCAL_USER_REMOVE(u);
02101       return -1;
02102    }
02103    /* AgentCallbackLogin() exit*/
02104    else {
02105       /* Set variables */
02106       if (login_state > 0) {
02107          pbx_builtin_setvar_helper(chan, "AGENTNUMBER", user);
02108          if (login_state==1) {
02109             pbx_builtin_setvar_helper(chan, "AGENTSTATUS", "on");
02110             pbx_builtin_setvar_helper(chan, "AGENTEXTEN", args.extension);
02111          }
02112          else {
02113             pbx_builtin_setvar_helper(chan, "AGENTSTATUS", "off");
02114          }
02115       }
02116       else {
02117          pbx_builtin_setvar_helper(chan, "AGENTSTATUS", "fail");
02118       }
02119       if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 1, chan->cid.cid_num)) {
02120          LOCAL_USER_REMOVE(u);
02121          return 0;
02122       }
02123       /* Do we need to play agent-goodbye now that we will be hanging up? */
02124       if (play_announcement) {
02125          if (!res)
02126             res = ast_safe_sleep(chan, 1000);
02127          res = ast_streamfile(chan, agent_goodbye, chan->language);
02128          if (!res)
02129             res = ast_waitstream(chan, "");
02130          if (!res)
02131             res = ast_safe_sleep(chan, 1000);
02132       }
02133    }
02134 
02135    LOCAL_USER_REMOVE(u);
02136    
02137    /* We should never get here if next priority exists when in callbackmode */
02138    return -1;
02139 }
02140 
02141 /**
02142  * Called by the AgentLogin application (from the dial plan).
02143  * 
02144  * @param chan
02145  * @param data
02146  * @returns
02147  * @sa callback_login_exec(), agentmonitoroutgoing_exec(), load_module().
02148  */
02149 static int login_exec(struct ast_channel *chan, void *data)
02150 {
02151    return __login_exec(chan, data, 0);
02152 }
02153 
02154 /**
02155  *  Called by the AgentCallbackLogin application (from the dial plan).
02156  * 
02157  * @param chan
02158  * @param data
02159  * @returns
02160  * @sa login_exec(), agentmonitoroutgoing_exec(), load_module().
02161  */
02162 static int callback_exec(struct ast_channel *chan, void *data)
02163 {
02164    return __login_exec(chan, data, 1);
02165 }
02166 
02167 /**
02168  * Sets an agent as logged in by callback in the Manager API.
02169  * It is registered on load_module() and it gets called by the manager backend.
02170  * @param s
02171  * @param m
02172  * @returns 
02173  * @sa action_agents(), action_agent_logoff(), load_module().
02174  */
02175 static int action_agent_callback_login(struct mansession *s, struct message *m)
02176 {
02177    char *agent = astman_get_header(m, "Agent");
02178    char *exten = astman_get_header(m, "Exten");
02179    char *context = astman_get_header(m, "Context");
02180    char *wrapuptime_s = astman_get_header(m, "WrapupTime");
02181    char *ackcall_s = astman_get_header(m, "AckCall");
02182    struct agent_pvt *p;
02183    int login_state = 0;
02184 
02185    if (ast_strlen_zero(agent)) {
02186       astman_send_error(s, m, "No agent specified");
02187       return 0;
02188    }
02189 
02190    if (ast_strlen_zero(exten)) {
02191       astman_send_error(s, m, "No extension specified");
02192       return 0;
02193    }
02194 
02195    ast_mutex_lock(&agentlock);
02196    p = agents;
02197    while(p) {
02198       if (strcmp(p->agent, agent) || p->pending) {
02199          p = p->next;
02200          continue;
02201       }
02202       if (p->chan) {
02203          login_state = 2; /* already logged in (and on the phone)*/
02204          break;
02205       }
02206       ast_mutex_lock(&p->lock);
02207       login_state = 1; /* Successful Login */
02208       
02209       if (ast_strlen_zero(context))
02210          ast_copy_string(p->loginchan, exten, sizeof(p->loginchan));
02211       else
02212          snprintf(p->loginchan, sizeof(p->loginchan), "%s@%s", exten, context);
02213 
02214       if (!ast_strlen_zero(wrapuptime_s)) {
02215          p->wrapuptime = atoi(wrapuptime_s);
02216          if (p->wrapuptime < 0)
02217             p->wrapuptime = 0;
02218       }
02219 
02220       if (ast_true(ackcall_s))
02221          p->ackcall = 1;
02222       else
02223          p->ackcall = 0;
02224 
02225       if (p->loginstart == 0)
02226          time(&p->loginstart);
02227       manager_event(EVENT_FLAG_AGENT, "Agentcallbacklogin",
02228                "Agent: %s\r\n"
02229                "Loginchan: %s\r\n",
02230                p->agent, p->loginchan);
02231       ast_queue_log("NONE", "NONE", agent, "AGENTCALLBACKLOGIN", "%s", p->loginchan);
02232       if (option_verbose > 1)
02233          ast_verbose(VERBOSE_PREFIX_2 "Callback Agent '%s' logged in on %s\n", p->agent, p->loginchan);
02234       ast_device_state_changed("Agent/%s", p->agent);
02235       ast_mutex_unlock(&p->lock);
02236       p = p->next;
02237       if (persistent_agents)
02238          dump_agents();
02239    }
02240    ast_mutex_unlock(&agentlock);
02241 
02242    if (login_state == 1)
02243       astman_send_ack(s, m, "Agent logged in");
02244    else if (login_state == 0)
02245       astman_send_error(s, m, "No such agent");
02246    else if (login_state == 2)
02247       astman_send_error(s, m, "Agent already logged in");
02248 
02249    return 0;
02250 }
02251 
02252 /**
02253  *  Called by the AgentMonitorOutgoing application (from the dial plan).
02254  *
02255  * @param chan
02256  * @param data
02257  * @returns
02258  * @sa login_exec(), callback_login_exec(), load_module().
02259  */
02260 static int agentmonitoroutgoing_exec(struct ast_channel *chan, void *data)
02261 {
02262    int exitifnoagentid = 0;
02263    int nowarnings = 0;
02264    int changeoutgoing = 0;
02265    int res = 0;
02266    char agent[AST_MAX_AGENT], *tmp;
02267 
02268    if (data) {
02269       if (strchr(data, 'd'))
02270          exitifnoagentid = 1;
02271       if (strchr(data, 'n'))
02272          nowarnings = 1;
02273       if (strchr(data, 'c'))
02274          changeoutgoing = 1;
02275    }
02276    if (chan->cid.cid_num) {
02277       char agentvar[AST_MAX_BUF];
02278       snprintf(agentvar, sizeof(agentvar), "%s_%s", GETAGENTBYCALLERID, chan->cid.cid_num);
02279       if ((tmp = pbx_builtin_getvar_helper(NULL, agentvar))) {
02280          struct agent_pvt *p = agents;
02281          ast_copy_string(agent, tmp, sizeof(agent));
02282          ast_mutex_lock(&agentlock);
02283          while (p) {
02284             if (!strcasecmp(p->agent, tmp)) {
02285                if (changeoutgoing) snprintf(chan->cdr->channel, sizeof(chan->cdr->channel), "Agent/%s", p->agent);
02286                __agent_start_monitoring(chan, p, 1);
02287                break;
02288             }
02289             p = p->next;
02290          }
02291          ast_mutex_unlock(&agentlock);
02292          
02293       } else {
02294          res = -1;
02295          if (!nowarnings)
02296             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);
02297       }
02298    } else {
02299       res = -1;
02300       if (!nowarnings)
02301          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");
02302    }
02303    /* check if there is n + 101 priority */
02304    if (res) {
02305       if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 101, chan->cid.cid_num)) {
02306          chan->priority+=100;
02307          if (option_verbose > 2)
02308             ast_verbose(VERBOSE_PREFIX_3 "Going to %d priority because there is no callerid or the agentid cannot be found.\n",chan->priority);
02309       }
02310       else if (exitifnoagentid)
02311          return res;
02312    }
02313    return 0;
02314 }
02315 
02316 /**
02317  * Dump AgentCallbackLogin agents to the database for persistence
02318  */
02319 static void dump_agents(void)
02320 {
02321    struct agent_pvt *cur_agent = NULL;
02322    char buf[256];
02323 
02324    for (cur_agent = agents; cur_agent; cur_agent = cur_agent->next) {
02325       if (cur_agent->chan)
02326          continue;
02327 
02328       if (!ast_strlen_zero(cur_agent->loginchan)) {
02329          snprintf(buf, sizeof(buf), "%s;%s", cur_agent->loginchan, cur_agent->logincallerid);
02330          if (ast_db_put(pa_family, cur_agent->agent, buf))
02331             ast_log(LOG_WARNING, "failed to create persistent entry!\n");
02332          else if (option_debug)
02333             ast_log(LOG_DEBUG, "Saved Agent: %s on %s\n", cur_agent->agent, cur_agent->loginchan);
02334       } else {
02335          /* Delete -  no agent or there is an error */
02336          ast_db_del(pa_family, cur_agent->agent);
02337       }
02338    }
02339 }
02340 
02341 /**
02342  * Reload the persistent agents from astdb.
02343  */
02344 static void reload_agents(void)
02345 {
02346    char *agent_num;
02347    struct ast_db_entry *db_tree;
02348    struct ast_db_entry *entry;
02349    struct agent_pvt *cur_agent;
02350    char agent_data[256];
02351    char *parse;
02352    char *agent_chan;
02353    char *agent_callerid;
02354 
02355    db_tree = ast_db_gettree(pa_family, NULL);
02356 
02357    ast_mutex_lock(&agentlock);
02358    for (entry = db_tree; entry; entry = entry->next) {
02359       agent_num = entry->key + strlen(pa_family) + 2;
02360       cur_agent = agents;
02361       while (cur_agent) {
02362          ast_mutex_lock(&cur_agent->lock);
02363          if (strcmp(agent_num, cur_agent->agent) == 0)
02364             break;
02365          ast_mutex_unlock(&cur_agent->lock);
02366          cur_agent = cur_agent->next;
02367       }
02368       if (!cur_agent) {
02369          ast_db_del(pa_family, agent_num);
02370          continue;
02371       } else
02372          ast_mutex_unlock(&cur_agent->lock);
02373       if (!ast_db_get(pa_family, agent_num, agent_data, sizeof(agent_data)-1)) {
02374          if (option_debug)
02375             ast_log(LOG_DEBUG, "Reload Agent: %s on %s\n", cur_agent->agent, agent_data);
02376          parse = agent_data;
02377          agent_chan = strsep(&parse, ";");
02378          agent_callerid = strsep(&parse, ";");
02379          ast_copy_string(cur_agent->loginchan, agent_chan, sizeof(cur_agent->loginchan));
02380          if (agent_callerid) {
02381             ast_copy_string(cur_agent->logincallerid, agent_callerid, sizeof(cur_agent->logincallerid));
02382             set_agentbycallerid(cur_agent->logincallerid, cur_agent->agent);
02383          } else
02384             cur_agent->logincallerid[0] = '\0';
02385          if (cur_agent->loginstart == 0)
02386             time(&cur_agent->loginstart);
02387          ast_device_state_changed("Agent/%s", cur_agent->agent);  
02388       }
02389    }
02390    ast_mutex_unlock(&agentlock);
02391    if (db_tree) {
02392       ast_log(LOG_NOTICE, "Agents successfully reloaded from database.\n");
02393       ast_db_freetree(db_tree);
02394    }
02395 }
02396 
02397 /*--- agent_devicestate: Part of PBX channel interface ---*/
02398 static int agent_devicestate(void *data)
02399 {
02400    struct agent_pvt *p;
02401    char *s;
02402    ast_group_t groupmatch;
02403    int groupoff;
02404    int waitforagent=0;
02405    int res = AST_DEVICE_INVALID;
02406    
02407    s = data;
02408    if ((s[0] == '@') && (sscanf(s + 1, "%30d", &groupoff) == 1)) {
02409       groupmatch = (1 << groupoff);
02410    } else if ((s[0] == ':') && (sscanf(s + 1, "%30d", &groupoff) == 1)) {
02411       groupmatch = (1 << groupoff);
02412       waitforagent = 1;
02413    } else {
02414       groupmatch = 0;
02415    }
02416 
02417    /* Check actual logged in agents first */
02418    ast_mutex_lock(&agentlock);
02419    p = agents;
02420    while(p) {
02421       ast_mutex_lock(&p->lock);
02422       if (!p->pending && ((groupmatch && (p->group & groupmatch)) || !strcmp(data, p->agent))) {
02423          if (p->owner) {
02424             if (res != AST_DEVICE_INUSE)
02425                res = AST_DEVICE_BUSY;
02426          } else {
02427             if (res == AST_DEVICE_BUSY)
02428                res = AST_DEVICE_INUSE;
02429             if (p->chan || !ast_strlen_zero(p->loginchan)) {
02430                if (res == AST_DEVICE_INVALID)
02431                   res = AST_DEVICE_UNKNOWN;
02432             } else if (res == AST_DEVICE_INVALID)  
02433                res = AST_DEVICE_UNAVAILABLE;
02434          }
02435          if (!strcmp(data, p->agent)) {
02436             ast_mutex_unlock(&p->lock);
02437             break;
02438          }
02439       }
02440       ast_mutex_unlock(&p->lock);
02441       p = p->next;
02442    }
02443    ast_mutex_unlock(&agentlock);
02444    return res;
02445 }
02446 
02447 /**
02448  * Initialize the Agents module.
02449  * This function is being called by Asterisk when loading the module. Among other thing it registers applications, cli commands and reads the cofiguration file.
02450  *
02451  * @returns int Always 0.
02452  */
02453 int load_module()
02454 {
02455    /* Make sure we can register our agent channel type */
02456    if (ast_channel_register(&agent_tech)) {
02457       ast_log(LOG_ERROR, "Unable to register channel class %s\n", channeltype);
02458       return -1;
02459    }
02460    /* Dialplan applications */
02461    ast_register_application(app, login_exec, synopsis, descrip);
02462    ast_register_application(app2, callback_exec, synopsis2, descrip2);
02463    ast_register_application(app3, agentmonitoroutgoing_exec, synopsis3, descrip3);
02464    /* Manager commands */
02465    ast_manager_register2("Agents", EVENT_FLAG_AGENT, action_agents, "Lists agents and their status", mandescr_agents);
02466    ast_manager_register2("AgentLogoff", EVENT_FLAG_AGENT, action_agent_logoff, "Sets an agent as no longer logged in", mandescr_agent_logoff);
02467    ast_manager_register2("AgentCallbackLogin", EVENT_FLAG_AGENT, action_agent_callback_login, "Sets an agent as logged in by callback", mandescr_agent_callback_login);
02468    /* CLI Application */
02469    ast_cli_register(&cli_show_agents);
02470    ast_cli_register(&cli_agent_logoff);
02471    /* Read in the config */
02472    read_agent_config();
02473    if (persistent_agents)
02474       reload_agents();
02475    return 0;
02476 }
02477 
02478 int reload()
02479 {
02480    read_agent_config();
02481    if (persistent_agents)
02482       reload_agents();
02483    return 0;
02484 }
02485 
02486 int unload_module()
02487 {
02488    struct agent_pvt *p;
02489    /* First, take us out of the channel loop */
02490    /* Unregister CLI application */
02491    ast_cli_unregister(&cli_show_agents);
02492    ast_cli_unregister(&cli_agent_logoff);
02493    /* Unregister dialplan applications */
02494    ast_unregister_application(app);
02495    ast_unregister_application(app2);
02496    ast_unregister_application(app3);
02497    /* Unregister manager command */
02498    ast_manager_unregister("Agents");
02499    ast_manager_unregister("AgentLogoff");
02500    ast_manager_unregister("AgentCallbackLogin");
02501    /* Unregister channel */
02502    ast_channel_unregister(&agent_tech);
02503    if (!ast_mutex_lock(&agentlock)) {
02504       /* Hangup all interfaces if they have an owner */
02505       p = agents;
02506       while(p) {
02507          if (p->owner)
02508             ast_softhangup(p->owner, AST_SOFTHANGUP_APPUNLOAD);
02509          p = p->next;
02510       }
02511       agents = NULL;
02512       ast_mutex_unlock(&agentlock);
02513    } else {
02514       ast_log(LOG_WARNING, "Unable to lock the monitor\n");
02515       return -1;
02516    }     
02517    return 0;
02518 }
02519 
02520 int usecount()
02521 {
02522    return usecnt;
02523 }
02524 
02525 char *key()
02526 {
02527    return ASTERISK_GPL_KEY;
02528 }
02529 
02530 char *description()
02531 {
02532    return (char *) desc;
02533 }
02534 

Generated on Wed Oct 28 15:47:50 2009 for Asterisk - the Open Source PBX by  doxygen 1.5.6