mirror of
https://github.com/asterisk/asterisk.git
synced 2025-11-01 19:43:03 +00:00
- Use our standard of doxygen comments, not javadoc style
- small reformatting changes git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@34093 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
@@ -140,16 +140,14 @@ static const char mandescr_agent_callback_login[] =
|
|||||||
|
|
||||||
static char moh[80] = "default";
|
static char moh[80] = "default";
|
||||||
|
|
||||||
#define AST_MAX_AGENT 80 /**< Agent ID or Password max length */
|
#define AST_MAX_AGENT 80 /*!< Agent ID or Password max length */
|
||||||
#define AST_MAX_BUF 256
|
#define AST_MAX_BUF 256
|
||||||
#define AST_MAX_FILENAME_LEN 256
|
#define AST_MAX_FILENAME_LEN 256
|
||||||
|
|
||||||
/** Persistent Agents astdb family */
|
static const char pa_family[] = "/Agents"; /*!< Persistent Agents astdb family */
|
||||||
static const char pa_family[] = "/Agents";
|
#define PA_MAX_LEN 2048 /*!< The maximum length of each persistent member agent database entry */
|
||||||
/** The maximum length of each persistent member agent database entry */
|
|
||||||
#define PA_MAX_LEN 2048
|
static int persistent_agents = 0; /*!< queues.conf [general] option */
|
||||||
/** queues.conf [general] option */
|
|
||||||
static int persistent_agents = 0;
|
|
||||||
static void dump_agents(void);
|
static void dump_agents(void);
|
||||||
|
|
||||||
static ast_group_t group;
|
static ast_group_t group;
|
||||||
@@ -173,37 +171,35 @@ static char beep[AST_MAX_BUF] = "beep";
|
|||||||
|
|
||||||
#define GETAGENTBYCALLERID "AGENTBYCALLERID"
|
#define GETAGENTBYCALLERID "AGENTBYCALLERID"
|
||||||
|
|
||||||
/**
|
/*! \brief * Structure representing an agent. */
|
||||||
* Structure representing an agent.
|
|
||||||
*/
|
|
||||||
struct agent_pvt {
|
struct agent_pvt {
|
||||||
ast_mutex_t lock; /**< Channel private lock */
|
ast_mutex_t lock; /*!< Channel private lock */
|
||||||
int dead; /**< Poised for destruction? */
|
int dead; /*!< Poised for destruction? */
|
||||||
int pending; /**< Not a real agent -- just pending a match */
|
int pending; /*!< Not a real agent -- just pending a match */
|
||||||
int abouttograb; /**< About to grab */
|
int abouttograb; /*!< About to grab */
|
||||||
int autologoff; /**< Auto timeout time */
|
int autologoff; /*!< Auto timeout time */
|
||||||
int ackcall; /**< ackcall */
|
int ackcall; /*!< ackcall */
|
||||||
time_t loginstart; /**< When agent first logged in (0 when logged off) */
|
time_t loginstart; /*!< When agent first logged in (0 when logged off) */
|
||||||
time_t start; /**< When call started */
|
time_t start; /*!< When call started */
|
||||||
struct timeval lastdisc; /**< When last disconnected */
|
struct timeval lastdisc; /*!< When last disconnected */
|
||||||
int wrapuptime; /**< Wrapup time in ms */
|
int wrapuptime; /*!< Wrapup time in ms */
|
||||||
ast_group_t group; /**< Group memberships */
|
ast_group_t group; /*!< Group memberships */
|
||||||
int acknowledged; /**< Acknowledged */
|
int acknowledged; /*!< Acknowledged */
|
||||||
char moh[80]; /**< Which music on hold */
|
char moh[80]; /*!< Which music on hold */
|
||||||
char agent[AST_MAX_AGENT]; /**< Agent ID */
|
char agent[AST_MAX_AGENT]; /*!< Agent ID */
|
||||||
char password[AST_MAX_AGENT]; /**< Password for Agent login */
|
char password[AST_MAX_AGENT]; /*!< Password for Agent login */
|
||||||
char name[AST_MAX_AGENT];
|
char name[AST_MAX_AGENT];
|
||||||
ast_mutex_t app_lock; /**< Synchronization between owning applications */
|
ast_mutex_t app_lock; /*!< Synchronization between owning applications */
|
||||||
volatile pthread_t owning_app; /**< Owning application thread id */
|
volatile pthread_t owning_app; /*!< Owning application thread id */
|
||||||
volatile int app_sleep_cond; /**< Sleep condition for the login app */
|
volatile int app_sleep_cond; /*!< Sleep condition for the login app */
|
||||||
struct ast_channel *owner; /**< Agent */
|
struct ast_channel *owner; /*!< Agent */
|
||||||
char loginchan[80]; /**< channel they logged in from */
|
char loginchan[80]; /*!< channel they logged in from */
|
||||||
char logincallerid[80]; /**< Caller ID they had when they logged in */
|
char logincallerid[80]; /*!< Caller ID they had when they logged in */
|
||||||
struct ast_channel *chan; /**< Channel we use */
|
struct ast_channel *chan; /*!< Channel we use */
|
||||||
AST_LIST_ENTRY(agent_pvt) list; /**< Next Agent in the linked list. */
|
AST_LIST_ENTRY(agent_pvt) list; /*!< Next Agent in the linked list. */
|
||||||
};
|
};
|
||||||
|
|
||||||
static AST_LIST_HEAD_STATIC(agents, agent_pvt); /**< Holds the list of agents (loaded form agents.conf). */
|
static AST_LIST_HEAD_STATIC(agents, agent_pvt); /*!< Holds the list of agents (loaded form agents.conf). */
|
||||||
|
|
||||||
#define CHECK_FORMATS(ast, p) do { \
|
#define CHECK_FORMATS(ast, p) do { \
|
||||||
if (p->chan) {\
|
if (p->chan) {\
|
||||||
@@ -222,7 +218,7 @@ static AST_LIST_HEAD_STATIC(agents, agent_pvt); /**< Holds the list of agents (l
|
|||||||
} \
|
} \
|
||||||
} while(0)
|
} while(0)
|
||||||
|
|
||||||
/* Cleanup moves all the relevant FD's from the 2nd to the first, but retains things
|
/*! \brief Cleanup moves all the relevant FD's from the 2nd to the first, but retains things
|
||||||
properly for a timingfd XXX This might need more work if agents were logged in as agents or other
|
properly for a timingfd XXX This might need more work if agents were logged in as agents or other
|
||||||
totally impractical combinations XXX */
|
totally impractical combinations XXX */
|
||||||
|
|
||||||
@@ -237,6 +233,7 @@ static AST_LIST_HEAD_STATIC(agents, agent_pvt); /**< Holds the list of agents (l
|
|||||||
} \
|
} \
|
||||||
} while(0)
|
} while(0)
|
||||||
|
|
||||||
|
/*--- Forward declarations */
|
||||||
static struct ast_channel *agent_request(const char *type, int format, void *data, int *cause);
|
static struct ast_channel *agent_request(const char *type, int format, void *data, int *cause);
|
||||||
static int agent_devicestate(void *data);
|
static int agent_devicestate(void *data);
|
||||||
static void agent_logoff_maintenance(struct agent_pvt *p, char *loginchan, long logintime, const char *uniqueid, char *logcommand);
|
static void agent_logoff_maintenance(struct agent_pvt *p, char *loginchan, long logintime, const char *uniqueid, char *logcommand);
|
||||||
@@ -253,6 +250,7 @@ static int agent_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
|
|||||||
static struct ast_channel *agent_bridgedchannel(struct ast_channel *chan, struct ast_channel *bridge);
|
static struct ast_channel *agent_bridgedchannel(struct ast_channel *chan, struct ast_channel *bridge);
|
||||||
static void set_agentbycallerid(const char *callerid, const char *agent);
|
static void set_agentbycallerid(const char *callerid, const char *agent);
|
||||||
|
|
||||||
|
/*! \brief Channel interface description for PBX integration */
|
||||||
static const struct ast_channel_tech agent_tech = {
|
static const struct ast_channel_tech agent_tech = {
|
||||||
.type = "Agent",
|
.type = "Agent",
|
||||||
.description = tdesc,
|
.description = tdesc,
|
||||||
@@ -273,13 +271,13 @@ static const struct ast_channel_tech agent_tech = {
|
|||||||
.bridged_channel = agent_bridgedchannel,
|
.bridged_channel = agent_bridgedchannel,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/*!
|
||||||
* Adds an agent to the global list of agents.
|
* Adds an agent to the global list of agents.
|
||||||
*
|
*
|
||||||
* @param agent A string with the username, password and real name of an agent. As defined in agents.conf. Example: "13,169,John Smith"
|
* \param agent A string with the username, password and real name of an agent. As defined in agents.conf. Example: "13,169,John Smith"
|
||||||
* @param pending If it is pending or not.
|
* \param pending If it is pending or not.
|
||||||
* @return The just created agent.
|
* @return The just created agent.
|
||||||
* @sa agent_pvt, agents.
|
* \sa agent_pvt, agents.
|
||||||
*/
|
*/
|
||||||
static struct agent_pvt *add_agent(char *agent, int pending)
|
static struct agent_pvt *add_agent(char *agent, int pending)
|
||||||
{
|
{
|
||||||
@@ -365,10 +363,10 @@ static struct agent_pvt *add_agent(char *agent, int pending)
|
|||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/*!
|
||||||
* Deletes an agent after doing some clean up.
|
* Deletes an agent after doing some clean up.
|
||||||
* Further documentation: How safe is this function ? What state should the agent be to be cleaned.
|
* Further documentation: How safe is this function ? What state should the agent be to be cleaned.
|
||||||
* @param p Agent to be deleted.
|
* \param p Agent to be deleted.
|
||||||
* @returns Always 0.
|
* @returns Always 0.
|
||||||
*/
|
*/
|
||||||
static int agent_cleanup(struct agent_pvt *p)
|
static int agent_cleanup(struct agent_pvt *p)
|
||||||
@@ -972,7 +970,7 @@ static struct ast_channel *agent_new(struct agent_pvt *p, int state)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/*!
|
||||||
* Read configuration data. The file named agents.conf.
|
* Read configuration data. The file named agents.conf.
|
||||||
*
|
*
|
||||||
* @returns Always 0, or so it seems.
|
* @returns Always 0, or so it seems.
|
||||||
@@ -1344,13 +1342,13 @@ static force_inline int powerof(unsigned int d)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/*!
|
||||||
* Lists agents and their status to the Manager API.
|
* Lists agents and their status to the Manager API.
|
||||||
* It is registered on load_module() and it gets called by the manager backend.
|
* It is registered on load_module() and it gets called by the manager backend.
|
||||||
* @param s
|
* \param s
|
||||||
* @param m
|
* \param m
|
||||||
* @returns
|
* @returns
|
||||||
* @sa action_agent_logoff(), action_agent_callback_login(), load_module().
|
* \sa action_agent_logoff(), action_agent_callback_login(), load_module().
|
||||||
*/
|
*/
|
||||||
static int action_agents(struct mansession *s, struct message *m)
|
static int action_agents(struct mansession *s, struct message *m)
|
||||||
{
|
{
|
||||||
@@ -1513,13 +1511,13 @@ static int agent_logoff_cmd(int fd, int argc, char **argv)
|
|||||||
return RESULT_SUCCESS;
|
return RESULT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/*!
|
||||||
* Sets an agent as no longer logged in in the Manager API.
|
* Sets an agent as no longer logged in in the Manager API.
|
||||||
* It is registered on load_module() and it gets called by the manager backend.
|
* It is registered on load_module() and it gets called by the manager backend.
|
||||||
* @param s
|
* \param s
|
||||||
* @param m
|
* \param m
|
||||||
* @returns
|
* @returns
|
||||||
* @sa action_agents(), action_agent_callback_login(), load_module().
|
* \sa action_agents(), action_agent_callback_login(), load_module().
|
||||||
*/
|
*/
|
||||||
static int action_agent_logoff(struct mansession *s, struct message *m)
|
static int action_agent_logoff(struct mansession *s, struct message *m)
|
||||||
{
|
{
|
||||||
@@ -1565,7 +1563,7 @@ static char *complete_agent_logoff_cmd(const char *line, const char *word, int p
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/*!
|
||||||
* Show agents in cli.
|
* Show agents in cli.
|
||||||
*/
|
*/
|
||||||
static int agents_show(int fd, int argc, char **argv)
|
static int agents_show(int fd, int argc, char **argv)
|
||||||
@@ -2155,39 +2153,39 @@ static int __login_exec(struct ast_channel *chan, void *data, int callbackmode)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/*!
|
||||||
* Called by the AgentLogin application (from the dial plan).
|
* Called by the AgentLogin application (from the dial plan).
|
||||||
*
|
*
|
||||||
* @param chan
|
* \param chan
|
||||||
* @param data
|
* \param data
|
||||||
* @returns
|
* @returns
|
||||||
* @sa callback_login_exec(), agentmonitoroutgoing_exec(), load_module().
|
* \sa callback_login_exec(), agentmonitoroutgoing_exec(), load_module().
|
||||||
*/
|
*/
|
||||||
static int login_exec(struct ast_channel *chan, void *data)
|
static int login_exec(struct ast_channel *chan, void *data)
|
||||||
{
|
{
|
||||||
return __login_exec(chan, data, 0);
|
return __login_exec(chan, data, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/*!
|
||||||
* Called by the AgentCallbackLogin application (from the dial plan).
|
* Called by the AgentCallbackLogin application (from the dial plan).
|
||||||
*
|
*
|
||||||
* @param chan
|
* \param chan
|
||||||
* @param data
|
* \param data
|
||||||
* @returns
|
* @returns
|
||||||
* @sa login_exec(), agentmonitoroutgoing_exec(), load_module().
|
* \sa login_exec(), agentmonitoroutgoing_exec(), load_module().
|
||||||
*/
|
*/
|
||||||
static int callback_exec(struct ast_channel *chan, void *data)
|
static int callback_exec(struct ast_channel *chan, void *data)
|
||||||
{
|
{
|
||||||
return __login_exec(chan, data, 1);
|
return __login_exec(chan, data, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/*!
|
||||||
* Sets an agent as logged in by callback in the Manager API.
|
* Sets an agent as logged in by callback in the Manager API.
|
||||||
* It is registered on load_module() and it gets called by the manager backend.
|
* It is registered on load_module() and it gets called by the manager backend.
|
||||||
* @param s
|
* \param s
|
||||||
* @param m
|
* \param m
|
||||||
* @returns
|
* @returns
|
||||||
* @sa action_agents(), action_agent_logoff(), load_module().
|
* \sa action_agents(), action_agent_logoff(), load_module().
|
||||||
*/
|
*/
|
||||||
static int action_agent_callback_login(struct mansession *s, struct message *m)
|
static int action_agent_callback_login(struct mansession *s, struct message *m)
|
||||||
{
|
{
|
||||||
@@ -2263,13 +2261,13 @@ static int action_agent_callback_login(struct mansession *s, struct message *m)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/*!
|
||||||
* Called by the AgentMonitorOutgoing application (from the dial plan).
|
* \brief Called by the AgentMonitorOutgoing application (from the dial plan).
|
||||||
*
|
*
|
||||||
* @param chan
|
* \param chan
|
||||||
* @param data
|
* \param data
|
||||||
* @returns
|
* \returns
|
||||||
* @sa login_exec(), callback_login_exec(), load_module().
|
* \sa login_exec(), callback_login_exec(), load_module().
|
||||||
*/
|
*/
|
||||||
static int agentmonitoroutgoing_exec(struct ast_channel *chan, void *data)
|
static int agentmonitoroutgoing_exec(struct ast_channel *chan, void *data)
|
||||||
{
|
{
|
||||||
@@ -2315,6 +2313,7 @@ static int agentmonitoroutgoing_exec(struct ast_channel *chan, void *data)
|
|||||||
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");
|
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");
|
||||||
}
|
}
|
||||||
/* check if there is n + 101 priority */
|
/* check if there is n + 101 priority */
|
||||||
|
/*! \todo XXX Needs to check option priorityjump etc etc */
|
||||||
if (res) {
|
if (res) {
|
||||||
if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 101, chan->cid.cid_num)) {
|
if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 101, chan->cid.cid_num)) {
|
||||||
chan->priority+=100;
|
chan->priority+=100;
|
||||||
@@ -2327,8 +2326,8 @@ static int agentmonitoroutgoing_exec(struct ast_channel *chan, void *data)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/*!
|
||||||
* Dump AgentCallbackLogin agents to the database for persistence
|
* \brief Dump AgentCallbackLogin agents to the ASTdb database for persistence
|
||||||
*/
|
*/
|
||||||
static void dump_agents(void)
|
static void dump_agents(void)
|
||||||
{
|
{
|
||||||
@@ -2342,7 +2341,7 @@ static void dump_agents(void)
|
|||||||
if (!ast_strlen_zero(cur_agent->loginchan)) {
|
if (!ast_strlen_zero(cur_agent->loginchan)) {
|
||||||
snprintf(buf, sizeof(buf), "%s;%s", cur_agent->loginchan, cur_agent->logincallerid);
|
snprintf(buf, sizeof(buf), "%s;%s", cur_agent->loginchan, cur_agent->logincallerid);
|
||||||
if (ast_db_put(pa_family, cur_agent->agent, buf))
|
if (ast_db_put(pa_family, cur_agent->agent, buf))
|
||||||
ast_log(LOG_WARNING, "failed to create persistent entry!\n");
|
ast_log(LOG_WARNING, "failed to create persistent entry in ASTdb for %s!\n", buf);
|
||||||
else if (option_debug)
|
else if (option_debug)
|
||||||
ast_log(LOG_DEBUG, "Saved Agent: %s on %s\n", cur_agent->agent, cur_agent->loginchan);
|
ast_log(LOG_DEBUG, "Saved Agent: %s on %s\n", cur_agent->agent, cur_agent->loginchan);
|
||||||
} else {
|
} else {
|
||||||
@@ -2352,8 +2351,8 @@ static void dump_agents(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/*!
|
||||||
* Reload the persistent agents from astdb.
|
* \brief Reload the persistent agents from astdb.
|
||||||
*/
|
*/
|
||||||
static void reload_agents(void)
|
static void reload_agents(void)
|
||||||
{
|
{
|
||||||
@@ -2406,7 +2405,7 @@ static void reload_agents(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! \brief Part of PBX channel interface ---*/
|
/*! \brief Part of PBX channel interface */
|
||||||
static int agent_devicestate(void *data)
|
static int agent_devicestate(void *data)
|
||||||
{
|
{
|
||||||
struct agent_pvt *p;
|
struct agent_pvt *p;
|
||||||
@@ -2495,11 +2494,10 @@ static int function_agent(struct ast_channel *chan, char *cmd, char *data, char
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!strcasecmp(args.item, "status")) {
|
if (!strcasecmp(args.item, "status")) {
|
||||||
if (agent->chan || !ast_strlen_zero(agent->loginchan)) {
|
char *status = "LOGGEDOUT";
|
||||||
ast_copy_string(buf, "LOGGEDIN", len);
|
if (agent->chan || !ast_strlen_zero(agent->loginchan))
|
||||||
} else {
|
status = "LOGGEDIN";
|
||||||
ast_copy_string(buf, "LOGGEDOUT", len);
|
ast_copy_string(buf, status, len);
|
||||||
}
|
|
||||||
} else if (!strcasecmp(args.item, "password")) {
|
} else if (!strcasecmp(args.item, "password")) {
|
||||||
ast_copy_string(buf, agent->password, len);
|
ast_copy_string(buf, agent->password, len);
|
||||||
} else if (!strcasecmp(args.item, "name")) {
|
} else if (!strcasecmp(args.item, "name")) {
|
||||||
@@ -2536,11 +2534,12 @@ struct ast_custom_function agent_function = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/*!
|
||||||
* Initialize the Agents module.
|
* \brief Initialize the Agents module.
|
||||||
* This function is being called by Asterisk when loading the module. Among other thing it registers applications, cli commands and reads the cofiguration file.
|
* This function is being called by Asterisk when loading the module.
|
||||||
|
* Among other things it registers applications, cli commands and reads the cofiguration file.
|
||||||
*
|
*
|
||||||
* @returns int Always 0.
|
* \returns int Always 0.
|
||||||
*/
|
*/
|
||||||
static int load_module(void *mod)
|
static int load_module(void *mod)
|
||||||
{
|
{
|
||||||
@@ -2554,16 +2553,20 @@ static int load_module(void *mod)
|
|||||||
ast_register_application(app, login_exec, synopsis, descrip);
|
ast_register_application(app, login_exec, synopsis, descrip);
|
||||||
ast_register_application(app2, callback_exec, synopsis2, descrip2);
|
ast_register_application(app2, callback_exec, synopsis2, descrip2);
|
||||||
ast_register_application(app3, agentmonitoroutgoing_exec, synopsis3, descrip3);
|
ast_register_application(app3, agentmonitoroutgoing_exec, synopsis3, descrip3);
|
||||||
|
|
||||||
/* Manager commands */
|
/* Manager commands */
|
||||||
ast_manager_register2("Agents", EVENT_FLAG_AGENT, action_agents, "Lists agents and their status", mandescr_agents);
|
ast_manager_register2("Agents", EVENT_FLAG_AGENT, action_agents, "Lists agents and their status", mandescr_agents);
|
||||||
ast_manager_register2("AgentLogoff", EVENT_FLAG_AGENT, action_agent_logoff, "Sets an agent as no longer logged in", mandescr_agent_logoff);
|
ast_manager_register2("AgentLogoff", EVENT_FLAG_AGENT, action_agent_logoff, "Sets an agent as no longer logged in", mandescr_agent_logoff);
|
||||||
ast_manager_register2("AgentCallbackLogin", EVENT_FLAG_AGENT, action_agent_callback_login, "Sets an agent as logged in by callback", mandescr_agent_callback_login);
|
ast_manager_register2("AgentCallbackLogin", EVENT_FLAG_AGENT, action_agent_callback_login, "Sets an agent as logged in by callback", mandescr_agent_callback_login);
|
||||||
|
|
||||||
/* CLI Commands */
|
/* CLI Commands */
|
||||||
ast_cli_register(&cli_show_agents);
|
ast_cli_register(&cli_show_agents);
|
||||||
ast_cli_register(&cli_show_agents_online);
|
ast_cli_register(&cli_show_agents_online);
|
||||||
ast_cli_register(&cli_agent_logoff);
|
ast_cli_register(&cli_agent_logoff);
|
||||||
|
|
||||||
/* Dialplan Functions */
|
/* Dialplan Functions */
|
||||||
ast_custom_function_register(&agent_function);
|
ast_custom_function_register(&agent_function);
|
||||||
|
|
||||||
/* Read in the config */
|
/* Read in the config */
|
||||||
read_agent_config();
|
read_agent_config();
|
||||||
if (persistent_agents)
|
if (persistent_agents)
|
||||||
|
|||||||
Reference in New Issue
Block a user