? Doxyfile ? agents-and-realtime.path ? agents-and-realtime2.patch ? agents-and-realtime2.path ? agents-and-realtime3.patch ? agents-and-realtime4.patch ? asterisk.kdevelop ? asterisk.kdevelop.filelist ? asterisk.kdevelop.pcs ? asterisk.kdevses ? chan_agent-realtime-agents.patch ? core.18540 ? core.20754 ? trivial-fix.patch ? doc/api Index: channels/chan_agent.c =================================================================== RCS file: /usr/cvsroot/asterisk/channels/chan_agent.c,v retrieving revision 1.168 diff -u -r1.168 chan_agent.c --- channels/chan_agent.c 8 Nov 2005 04:02:35 -0000 1.168 +++ channels/chan_agent.c 9 Nov 2005 13:50:18 -0000 @@ -74,48 +74,60 @@ static const char tdesc[] = "Call Agent Proxy Channel"; static const char config[] = "agents.conf"; -static const char app[] = "AgentLogin"; -static const char app2[] = "AgentCallbackLogin"; -static const char app3[] = "AgentMonitorOutgoing"; - -static const char synopsis[] = "Call agent login"; -static const char synopsis2[] = "Call agent callback login"; -static const char synopsis3[] = "Record agent's outgoing call"; - -static const char descrip[] = -" AgentLogin([AgentNo][|options]):\n" -"Asks the agent to login to the system. Always returns -1. While\n" -"logged in, the agent can receive calls and will hear a 'beep'\n" -"when a new call comes in. The agent can dump the call by pressing\n" -"the star key.\n" -"The option string may contain zero or more of the following characters:\n" -" 's' -- silent login - do not announce the login ok segment after agent logged in/off\n"; - -static const char descrip2[] = -" AgentCallbackLogin([AgentNo][|[options][|[exten]@context]]):\n" -"Asks the agent to login to the system with callback.\n" -"The agent's callback extension is called (optionally with the specified\n" -"context).\n" -"The option string may contain zero or more of the following characters:\n" -" 's' -- silent login - do not announce the login ok segment agent logged in/off\n"; - -static const char descrip3[] = -" AgentMonitorOutgoing([options]):\n" -"Tries to figure out the id of the agent who is placing outgoing call based on\n" -"comparision of the callerid of the current interface and the global variable \n" -"placed by the AgentCallbackLogin application. That's why it should be used only\n" -"with the AgentCallbackLogin app. Uses the monitoring functions in chan_agent \n" -"instead of Monitor application. That have to be configured in the agents.conf file.\n" -"\nReturn value:\n" -"Normally the app returns 0 unless the options are passed. Also if the callerid or\n" -"the agentid are not specified it'll look for n+101 priority.\n" -"\nOptions:\n" -" 'd' - make the app return -1 if there is an error condition and there is\n" -" no extension n+101\n" -" 'c' - change the CDR so that the source of the call is 'Agent/agent_id'\n" -" 'n' - don't generate the warnings when there is no callerid or the\n" -" agentid is not known.\n" -" It's handy if you want to have one context for agent and non-agent calls.\n"; +/* Indexes for the app-related strings. */ +#define NAME 0 +#define SYNOPSIS 1 +#define DESCRIPTION 2 + +/** + * Metainformation related to the AgentLogin application. + */ +static const char *agent_login_meta[]= { + "AgentLogin", + "Call agent login", + " AgentLogin([AgentNo][|options]):\n" + "Asks the agent to login to the system. Always returns -1. While\n" + "logged in, the agent can receive calls and will hear a 'beep'\n" + "when a new call comes in. The agent can dump the call by pressing\n" + "the star key.\n" + "The option string may contain zero or more of the following characters:\n" + " 's' -- silent login - do not announce the login ok segment after agent logged in/off\n"}; + +/** + * Metainformation related to the AgentCallbackLogin application. + */ +static const char *agent_callback_login_meta[]={ + "AgentCallbackLogin", + "Call agent callback login", + " AgentCallbackLogin([AgentNo][|[options][|[exten]@context]]):\n" + "Asks the agent to login to the system with callback.\n" + "The agent's callback extension is called (optionally with the specified\n" + "context).\n" + "The option string may contain zero or more of the following characters:\n" + " 's' -- silent login - do not announce the login ok segment agent logged in/off\n"}; + +/** + * Metainformation related to the AgentMonitorOutgoing application. + */ +static const char *agent_monitor_outgoing_meta[] = { + "AgentMonitorOutgoing", + "Record agent's outgoing call", + " AgentMonitorOutgoing([options]):\n" + "Tries to figure out the id of the agent who is placing outgoing call based on\n" + "comparision of the callerid of the current interface and the global variable \n" + "placed by the AgentCallbackLogin application. That's why it should be used only\n" + "with the AgentCallbackLogin app. Uses the monitoring functions in chan_agent \n" + "instead of Monitor application. That have to be configured in the agents.conf file.\n" + "\nReturn value:\n" + "Normally the app returns 0 unless the options are passed. Also if the callerid or\n" + "the agentid are not specified it'll look for n+101 priority.\n" + "\nOptions:\n" + " 'd' - make the app return -1 if there is an error condition and there is\n" + " no extension n+101\n" + " 'c' - change the CDR so that the source of the call is 'Agent/agent_id'\n" + " 'n' - don't generate the warnings when there is no callerid or the\n" + " agentid is not known.\n" + " It's handy if you want to have one context for agent and non-agent calls.\n"}; static const char mandescr_agents[] = "Description: Will list info about all possible agents.\n" @@ -203,6 +215,7 @@ char logincallerid[80]; /**< Caller ID they had when they logged in */ struct ast_channel *chan; /**< Channel we use */ struct agent_pvt *next; /**< Next Agent in the linked list. */ + int realtime; /**< Is this a realtime agent ? */ }; static struct agent_pvt *agents = NULL; /**< Holds the list of agents (loaded form agents.conf). */ @@ -373,6 +386,7 @@ ast_copy_string(p->moh, moh, sizeof(p->moh)); p->ackcall = ackcall; p->autologoff = autologoff; + p->realtime = 0; /* By default, we are not realtime. */ /* If someone reduces the wrapuptime and reloads, we want it * to change the wrapuptime immediately on all calls */ @@ -405,17 +419,18 @@ { struct ast_channel *chan = p->owner; p->owner = NULL; - chan->tech_pvt = NULL; p->app_sleep_cond = 1; /* Release ownership of the agent to other threads (presumably running the login app). */ ast_mutex_unlock(&p->app_lock); - if (chan) + if (chan){ + chan->tech_pvt = NULL; ast_channel_free(chan); + } if (p->dead) { ast_mutex_destroy(&p->lock); ast_mutex_destroy(&p->app_lock); free(p); - } + } return 0; } @@ -1143,6 +1158,9 @@ return 0; } +/** + * What does this do ? + */ static int check_availability(struct agent_pvt *newlyavailable, int needlock) { struct ast_channel *chan=NULL, *parent=NULL; @@ -1659,6 +1677,84 @@ STANDARD_LOCAL_USER; LOCAL_USER_DECL; +/** + * Find an agent in the list based on the username. + * + * @param username Username of the agent to find. + * @returns A pointer to the structure of the found agent or null. + */ +static struct agent_pvt *find_agent(const char *username){ + struct agent_pvt *agent = agents; /* Get a pointer to the first agent. */ + + /* Iterate thru agents to find the one. */ + ast_mutex_lock(&agentlock); + while(agent) { + /* Check if the agent is realtime and if this realtime agent still exists on the database.*/ + if(agent->realtime && !ast_load_realtime("agents", "user", agent->agent, NULL)){ + ast_mutex_lock(&agent->lock); + /* The username changed or the agent doesn't exist anymore. */ + struct agent_pvt *next_agent = agent->next; /* Save a pointer to the next agent. */ + agent->dead = 1; /* Mark this agent for destruction. */ + agent_unlink(agent); /* Take it out of the list. */ + agent_cleanup(agent); /* Destruct it. */ + agent = next_agent; /* Make the current, the next one. */ + continue; + } + + /* Compare the usernames and ensure it is not pending. FIXME: shouldn't we also avoid 'dead' agents ? */ + if (!strcmp(agent->agent, username) && !agent->pending){ + /* Is this agent realtime ? */ + if(agent->realtime){ + ast_mutex_lock(&agent->lock); + /* If so, fetch its data from the database to update it.*/ + struct ast_variable *var = ast_load_realtime("agents", "user", username, NULL); + /* Update agent pass and name. */ + while(var){ + if(!strcasecmp(var->name, "pass")) { + ast_copy_string(agent->password, var->value, sizeof(agent->password)); + } else if(!strcasecmp(var->name, "name")) { + ast_copy_string(agent->name, var->value, sizeof(agent->name)); + } + var = var->next; + } + /* TODO: handle pendiness when it is implemented. */ + ast_mutex_unlock(&agent->lock); + } + break; + } + agent = agent->next; /* Get the next agent. */ + } + + if(!agent){ + // If no agent was found on the agents.conf, lets try realtime. + struct ast_variable *var = ast_load_realtime("agents", "user", username, NULL); + if(var){ + char user[AST_MAX_AGENT], pass[AST_MAX_AGENT], name[AST_MAX_AGENT]; + char user_pass_name[AST_MAX_AGENT * 3 + 2]; /* Enough space for the previous three strings and two commas separating them. */ + while(var){ + if(!strcasecmp(var->name, "user")) { + ast_copy_string(user, var->value, sizeof(user)); + } else if(!strcasecmp(var->name, "pass")) { + ast_copy_string(pass, var->value, sizeof(pass)); + } else if(!strcasecmp(var->name, "name")) { + ast_copy_string(name, var->value, sizeof(name)); + } + var = var->next; + } + /* Build a string similar to the one that is gotten from the configuration file. + This is needed because build_agent() expects it, but this shouldn't be consider more than a hack, build_agent()'s API should be fixed. */ + snprintf(user_pass_name, AST_MAX_AGENT * 3 + 2, "%s,%s,%s", user, pass, name); + // TODO: grab pendiness from the table. + agent = add_agent(user_pass_name, 0); + agent->realtime = 1; /* It is a realtime agent */ + } + } + + ast_mutex_unlock(&agentlock); + + return agent; /* Return the agent, if no agent was found, this should be NULL, shouldn't it ? */ +} + /*! * \brief Log in agent application. * @@ -1753,14 +1849,19 @@ while (!res && (max_login_tries==0 || tries < max_login_tries)) { tries++; /* Check for password */ - ast_mutex_lock(&agentlock); + p = find_agent(user); + if(p){ + ast_copy_string(xpass, p->password, sizeof(xpass)); + } +/* FIXME: The following code should go once the previous one is known to work. */ +/* ast_mutex_lock(&agentlock); p = agents; while(p) { if (!strcmp(p->agent, user) && !p->pending) ast_copy_string(xpass, p->password, sizeof(xpass)); p = p->next; } - ast_mutex_unlock(&agentlock); + ast_mutex_unlock(&agentlock); */ if (!res) { if (!ast_strlen_zero(xpass)) res = ast_app_getdata(chan, "agent-pass", pass, sizeof(pass) - 1, 0); @@ -1773,10 +1874,11 @@ ast_log(LOG_NOTICE, "user: %s, pass: %s\n", user, pass); #endif - /* Check again for accuracy */ + /* The following code used to me a while that got thru all the agents once more, checking user and password while we had already checked for user, it didn't make much sense. To see chat code check Asterisk from CVS HEAD as of 2005-09-05. + I (Pupeno) did a simple change to make the while into an if, but the whole code is still clutered with statment that only made sense in the previous scheme. + TODO: Clean this code up. */ ast_mutex_lock(&agentlock); - p = agents; - while(p) { + if(p) { ast_mutex_lock(&p->lock); if (!strcmp(p->agent, user) && !strcmp(p->password, pass) && !p->pending) { @@ -2052,10 +2154,10 @@ break; } ast_mutex_unlock(&p->lock); - p = p->next; - } - if (!p) + } else { +/* if (!p) */ ast_mutex_unlock(&agentlock); + } if (!res && (max_login_tries==0 || tries < max_login_tries)) res = ast_app_getdata(chan, errmsg, user, sizeof(user) - 1, 0); @@ -2128,7 +2230,7 @@ * @returns * @sa login_exec(), agentmonitoroutgoing_exec(), load_module(). */ -static int callback_exec(struct ast_channel *chan, void *data) +static int callback_login_exec(struct ast_channel *chan, void *data) { return __login_exec(chan, data, 1); } @@ -2424,17 +2526,42 @@ ast_log(LOG_ERROR, "Unable to register channel class %s\n", channeltype); return -1; } + /* Dialplan applications */ - ast_register_application(app, login_exec, synopsis, descrip); - ast_register_application(app2, callback_exec, synopsis2, descrip2); - ast_register_application(app3, agentmonitoroutgoing_exec, synopsis3, descrip3); - /* Manager commands */ - 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("AgentCallbackLogin", EVENT_FLAG_AGENT, action_agent_callback_login, "Sets an agent as logged in by callback", mandescr_agent_callback_login); + ast_register_application(agent_login_meta[NAME], + login_exec, + agent_login_meta[SYNOPSIS], + agent_login_meta[DESCRIPTION]); + ast_register_application(agent_callback_login_meta[NAME], + callback_login_exec, + agent_callback_login_meta[SYNOPSIS], + agent_callback_login_meta[DESCRIPTION]); + ast_register_application(agent_monitor_outgoing_meta[NAME], + agentmonitoroutgoing_exec, + agent_monitor_outgoing_meta[SYNOPSIS], + agent_monitor_outgoing_meta[DESCRIPTION]); + + /* Manager commands */ + 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("AgentCallbackLogin", + EVENT_FLAG_AGENT, + action_agent_callback_login, + "Sets an agent as logged in by callback", + mandescr_agent_callback_login); + /* CLI Application */ ast_cli_register(&cli_show_agents); ast_cli_register(&cli_agent_logoff); + /* Read in the config */ read_agent_config(); if (persistent_agents) @@ -2457,14 +2584,17 @@ /* Unregister CLI application */ ast_cli_unregister(&cli_show_agents); ast_cli_unregister(&cli_agent_logoff); + /* Unregister dialplan applications */ - ast_unregister_application(app); - ast_unregister_application(app2); - ast_unregister_application(app3); + ast_unregister_application(agent_login_meta[NAME]); + ast_unregister_application(agent_callback_login_meta[NAME]); + ast_unregister_application(agent_monitor_outgoing_meta[NAME]); + /* Unregister manager command */ ast_manager_unregister("Agents"); ast_manager_unregister("AgentLogoff"); ast_manager_unregister("AgentCallbackLogin"); + /* Unregister channel */ ast_channel_unregister(&agent_tech); if (!ast_mutex_lock(&agentlock)) {