Sun Aug 6 15:02:29 2006

Asterisk developer's documentation


Main Page | Modules | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals | Related Pages

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

Generated on Sun Aug 6 15:02:29 2006 for Asterisk - the Open Source PBX by  doxygen 1.4.2