Newer
Older
{ EVENT_FLAG_REPORTING, "reporting" },
Tilghman Lesher
committed
{ EVENT_FLAG_CDR, "cdr" },
Tilghman Lesher
committed
{ EVENT_FLAG_ORIGINATE, "originate" },
{ EVENT_FLAG_AGI, "agi" },
{ EVENT_FLAG_AOC, "aoc" },
{ EVENT_FLAG_TEST, "test" },
{ EVENT_FLAG_SECURITY, "security" },
{ EVENT_FLAG_MESSAGE, "message" },
{ 0, "none" },
};
/*! Maximum string length of the AMI authority permission string buildable from perms[]. */
#define MAX_AUTH_PERM_STRING 150
Jonathan Rose
committed
/*! \brief Checks to see if a string which can be used to evaluate functions should be rejected */
static int function_capable_string_allowed_with_auths(const char *evaluating, int writepermlist)
{
if (!(writepermlist & EVENT_FLAG_SYSTEM)
&& (
strstr(evaluating, "SHELL") || /* NoOp(${SHELL(rm -rf /)}) */
strstr(evaluating, "EVAL") /* NoOp(${EVAL(${some_var_containing_SHELL})}) */
)) {
return 0;
}
return 1;
}
Matthew Jordan
committed
/*! \brief Convert authority code to a list of options for a user. This will only
* display those authority codes that have an explicit match on authority */
static const char *user_authority_to_str(int authority, struct ast_str **res)
{
int i;
char *sep = "";
ast_str_reset(*res);
for (i = 0; i < ARRAY_LEN(perms) - 1; i++) {
if ((authority & perms[i].num) == perms[i].num) {
ast_str_append(res, 0, "%s%s", sep, perms[i].label);
sep = ",";
}
}
if (ast_str_strlen(*res) == 0) {
/* replace empty string with something sensible */
Matthew Jordan
committed
ast_str_append(res, 0, "<none>");
Matthew Jordan
committed
return ast_str_buffer(*res);
}
/*! \brief Convert authority code to a list of options. Note that the EVENT_FLAG_ALL
* authority will always be returned. */
static const char *authority_to_str(int authority, struct ast_str **res)
Malcolm Davenport
committed
{
ast_str_reset(*res);
if (authority != EVENT_FLAG_SHUTDOWN) {
for (i = 0; i < ARRAY_LEN(perms) - 1; i++) {
if (authority & perms[i].num) {
ast_str_append(res, 0, "%s%s", sep, perms[i].label);
sep = ",";
}
Malcolm Davenport
committed
}
}
if (ast_str_strlen(*res) == 0) {
/* replace empty string with something sensible */
ast_str_append(res, 0, "<none>");
return ast_str_buffer(*res);
Malcolm Davenport
committed
}
/*! Tells you if smallstr exists inside bigstr
which is delim by delim and uses no buf or stringsep
ast_instring("this|that|more","this",'|') == 1;
feel free to move this to app.c -anthm */
static int ast_instring(const char *bigstr, const char *smallstr, const char delim)
const char *val = bigstr, *next;
do {
if ((next = strchr(val, delim))) {
if (!strncmp(val, smallstr, (next - val))) {
} else {
}
} else {
return !strcmp(smallstr, val);
} while (*(val = (next + 1)));
return 0;
}
static int get_perm(const char *instr)
Mark Spencer
committed
{
int x = 0, ret = 0;
if (!instr) {
for (x = 0; x < ARRAY_LEN(perms); x++) {
if (ast_instring(instr, perms[x].label, ',')) {
ret |= perms[x].num;
Mark Spencer
committed
}
Mark Spencer
committed
}
* A number returns itself, false returns 0, true returns all flags,
* other strings return the flags that are set.
static int strings_to_mask(const char *string)
Mark Spencer
committed
{
const char *p;
if (ast_strlen_zero(string)) {
for (p = string; *p; p++) {
if (*p < '0' || *p > '9') {
}
}
if (!*p) { /* all digits */
return atoi(string);
}
if (ast_false(string)) {
if (ast_true(string)) { /* all permissions */
int x, ret = 0;
for (x = 0; x < ARRAY_LEN(perms); x++) {
Mark Spencer
committed
}
return get_perm(string);
}
/*! \brief Unreference manager session object.
If no more references, then go ahead and delete it */
static struct mansession_session *unref_mansession(struct mansession_session *s)
{
int refcount = ao2_ref(s, -1);
ast_debug(1, "Mansession: %p refcount now %d\n", s, refcount - 1);
static void event_filter_destructor(void *obj)
{
regex_t *regex_filter = obj;
regfree(regex_filter);
}
static void session_destructor(void *obj)
{
struct mansession_session *session = obj;
struct eventqent *eqe = session->last_ev;
Eliel C. Sardanons
committed
struct ast_datastore *datastore;
/* Get rid of each of the data stores on the session */
while ((datastore = AST_LIST_REMOVE_HEAD(&session->datastores, entry))) {
/* Free the data store */
ast_datastore_free(datastore);
}
if (session->f != NULL) {
fflush(session->f);
fclose(session->f);
}
if (eqe) {
ast_atomic_fetchadd_int(&eqe->usecount, -1);
}
if (session->chanvars) {
ast_variables_destroy(session->chanvars);
}
ao2_t_ref(session->whitefilters, -1, "decrement ref for white container, should be last one");
}
if (session->blackfilters) {
ao2_t_ref(session->blackfilters, -1, "decrement ref for black container, should be last one");
}
/*! \brief Allocate manager session structure and add it to the list of sessions */
static struct mansession_session *build_mansession(const struct ast_sockaddr *addr)
struct ao2_container *sessions;
struct mansession_session *newsession;
newsession = ao2_alloc(sizeof(*newsession), session_destructor);
if (!newsession) {
return NULL;
}
newsession->whitefilters = ao2_container_alloc(1, NULL, NULL);
newsession->blackfilters = ao2_container_alloc(1, NULL, NULL);
if (!newsession->whitefilters || !newsession->blackfilters) {
ao2_ref(newsession, -1);
return NULL;
}
newsession->fd = -1;
newsession->waiting_thread = AST_PTHREADT_NULL;
newsession->writetimeout = 100;
newsession->send_events = -1;
ast_sockaddr_copy(&newsession->addr, addr);
sessions = ao2_global_obj_ref(mgr_sessions);
if (sessions) {
ao2_link(sessions, newsession);
ao2_ref(sessions, -1);
}
Eliel C. Sardanons
committed
return newsession;
}
static int mansession_cmp_fn(void *obj, void *arg, int flags)
{
struct mansession_session *s = obj;
char *str = arg;
return !strcasecmp(s->username, str) ? CMP_MATCH : 0;
}
static void session_destroy(struct mansession_session *s)
{
struct ao2_container *sessions;
sessions = ao2_global_obj_ref(mgr_sessions);
if (sessions) {
ao2_unlink(sessions, s);
ao2_ref(sessions, -1);
}
Eliel C. Sardanons
committed
unref_mansession(s);
}
static int check_manager_session_inuse(const char *name)
{
struct ao2_container *sessions;
struct mansession_session *session;
Eliel C. Sardanons
committed
int inuse = 0;
sessions = ao2_global_obj_ref(mgr_sessions);
if (sessions) {
session = ao2_find(sessions, (char *) name, 0);
ao2_ref(sessions, -1);
if (session) {
unref_mansession(session);
inuse = 1;
}
Eliel C. Sardanons
committed
return inuse;
}
* lookup an entry in the list of registered users.
* must be called with the list lock held.
*/
static struct ast_manager_user *get_manager_by_name_locked(const char *name)
{
struct ast_manager_user *user = NULL;
AST_RWLIST_TRAVERSE(&users, user, list) {
if (!strcasecmp(user->username, name)) {
/*! \brief Get displayconnects config option.
* \param session manager session to get parameter from.
* \return displayconnects config option value.
*/
static int manager_displayconnects(struct mansession_session *session)
{
struct ast_manager_user *user = NULL;
int ret = 0;
if ((user = get_manager_by_name_locked(session->username))) {
ret = user->displayconnects;
return ret;
}
static void print_event_instance(struct ast_cli_args *a, struct ast_xml_doc_item *instance);
static char *handle_showmancmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
struct manager_action *cur;
const char *auth_str;
#ifdef AST_XML_DOCS
char syntax_title[64], description_title[64], synopsis_title[64], seealso_title[64];
char arguments_title[64], privilege_title[64], final_response_title[64], list_responses_title[64];
#endif
switch (cmd) {
case CLI_INIT:
e->command = "manager show command";
e->usage =
Eliel C. Sardanons
committed
"Usage: manager show command <actionname> [<actionname> [<actionname> [...]]]\n"
" Shows the detailed description for a specific Asterisk manager interface command.\n";
return NULL;
case CLI_GENERATE:
l = strlen(a->word);
which = 0;
AST_RWLIST_RDLOCK(&actions);
AST_RWLIST_TRAVERSE(&actions, cur, list) {
if (!strncasecmp(a->word, cur->action, l) && ++which > a->n) {
ret = ast_strdup(cur->action);
break; /* make sure we exit even if ast_strdup() returns NULL */
AST_RWLIST_UNLOCK(&actions);
return ret;
authority = ast_str_alloca(MAX_AUTH_PERM_STRING);
Eliel C. Sardanons
committed
if (a->argc < 4) {
#ifdef AST_XML_DOCS
/* setup the titles */
term_color(synopsis_title, "[Synopsis]\n", COLOR_MAGENTA, 0, 40);
term_color(description_title, "[Description]\n", COLOR_MAGENTA, 0, 40);
term_color(syntax_title, "[Syntax]\n", COLOR_MAGENTA, 0, 40);
term_color(seealso_title, "[See Also]\n", COLOR_MAGENTA, 0, 40);
term_color(arguments_title, "[Arguments]\n", COLOR_MAGENTA, 0, 40);
term_color(privilege_title, "[Privilege]\n", COLOR_MAGENTA, 0, 40);
term_color(final_response_title, "[Final Response]\n", COLOR_MAGENTA, 0, 40);
term_color(list_responses_title, "[List Responses]\n", COLOR_MAGENTA, 0, 40);
#endif
AST_RWLIST_RDLOCK(&actions);
AST_RWLIST_TRAVERSE(&actions, cur, list) {
for (num = 3; num < a->argc; num++) {
if (!strcasecmp(cur->action, a->argv[num])) {
auth_str = authority_to_str(cur->authority, &authority);
Jonathan Rose
committed
#ifdef AST_XML_DOCS
if (cur->docsrc == AST_XML_DOC) {
char *syntax = ast_xmldoc_printable(S_OR(cur->syntax, "Not available"), 1);
char *synopsis = ast_xmldoc_printable(S_OR(cur->synopsis, "Not available"), 1);
char *description = ast_xmldoc_printable(S_OR(cur->description, "Not available"), 1);
char *arguments = ast_xmldoc_printable(S_OR(cur->arguments, "Not available"), 1);
char *seealso = ast_xmldoc_printable(S_OR(cur->seealso, "Not available"), 1);
char *privilege = ast_xmldoc_printable(S_OR(auth_str, "Not available"), 1);
char *responses = ast_xmldoc_printable("None", 1);
ast_cli(a->fd, "%s%s\n\n%s%s\n\n%s%s\n\n%s%s\n\n%s%s\n\n%s%s\n\n%s",
syntax_title, syntax,
synopsis_title, synopsis,
description_title, description,
arguments_title, arguments,
seealso_title, seealso,
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
privilege_title, privilege,
list_responses_title);
if (!cur->list_responses) {
ast_cli(a->fd, "%s\n\n", responses);
} else {
struct ast_xml_doc_item *temp;
for (temp = cur->list_responses; temp; temp = AST_LIST_NEXT(temp, next)) {
ast_cli(a->fd, "Event: %s\n", temp->name);
print_event_instance(a, temp);
}
}
ast_cli(a->fd, "%s", final_response_title);
if (!cur->final_response) {
ast_cli(a->fd, "%s\n\n", responses);
} else {
ast_cli(a->fd, "Event: %s\n", cur->final_response->name);
print_event_instance(a, cur->final_response);
}
#endif
ast_cli(a->fd, "Action: %s\nSynopsis: %s\nPrivilege: %s\n%s\n",
cur->action, cur->synopsis,
auth_str,
S_OR(cur->description, ""));
}
}
}
}
AST_RWLIST_UNLOCK(&actions);
}
static char *handle_mandebug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
e->command = "manager set debug [on|off]";
e->usage = "Usage: manager set debug [on|off]\n Show, enable, disable debugging of the manager code.\n";
return NULL;
if (a->argc == 3) {
ast_cli(a->fd, "manager debug is %s\n", manager_debug? "on" : "off");
} else if (a->argc == 4) {
if (!strcasecmp(a->argv[3], "on")) {
manager_debug = 1;
} else if (!strcasecmp(a->argv[3], "off")) {
manager_debug = 0;
} else {
static char *handle_showmanager(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
struct ast_manager_user *user = NULL;
struct ast_str *rauthority = ast_str_alloca(MAX_AUTH_PERM_STRING);
struct ast_str *wauthority = ast_str_alloca(MAX_AUTH_PERM_STRING);
Olle Johansson
committed
switch (cmd) {
case CLI_INIT:
e->command = "manager show user";
e->usage =
" Usage: manager show user <user>\n"
" Display all information related to the manager user specified.\n";
return NULL;
case CLI_GENERATE:
l = strlen(a->word);
which = 0;
if (a->pos != 3) {
AST_RWLIST_RDLOCK(&users);
AST_RWLIST_TRAVERSE(&users, user, list) {
if ( !strncasecmp(a->word, user->username, l) && ++which > a->n ) {
ret = ast_strdup(user->username);
break;
}
}
AST_RWLIST_UNLOCK(&users);
return ret;
}
if (a->argc != 4) {
if (!(user = get_manager_by_name_locked(a->argv[3]))) {
ast_cli(a->fd, "There is no manager called %s\n", a->argv[3]);
Tilghman Lesher
committed
ast_cli(a->fd, "\n");
" username: %s\n"
" secret: %s\n"
" ACL: %s\n"
" read perm: %s\n"
" write perm: %s\n"
" displayconnects: %s\n"
"allowmultiplelogin: %s\n",
S_OR(user->username, "(N/A)"),
(user->secret ? "<Set>" : "(N/A)"),
((user->acl && !ast_acl_list_is_empty(user->acl)) ? "yes" : "no"),
Matthew Jordan
committed
user_authority_to_str(user->readperm, &rauthority),
user_authority_to_str(user->writeperm, &wauthority),
(user->displayconnects ? "yes" : "no"),
(user->allowmultiplelogin ? "yes" : "no"));
ast_cli(a->fd, " Variables: \n");
for (v = user->chanvars ; v ; v = v->next) {
ast_cli(a->fd, " %s = %s\n", v->name, v->value);
}
static char *handle_showmanagers(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
struct ast_manager_user *user = NULL;
int count_amu = 0;
switch (cmd) {
case CLI_INIT:
e->command = "manager show users";
e->usage =
"Usage: manager show users\n"
" Prints a listing of all managers that are currently configured on that\n"
" system.\n";
return NULL;
case CLI_GENERATE:
return NULL;
}
if (a->argc != 3) {
/* If there are no users, print out something along those lines */
if (AST_RWLIST_EMPTY(&users)) {
AST_RWLIST_TRAVERSE(&users, user, list) {
ast_cli(a->fd,"-------------------\n"
"%d manager users configured.\n", count_amu);
/*! \brief CLI command manager list commands */
static char *handle_showmancmds(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
struct manager_action *cur;
Jonathan Rose
committed
int name_len = 1;
int space_remaining;
#define HSMC_FORMAT " %-*.*s %-.*s\n"
switch (cmd) {
case CLI_INIT:
e->command = "manager show commands";
e->usage =
"Usage: manager show commands\n"
" Prints a listing of all the available Asterisk manager interface commands.\n";
return NULL;
case CLI_GENERATE:
return NULL;
}
AST_RWLIST_RDLOCK(&actions);
AST_RWLIST_TRAVERSE(&actions, cur, list) {
Jonathan Rose
committed
int incoming_len = strlen(cur->action);
if (incoming_len > name_len) {
name_len = incoming_len;
}
}
space_remaining = MGR_SHOW_TERMINAL_WIDTH - name_len - 4;
if (space_remaining < 0) {
space_remaining = 0;
}
ast_cli(a->fd, HSMC_FORMAT, name_len, name_len, "Action", space_remaining, "Synopsis");
ast_cli(a->fd, HSMC_FORMAT, name_len, name_len, "------", space_remaining, "--------");
AST_RWLIST_TRAVERSE(&actions, cur, list) {
ast_cli(a->fd, HSMC_FORMAT, name_len, name_len, cur->action, space_remaining, cur->synopsis);
AST_RWLIST_UNLOCK(&actions);
/*! \brief CLI command manager list connected */
static char *handle_showmanconn(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
struct ao2_container *sessions;
struct mansession_session *session;
time_t now = time(NULL);
#define HSMCONN_FORMAT1 " %-15.15s %-55.55s %-10.10s %-10.10s %-8.8s %-8.8s %-5.5s %-5.5s\n"
#define HSMCONN_FORMAT2 " %-15.15s %-55.55s %-10d %-10d %-8d %-8d %-5.5d %-5.5d\n"
struct ao2_iterator i;
switch (cmd) {
case CLI_INIT:
e->command = "manager show connected";
e->usage =
"Usage: manager show connected\n"
" Prints a listing of the users that are currently connected to the\n"
"Asterisk manager interface.\n";
return NULL;
case CLI_GENERATE:
return NULL;
ast_cli(a->fd, HSMCONN_FORMAT1, "Username", "IP Address", "Start", "Elapsed", "FileDes", "HttpCnt", "Read", "Write");
sessions = ao2_global_obj_ref(mgr_sessions);
if (sessions) {
i = ao2_iterator_init(sessions, 0);
ao2_ref(sessions, -1);
while ((session = ao2_iterator_next(&i))) {
ao2_lock(session);
ast_cli(a->fd, HSMCONN_FORMAT2, session->username,
ast_sockaddr_stringify_addr(&session->addr),
(int) (session->sessionstart),
(int) (now - session->sessionstart),
session->fd,
session->inuse,
session->readperm,
session->writeperm);
count++;
ao2_unlock(session);
unref_mansession(session);
}
ao2_iterator_destroy(&i);
/*! \brief CLI command manager list eventq */
/* Should change to "manager show connected" */
static char *handle_showmaneventq(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
switch (cmd) {
case CLI_INIT:
e->command = "manager show eventq";
e->usage =
"Usage: manager show eventq\n"
" Prints a listing of all events pending in the Asterisk manger\n"
"event queue.\n";
return NULL;
case CLI_GENERATE:
return NULL;
}
Tilghman Lesher
committed
AST_RWLIST_RDLOCK(&all_events);
AST_RWLIST_TRAVERSE(&all_events, s, eq_next) {
Tilghman Lesher
committed
ast_cli(a->fd, "Usecount: %d\n", s->usecount);
ast_cli(a->fd, "Category: %d\n", s->category);
ast_cli(a->fd, "Event:\n%s", s->eventdata);
Tilghman Lesher
committed
AST_RWLIST_UNLOCK(&all_events);
/*! \brief CLI command manager reload */
static char *handle_manager_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
switch (cmd) {
case CLI_INIT:
e->command = "manager reload";
e->usage =
"Usage: manager reload\n"
" Reloads the manager configuration.\n";
return NULL;
case CLI_GENERATE:
return NULL;
}
if (a->argc > 2) {
return CLI_SHOWUSAGE;
reload_manager();
return CLI_SUCCESS;
}
Tilghman Lesher
committed
static struct eventqent *advance_event(struct eventqent *e)
Tilghman Lesher
committed
struct eventqent *next;
Tilghman Lesher
committed
AST_RWLIST_RDLOCK(&all_events);
if ((next = AST_RWLIST_NEXT(e, eq_next))) {
ast_atomic_fetchadd_int(&next->usecount, 1);
ast_atomic_fetchadd_int(&e->usecount, -1);
}
AST_RWLIST_UNLOCK(&all_events);
return next;
#define GET_HEADER_FIRST_MATCH 0
#define GET_HEADER_LAST_MATCH 1
#define GET_HEADER_SKIP_EMPTY 2
/*!
* \brief Return a matching header value.
*
* \details
* Generic function to return either the first or the last
* matching header from a list of variables, possibly skipping
* empty strings.
*
* \note At the moment there is only one use of this function in
* this file, so we make it static.
*
* \note Never returns NULL.
*/
static const char *__astman_get_header(const struct message *m, char *var, int mode)
int x, l = strlen(var);
const char *result = "";
if (!m) {
return result;
}
for (x = 0; x < m->hdrcount; x++) {
const char *h = m->headers[x];
if (!strncasecmp(var, h, l) && h[l] == ':') {
const char *value = h + l + 1;
value = ast_skip_blanks(value); /* ignore leading spaces in the value */
/* found a potential candidate */
if ((mode & GET_HEADER_SKIP_EMPTY) && ast_strlen_zero(value)) {
continue; /* not interesting */
}
if (mode & GET_HEADER_LAST_MATCH) {
result = value; /* record the last match so far */
return value;
}
/*!
* \brief Return the first matching variable from an array.
*
* \note This is the legacy function and is implemented in
* therms of __astman_get_header().
*
* \note Never returns NULL.
*/
const char *astman_get_header(const struct message *m, char *var)
{
return __astman_get_header(m, var, GET_HEADER_FIRST_MATCH);
}
/*!
* \internal
* \brief Process one "Variable:" header value string.
*
* \param head Current list of AMI variables to get new values added.
* \param hdr_val Header value string to process.
*
* \return New variable list head.
*/
static struct ast_variable *man_do_variable_value(struct ast_variable *head, const char *hdr_val)
{
AST_APP_ARG(vars)[64];
hdr_val = ast_skip_blanks(hdr_val); /* ignore leading spaces in the value */
parse = ast_strdupa(hdr_val);
/* Break the header value string into name=val pair items. */
AST_STANDARD_APP_ARGS(args, parse);
if (args.argc) {
int y;
/* Process each name=val pair item. */
struct ast_variable *cur;
char *var;
char *val;
if (!args.vars[y]) {
var = val = args.vars[y];
/* XXX We may wish to trim whitespace from the strings. */
if (!val || ast_strlen_zero(var)) {
/* Create new variable list node and prepend it to the list. */
Steve Murphy
committed
cur = ast_variable_new(var, val, "");
if (cur) {
cur->next = head;
head = cur;
}
}
}
return head;
}
struct ast_variable *astman_get_variables(const struct message *m)
{
return astman_get_variables_order(m, ORDER_REVERSE);
}
struct ast_variable *astman_get_variables_order(const struct message *m,
enum variable_orders order)
{
int varlen;
int x;
struct ast_variable *head = NULL;
static const char var_hdr[] = "Variable:";
/* Process all "Variable:" headers. */
varlen = strlen(var_hdr);
for (x = 0; x < m->hdrcount; x++) {
if (strncasecmp(var_hdr, m->headers[x], varlen)) {
continue;
}
head = man_do_variable_value(head, m->headers[x] + varlen);
}
if (order == ORDER_NATURAL) {
head = ast_variables_reverse(head);
}
return head;
}
/*! \brief access for hooks to send action messages to ami */
int ast_hook_send_action(struct manager_custom_hook *hook, const char *msg)
{
const char *action;
int ret = 0;
struct manager_action *act_found;
struct mansession s = {.session = NULL, };
struct message m = { 0 };
char *dup_str;
char *src;
int x = 0;
int curlen;
if (hook == NULL) {
return -1;
}
/* Create our own copy of the AMI action msg string. */
src = dup_str = ast_strdup(msg);
if (!dup_str) {
return -1;
}
for (x = 0; x < curlen; x++) {
int cr; /* set if we have \r */
if (src[x] == '\r' && x+1 < curlen && src[x+1] == '\n')
cr = 2; /* Found. Update length to include \r\n */
else if (src[x] == '\n')
cr = 1; /* also accept \n only */
else
continue;
/* don't keep empty lines */
if (x && m.hdrcount < ARRAY_LEN(m.headers)) {
/* ... but trim \r\n and terminate the header string */
src[x] = '\0';
m.headers[m.hdrcount++] = src;
}
x += cr;
curlen -= x; /* remaining size */
src += x; /* update pointer */
x = -1; /* reset loop */
}
action = astman_get_header(&m, "Action");
if (strcasecmp(action, "login")) {
act_found = action_find(action);
if (act_found) {
* we have to simulate a session for this action request
* to be able to pass it down for processing
* This is necessary to meet the previous design of manager.c
*/
s.hook = hook;
s.f = (void*)1; /* set this to something so our request will make it through all functions that test it*/
ao2_lock(act_found);
if (act_found->registered && act_found->func) {
if (act_found->module) {
ast_module_ref(act_found->module);
}
ao2_unlock(act_found);
ret = act_found->func(&s, &m);
ao2_lock(act_found);
if (act_found->module) {
ast_module_unref(act_found->module);
}
} else {
ret = -1;
}
ao2_unlock(act_found);
ao2_t_ref(act_found, -1, "done with found action object");
/*!
* helper function to send a string to the socket.
* Return -1 on error (e.g. buffer full).
*/
static int send_string(struct mansession *s, char *string)
{
FILE *f = s->f ? s->f : s->session->f;
int fd = s->f ? s->fd : s->session->fd;
/* It's a result from one of the hook's action invocation */
if (s->hook) {
/*
* to send responses, we're using the same function
* as for receiving events. We call the event "HookResponse"
*/
s->hook->helper(EVENT_FLAG_HOOKRESPONSE, "HookResponse", string);
return 0;
if ((res = ast_careful_fwrite(f, fd, string, strlen(string), s->session->writetimeout))) {
/*!
* \brief thread local buffer for astman_append
*
* \note This can not be defined within the astman_append() function
* because it declares a couple of functions that get used to
* initialize the thread local storage key.
*/
AST_THREADSTORAGE(astman_append_buf);
AST_THREADSTORAGE(userevent_buf);
/*! \brief initial allocated size for the astman_append_buf and astman_send_*_va */
#define ASTMAN_APPEND_BUF_INITSIZE 256
/*!
* utility functions for creating AMI replies
*/
void astman_append(struct mansession *s, const char *fmt, ...)
{
if (!(buf = ast_str_thread_get(&astman_append_buf, ASTMAN_APPEND_BUF_INITSIZE))) {
va_start(ap, fmt);
res = ast_str_set_va(&buf, 0, fmt, ap);
if (res == AST_DYNSTR_BUILD_FAILED) {
return;
}
if (s->f != NULL || s->session->f != NULL) {
send_string(s, ast_str_buffer(buf));
} else {
ast_verbose("fd == -1 in astman_append, should not happen\n");
}