Newer
Older
/*! \note NOTE: XXX this comment is unclear and possibly wrong.
Kevin P. Fleming
committed
Callers of astman_send_error(), astman_send_response() or astman_send_ack() must EITHER
hold the session lock _or_ be running in an action callback (in which case s->session->busy will
Kevin P. Fleming
committed
be non-zero). In either of these cases, there is no need to lock-protect the session's
fd, since no other output will be sent (events will be queued), and no input will
be read until either the current action finishes or get_input() obtains the session
lock.
*/
/*! \todo XXX MSG_MOREDATA should go to a header file. */
#define MSG_MOREDATA ((char *)astman_send_response)
/*! \brief send a response with an optional message,
* and terminate it with an empty line.
* m is used only to grab the 'ActionID' field.
*
* Use the explicit constant MSG_MOREDATA to remove the empty line.
* XXX MSG_MOREDATA should go to a header file.
*/
static void astman_send_response_full(struct mansession *s, const struct message *m, char *resp, char *msg, char *listflag)
Tilghman Lesher
committed
const char *id = astman_get_header(m, "ActionID");
Anthony Minessale II
committed
astman_append(s, "Response: %s\r\n", resp);
if (!ast_strlen_zero(id)) {
astman_append(s, "ActionID: %s\r\n", id);
}
if (listflag) {
astman_append(s, "EventList: %s\r\n", listflag); /* Start, complete, cancelled */
}
if (msg == MSG_MOREDATA) {
} else if (msg) {
astman_append(s, "Message: %s\r\n\r\n", msg);
} else {
astman_append(s, "\r\n");
Anthony Minessale II
committed
}
void astman_send_response(struct mansession *s, const struct message *m, char *resp, char *msg)
Olle Johansson
committed
{
astman_send_response_full(s, m, resp, msg, NULL);
}
void astman_send_error(struct mansession *s, const struct message *m, char *error)
Olle Johansson
committed
astman_send_response_full(s, m, "Error", error, NULL);
Anthony Minessale II
committed
void astman_send_error_va(struct mansession *s, const struct message *m, const char *fmt, ...)
{
va_list ap;
struct ast_str *buf;
char *msg;
if (!(buf = ast_str_thread_get(&astman_append_buf, ASTMAN_APPEND_BUF_INITSIZE))) {
return;
}
va_start(ap, fmt);
res = ast_str_set_va(&buf, 0, fmt, ap);
if (res == AST_DYNSTR_BUILD_FAILED) {
return;
}
/* astman_append will use the same underlying buffer, so copy the message out
* before sending the response */
msg = ast_str_buffer(buf);
if (msg) {
msg = ast_strdupa(msg);
}
astman_send_response_full(s, m, "Error", msg, NULL);
void astman_send_ack(struct mansession *s, const struct message *m, char *msg)
Olle Johansson
committed
astman_send_response_full(s, m, "Success", msg, NULL);
Anthony Minessale II
committed
}
static void astman_start_ack(struct mansession *s, const struct message *m)
{
Olle Johansson
committed
astman_send_response_full(s, m, "Success", MSG_MOREDATA, NULL);
}
void astman_send_listack(struct mansession *s, const struct message *m, char *msg, char *listflag)
Olle Johansson
committed
{
astman_send_response_full(s, m, "Success", msg, listflag);
}
void astman_send_list_complete_start(struct mansession *s, const struct message *m, const char *event_name, int count)
{
const char *id = astman_get_header(m, "ActionID");
astman_append(s, "Event: %s\r\n", event_name);
if (!ast_strlen_zero(id)) {
astman_append(s, "ActionID: %s\r\n", id);
}
astman_append(s,
"EventList: Complete\r\n"
"ListItems: %d\r\n",
count);
}
void astman_send_list_complete_end(struct mansession *s)
{
astman_append(s, "\r\n");
Anthony Minessale II
committed
Eliel C. Sardanons
committed
/*! \brief Lock the 'mansession' structure. */
static void mansession_lock(struct mansession *s)
{
ast_mutex_lock(&s->lock);
}
/*! \brief Unlock the 'mansession' structure. */
static void mansession_unlock(struct mansession *s)
{
ast_mutex_unlock(&s->lock);
}
Anthony Minessale II
committed
Rather than braindead on,off this now can also accept a specific int mask value
Anthony Minessale II
committed
or a ',' delim list of mask strings (the same as manager.conf) -anthm
*/
static int set_eventmask(struct mansession *s, const char *eventmask)
{
int maskint = strings_to_mask(eventmask);
Anthony Minessale II
committed
if (maskint >= 0) {
s->session->send_events = maskint;
return maskint;
}
static enum ast_transport mansession_get_transport(const struct mansession *s)
Russell Bryant
committed
{
return s->tcptls_session->parent->tls_cfg ? AST_TRANSPORT_TLS :
AST_TRANSPORT_TCP;
Russell Bryant
committed
}
static void report_invalid_user(const struct mansession *s, const char *username)
{
char session_id[32];
struct ast_security_event_inval_acct_id inval_acct_id = {
.common.event_type = AST_SECURITY_EVENT_INVAL_ACCT_ID,
.common.version = AST_SECURITY_EVENT_INVAL_ACCT_ID_VERSION,
.common.service = "AMI",
.common.account_id = username,
.common.session_tv = &s->session->sessionstart_tv,
.common.local_addr = {
.addr = &s->tcptls_session->parent->local_address,
Russell Bryant
committed
.transport = mansession_get_transport(s),
},
.common.remote_addr = {
Russell Bryant
committed
.transport = mansession_get_transport(s),
},
.common.session_id = session_id,
};
snprintf(session_id, sizeof(session_id), "%p", s);
ast_security_event_report(AST_SEC_EVT(&inval_acct_id));
}
static void report_failed_acl(const struct mansession *s, const char *username)
{
char session_id[32];
struct ast_security_event_failed_acl failed_acl_event = {
.common.event_type = AST_SECURITY_EVENT_FAILED_ACL,
.common.version = AST_SECURITY_EVENT_FAILED_ACL_VERSION,
.common.service = "AMI",
.common.account_id = username,
.common.session_tv = &s->session->sessionstart_tv,
.common.local_addr = {
.addr = &s->tcptls_session->parent->local_address,
Russell Bryant
committed
.transport = mansession_get_transport(s),
},
.common.remote_addr = {
Russell Bryant
committed
.transport = mansession_get_transport(s),
},
.common.session_id = session_id,
};
snprintf(session_id, sizeof(session_id), "%p", s->session);
ast_security_event_report(AST_SEC_EVT(&failed_acl_event));
}
static void report_inval_password(const struct mansession *s, const char *username)
{
char session_id[32];
struct ast_security_event_inval_password inval_password = {
.common.event_type = AST_SECURITY_EVENT_INVAL_PASSWORD,
.common.version = AST_SECURITY_EVENT_INVAL_PASSWORD_VERSION,
.common.service = "AMI",
.common.account_id = username,
.common.session_tv = &s->session->sessionstart_tv,
.common.local_addr = {
.addr = &s->tcptls_session->parent->local_address,
Russell Bryant
committed
.transport = mansession_get_transport(s),
},
.common.remote_addr = {
Russell Bryant
committed
.transport = mansession_get_transport(s),
},
.common.session_id = session_id,
};
snprintf(session_id, sizeof(session_id), "%p", s->session);
ast_security_event_report(AST_SEC_EVT(&inval_password));
}
static void report_auth_success(const struct mansession *s)
{
char session_id[32];
struct ast_security_event_successful_auth successful_auth = {
.common.event_type = AST_SECURITY_EVENT_SUCCESSFUL_AUTH,
.common.version = AST_SECURITY_EVENT_SUCCESSFUL_AUTH_VERSION,
.common.service = "AMI",
.common.account_id = s->session->username,
.common.session_tv = &s->session->sessionstart_tv,
.common.local_addr = {
.addr = &s->tcptls_session->parent->local_address,
Russell Bryant
committed
.transport = mansession_get_transport(s),
},
.common.remote_addr = {
Russell Bryant
committed
3237
3238
3239
3240
3241
3242
3243
3244
3245
3246
3247
3248
3249
3250
3251
3252
3253
3254
3255
3256
3257
.transport = mansession_get_transport(s),
},
.common.session_id = session_id,
};
snprintf(session_id, sizeof(session_id), "%p", s->session);
ast_security_event_report(AST_SEC_EVT(&successful_auth));
}
static void report_req_not_allowed(const struct mansession *s, const char *action)
{
char session_id[32];
char request_type[64];
struct ast_security_event_req_not_allowed req_not_allowed = {
.common.event_type = AST_SECURITY_EVENT_REQ_NOT_ALLOWED,
.common.version = AST_SECURITY_EVENT_REQ_NOT_ALLOWED_VERSION,
.common.service = "AMI",
.common.account_id = s->session->username,
.common.session_tv = &s->session->sessionstart_tv,
.common.local_addr = {
.addr = &s->tcptls_session->parent->local_address,
Russell Bryant
committed
.transport = mansession_get_transport(s),
},
.common.remote_addr = {
Russell Bryant
committed
3263
3264
3265
3266
3267
3268
3269
3270
3271
3272
3273
3274
3275
3276
3277
3278
3279
3280
3281
3282
3283
3284
3285
3286
.transport = mansession_get_transport(s),
},
.common.session_id = session_id,
.request_type = request_type,
};
snprintf(session_id, sizeof(session_id), "%p", s->session);
snprintf(request_type, sizeof(request_type), "Action: %s", action);
ast_security_event_report(AST_SEC_EVT(&req_not_allowed));
}
static void report_req_bad_format(const struct mansession *s, const char *action)
{
char session_id[32];
char request_type[64];
struct ast_security_event_req_bad_format req_bad_format = {
.common.event_type = AST_SECURITY_EVENT_REQ_BAD_FORMAT,
.common.version = AST_SECURITY_EVENT_REQ_BAD_FORMAT_VERSION,
.common.service = "AMI",
.common.account_id = s->session->username,
.common.session_tv = &s->session->sessionstart_tv,
.common.local_addr = {
.addr = &s->tcptls_session->parent->local_address,
Russell Bryant
committed
.transport = mansession_get_transport(s),
},
.common.remote_addr = {
Russell Bryant
committed
3292
3293
3294
3295
3296
3297
3298
3299
3300
3301
3302
3303
3304
3305
3306
3307
3308
3309
3310
3311
3312
3313
3314
3315
.transport = mansession_get_transport(s),
},
.common.session_id = session_id,
.request_type = request_type,
};
snprintf(session_id, sizeof(session_id), "%p", s->session);
snprintf(request_type, sizeof(request_type), "Action: %s", action);
ast_security_event_report(AST_SEC_EVT(&req_bad_format));
}
static void report_failed_challenge_response(const struct mansession *s,
const char *response, const char *expected_response)
{
char session_id[32];
struct ast_security_event_chal_resp_failed chal_resp_failed = {
.common.event_type = AST_SECURITY_EVENT_CHAL_RESP_FAILED,
.common.version = AST_SECURITY_EVENT_CHAL_RESP_FAILED_VERSION,
.common.service = "AMI",
.common.account_id = s->session->username,
.common.session_tv = &s->session->sessionstart_tv,
.common.local_addr = {
.addr = &s->tcptls_session->parent->local_address,
Russell Bryant
committed
.transport = mansession_get_transport(s),
},
.common.remote_addr = {
Russell Bryant
committed
3321
3322
3323
3324
3325
3326
3327
3328
3329
3330
3331
3332
3333
3334
3335
3336
3337
3338
3339
3340
3341
3342
3343
3344
.transport = mansession_get_transport(s),
},
.common.session_id = session_id,
.challenge = s->session->challenge,
.response = response,
.expected_response = expected_response,
};
snprintf(session_id, sizeof(session_id), "%p", s->session);
ast_security_event_report(AST_SEC_EVT(&chal_resp_failed));
}
static void report_session_limit(const struct mansession *s)
{
char session_id[32];
struct ast_security_event_session_limit session_limit = {
.common.event_type = AST_SECURITY_EVENT_SESSION_LIMIT,
.common.version = AST_SECURITY_EVENT_SESSION_LIMIT_VERSION,
.common.service = "AMI",
.common.account_id = s->session->username,
.common.session_tv = &s->session->sessionstart_tv,
.common.local_addr = {
.addr = &s->tcptls_session->parent->local_address,
Russell Bryant
committed
.transport = mansession_get_transport(s),
},
.common.remote_addr = {
Russell Bryant
committed
.transport = mansession_get_transport(s),
},
.common.session_id = session_id,
};
snprintf(session_id, sizeof(session_id), "%p", s->session);
ast_security_event_report(AST_SEC_EVT(&session_limit));
}
/*
* Here we start with action_ handlers for AMI actions,
* and the internal functions used by them.
* Generally, the handlers are called action_foo()
*/
/* helper function for action_login() */
static int authenticate(struct mansession *s, const struct message *m)
Olle Johansson
committed
const char *username = astman_get_header(m, "Username");
const char *password = astman_get_header(m, "Secret");
Olle Johansson
committed
struct ast_manager_user *user = NULL;
regex_t *regex_filter;
struct ao2_iterator filter_iter;
if (ast_strlen_zero(username)) { /* missing username */
Olle Johansson
committed
/* locate user in locked state */
AST_RWLIST_WRLOCK(&users);
Olle Johansson
committed
if (!(user = get_manager_by_name_locked(username))) {
Russell Bryant
committed
report_invalid_user(s, username);
ast_log(LOG_NOTICE, "%s tried to authenticate with nonexistent user '%s'\n", ast_sockaddr_stringify_addr(&s->session->addr), username);
} else if (user->acl && (ast_apply_acl(user->acl, &s->session->addr, "Manager User ACL: ") == AST_SENSE_DENY)) {
Russell Bryant
committed
report_failed_acl(s, username);
ast_log(LOG_NOTICE, "%s failed to pass IP ACL as '%s'\n", ast_sockaddr_stringify_addr(&s->session->addr), username);
Olle Johansson
committed
} else if (!strcasecmp(astman_get_header(m, "AuthType"), "MD5")) {
const char *key = astman_get_header(m, "Key");
if (!ast_strlen_zero(key) && !ast_strlen_zero(s->session->challenge) && user->secret) {
int x;
int len = 0;
char md5key[256] = "";
struct MD5Context md5;
unsigned char digest[16];
MD5Init(&md5);
MD5Update(&md5, (unsigned char *) s->session->challenge, strlen(s->session->challenge));
MD5Update(&md5, (unsigned char *) user->secret, strlen(user->secret));
MD5Final(digest, &md5);
len += sprintf(md5key + len, "%02hhx", digest[x]);
if (!strcmp(md5key, key)) {
Russell Bryant
committed
} else {
report_failed_challenge_response(s, key, md5key);
ast_debug(1, "MD5 authentication is not possible. challenge: '%s'\n",
S_OR(s->session->challenge, ""));
Russell Bryant
committed
} else if (user->secret) {
if (!strcmp(password, user->secret)) {
error = 0;
} else {
report_inval_password(s, username);
}
Olle Johansson
committed
ast_log(LOG_NOTICE, "%s failed to authenticate as '%s'\n", ast_sockaddr_stringify_addr(&s->session->addr), username);
Olle Johansson
committed
AST_RWLIST_UNLOCK(&users);
Olle Johansson
committed
/* auth complete */
/* All of the user parameters are copied to the session so that in the event
* of a reload and a configuration change, the session parameters are not
* changed. */
ast_copy_string(s->session->username, username, sizeof(s->session->username));
s->session->readperm = user->readperm;
s->session->writeperm = user->writeperm;
s->session->writetimeout = user->writetimeout;
if (user->chanvars) {
s->session->chanvars = ast_variables_dup(user->chanvars);
}
filter_iter = ao2_iterator_init(user->whitefilters, 0);
while ((regex_filter = ao2_iterator_next(&filter_iter))) {
ao2_t_link(s->session->whitefilters, regex_filter, "add white user filter to session");
ao2_t_ref(regex_filter, -1, "remove iterator ref");
}
ao2_iterator_destroy(&filter_iter);
filter_iter = ao2_iterator_init(user->blackfilters, 0);
while ((regex_filter = ao2_iterator_next(&filter_iter))) {
ao2_t_link(s->session->blackfilters, regex_filter, "add black user filter to session");
ao2_t_ref(regex_filter, -1, "remove iterator ref");
ao2_iterator_destroy(&filter_iter);
s->session->sessionstart = time(NULL);
Russell Bryant
committed
s->session->sessionstart_tv = ast_tvnow();
set_eventmask(s, astman_get_header(m, "Events"));
Russell Bryant
committed
report_auth_success(s);
Olle Johansson
committed
AST_RWLIST_UNLOCK(&users);
static int action_ping(struct mansession *s, const struct message *m)
const char *actionid = astman_get_header(m, "ActionID");
struct timeval now = ast_tvnow();
astman_append(s, "Response: Success\r\n");
if (!ast_strlen_zero(actionid)){
astman_append(s, "ActionID: %s\r\n", actionid);
}
astman_append(
s,
"Ping: Pong\r\n"
"Timestamp: %ld.%06lu\r\n"
"\r\n",
(long) now.tv_sec, (unsigned long) now.tv_usec);
static int action_getconfig(struct mansession *s, const struct message *m)
{
struct ast_config *cfg;
const char *fn = astman_get_header(m, "Filename");
const char *category = astman_get_header(m, "Category");
const char *filter = astman_get_header(m, "Filter");
const char *category_name;
int catcount = 0;
int lineno = 0;
struct ast_category *cur_category = NULL;
struct ast_variable *v;
struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
if (ast_strlen_zero(fn)) {
astman_send_error(s, m, "Filename not specified");
return 0;
}
Tilghman Lesher
committed
cfg = ast_config_load2(fn, "manager", config_flags);
if (cfg == CONFIG_STATUS_FILEMISSING) {
astman_send_error(s, m, "Config file not found");
return 0;
} else if (cfg == CONFIG_STATUS_FILEINVALID) {
astman_send_error(s, m, "Config file has invalid format");
return 0;
astman_start_ack(s, m);
while ((cur_category = ast_category_browse_filtered(cfg, category, cur_category, filter))) {
struct ast_str *templates;
category_name = ast_category_get_name(cur_category);
lineno = 0;
astman_append(s, "Category-%06d: %s\r\n", catcount, category_name);
if (ast_category_is_template(cur_category)) {
astman_append(s, "IsTemplate-%06d: %d\r\n", catcount, 1);
}
if ((templates = ast_category_get_templates(cur_category))
&& ast_str_strlen(templates) > 0) {
astman_append(s, "Templates-%06d: %s\r\n", catcount, ast_str_buffer(templates));
ast_free(templates);
for (v = ast_category_first(cur_category); v; v = v->next) {
astman_append(s, "Line-%06d-%06d: %s=%s\r\n", catcount, lineno++, v->name, v->value);
}
catcount++;
if (!ast_strlen_zero(category) && catcount == 0) { /* TODO: actually, a config with no categories doesn't even get loaded */
astman_append(s, "No categories found\r\n");
ast_config_destroy(cfg);
astman_append(s, "\r\n");
return 0;
}
static int action_listcategories(struct mansession *s, const struct message *m)
{
struct ast_config *cfg;
const char *fn = astman_get_header(m, "Filename");
const char *match = astman_get_header(m, "Match");
struct ast_category *category = NULL;
struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
int catcount = 0;
if (ast_strlen_zero(fn)) {
astman_send_error(s, m, "Filename not specified");
return 0;
}
Tilghman Lesher
committed
if (!(cfg = ast_config_load2(fn, "manager", config_flags))) {
astman_send_error(s, m, "Config file not found");
return 0;
} else if (cfg == CONFIG_STATUS_FILEINVALID) {
astman_send_error(s, m, "Config file has invalid format");
astman_start_ack(s, m);
while ((category = ast_category_browse_filtered(cfg, NULL, category, match))) {
astman_append(s, "Category-%06d: %s\r\n", catcount, ast_category_get_name(category));
if (catcount == 0) { /* TODO: actually, a config with no categories doesn't even get loaded */
astman_append(s, "Error: no categories found\r\n");
ast_config_destroy(cfg);
astman_append(s, "\r\n");
return 0;
}
Russell Bryant
committed
/*! The amount of space in out must be at least ( 2 * strlen(in) + 1 ) */
static void json_escape(char *out, const char *in)
{
for (; *in; in++) {
if (*in == '\\' || *in == '\"') {
Russell Bryant
committed
*out++ = '\\';
Russell Bryant
committed
*out++ = *in;
}
*out = '\0';
}
/*!
* \internal
* \brief Append a JSON escaped string to the manager stream.
*
* \param s AMI stream to append a string.
* \param str String to append to the stream after JSON escaping it.
*
* \return Nothing
*/
static void astman_append_json(struct mansession *s, const char *str)
{
char *buf;
buf = ast_alloca(2 * strlen(str) + 1);
json_escape(buf, str);
astman_append(s, "%s", buf);
}
Russell Bryant
committed
static int action_getconfigjson(struct mansession *s, const struct message *m)
{
struct ast_config *cfg;
const char *fn = astman_get_header(m, "Filename");
const char *filter = astman_get_header(m, "Filter");
const char *category = astman_get_header(m, "Category");
struct ast_category *cur_category = NULL;
const char *category_name;
Russell Bryant
committed
struct ast_variable *v;
int comma1 = 0;
struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
Russell Bryant
committed
if (ast_strlen_zero(fn)) {
astman_send_error(s, m, "Filename not specified");
return 0;
}
Tilghman Lesher
committed
if (!(cfg = ast_config_load2(fn, "manager", config_flags))) {
Russell Bryant
committed
astman_send_error(s, m, "Config file not found");
return 0;
} else if (cfg == CONFIG_STATUS_FILEINVALID) {
astman_send_error(s, m, "Config file has invalid format");
return 0;
Russell Bryant
committed
}
astman_start_ack(s, m);
astman_append(s, "JSON: {");
while ((cur_category = ast_category_browse_filtered(cfg, category, cur_category, filter))) {
Russell Bryant
committed
int comma2 = 0;
struct ast_str *templates;
category_name = ast_category_get_name(cur_category);
astman_append(s, "%s\"", comma1 ? "," : "");
astman_append_json(s, category_name);
astman_append(s, "\":{");
if (ast_category_is_template(cur_category)) {
astman_append(s, "\"istemplate\":1");
comma2 = 1;
}
if ((templates = ast_category_get_templates(cur_category))
&& ast_str_strlen(templates) > 0) {
astman_append(s, "%s", comma2 ? "," : "");
astman_append(s, "\"templates\":\"%s\"", ast_str_buffer(templates));
ast_free(templates);
comma2 = 1;
}
for (v = ast_category_first(cur_category); v; v = v->next) {
astman_append(s, "%s\"", comma2 ? "," : "");
astman_append_json(s, v->name);
astman_append(s, "\":\"");
astman_append_json(s, v->value);
astman_append(s, "\"");
comma2 = 1;
Russell Bryant
committed
}
astman_append(s, "}");
Russell Bryant
committed
}
astman_append(s, "}\r\n\r\n");
ast_config_destroy(cfg);
return 0;
}
/*! \brief helper function for action_updateconfig */
static enum error_type handle_updates(struct mansession *s, const struct message *m, struct ast_config *cfg, const char *dfn)
{
int x;
char hdr[40];
const char *action, *cat, *var, *value, *match, *line, *options;
struct ast_variable *v;
struct ast_str *str1 = ast_str_create(16), *str2 = ast_str_create(16);
enum error_type result = 0;
for (x = 0; x < 100000; x++) { /* 100000 = the max number of allowed updates + 1 */
char *dupoptions;
int allowdups = 0;
int istemplate = 0;
int ignoreerror = 0;
char *inherit = NULL;
char *catfilter = NULL;
char *token;
int foundvar = 0;
int foundcat = 0;
struct ast_category *category = NULL;
snprintf(hdr, sizeof(hdr), "Action-%06d", x);
action = astman_get_header(m, hdr);
if (ast_strlen_zero(action)) /* breaks the for loop if no action header */
break; /* this could cause problems if actions come in misnumbered */
snprintf(hdr, sizeof(hdr), "Cat-%06d", x);
cat = astman_get_header(m, hdr);
snprintf(hdr, sizeof(hdr), "Var-%06d", x);
var = astman_get_header(m, hdr);
snprintf(hdr, sizeof(hdr), "Value-%06d", x);
value = astman_get_header(m, hdr);
if (!ast_strlen_zero(value) && *value == '>') {
object = 1;
value++;
}
snprintf(hdr, sizeof(hdr), "Match-%06d", x);
match = astman_get_header(m, hdr);
snprintf(hdr, sizeof(hdr), "Line-%06d", x);
line = astman_get_header(m, hdr);
3732
3733
3734
3735
3736
3737
3738
3739
3740
3741
3742
3743
3744
3745
3746
3747
3748
3749
3750
3751
3752
3753
3754
3755
3756
3757
3758
3759
3760
3761
3762
3763
3764
3765
3766
3767
snprintf(hdr, sizeof(hdr), "Options-%06d", x);
options = astman_get_header(m, hdr);
if (!ast_strlen_zero(options)) {
dupoptions = ast_strdupa(options);
while ((token = ast_strsep(&dupoptions, ',', AST_STRSEP_STRIP))) {
if (!strcasecmp("allowdups", token)) {
allowdups = 1;
continue;
}
if (!strcasecmp("template", token)) {
istemplate = 1;
continue;
}
if (!strcasecmp("ignoreerror", token)) {
ignoreerror = 1;
continue;
}
if (ast_begins_with(token, "inherit")) {
char *c = ast_strsep(&token, '=', AST_STRSEP_STRIP);
c = ast_strsep(&token, '=', AST_STRSEP_STRIP);
if (c) {
inherit = ast_strdupa(c);
}
continue;
}
if (ast_begins_with(token, "catfilter")) {
char *c = ast_strsep(&token, '=', AST_STRSEP_STRIP);
c = ast_strsep(&token, '=', AST_STRSEP_STRIP);
if (c) {
catfilter = ast_strdupa(c);
}
continue;
}
}
}
if (!strcasecmp(action, "newcat")) {
struct ast_category *template;
char *tmpl_name = NULL;
if (!allowdups) {
if (ast_category_get(cfg, cat, "TEMPLATES=include")) {
if (ignoreerror) {
continue;
} else {
result = FAILURE_NEWCAT; /* already exist */
break;
}
}
}
if (istemplate) {
category = ast_category_new_template(cat, dfn, -1);
} else {
category = ast_category_new(cat, dfn, -1);
}
if (!category) {
result = FAILURE_ALLOCATION;
break;
}
if (inherit) {
while ((tmpl_name = ast_strsep(&inherit, ',', AST_STRSEP_STRIP))) {
if ((template = ast_category_get(cfg, tmpl_name, "TEMPLATES=restrict"))) {
if (ast_category_inherit(category, template)) {
result = FAILURE_ALLOCATION;
break;
}
} else {
ast_category_destroy(category);
category = NULL;
result = FAILURE_TEMPLATE; /* template not found */
break;
}
}
}
if (category != NULL) {
if (ast_strlen_zero(match)) {
ast_category_append(cfg, category);
} else {
if (ast_category_insert(cfg, category, match)) {
ast_category_destroy(category);
result = FAILURE_NEWCAT;
break;
}
George Joseph
committed
}
} else if (!strcasecmp(action, "renamecat")) {
result = UNSPECIFIED_ARGUMENT;
break;
}
foundcat = 0;
while ((category = ast_category_browse_filtered(cfg, cat, category, catfilter))) {
ast_category_rename(category, value);
foundcat = 1;
}
if (!foundcat) {
result = UNKNOWN_CATEGORY;
break;
}
} else if (!strcasecmp(action, "delcat")) {
foundcat = 0;
while ((category = ast_category_browse_filtered(cfg, cat, category, catfilter))) {
category = ast_category_delete(cfg, category);
foundcat = 1;
}
if (!foundcat && !ignoreerror) {
result = UNKNOWN_CATEGORY;
break;
}
} else if (!strcasecmp(action, "emptycat")) {
foundcat = 0;
while ((category = ast_category_browse_filtered(cfg, cat, category, catfilter))) {
ast_category_empty(category);
foundcat = 1;
}
if (!foundcat) {
result = UNKNOWN_CATEGORY;
break;
}
} else if (!strcasecmp(action, "update")) {
result = UNSPECIFIED_ARGUMENT;
break;
}
foundcat = 0;
foundvar = 0;
while ((category = ast_category_browse_filtered(cfg, cat, category, catfilter))) {
if (!ast_variable_update(category, var, value, match, object)) {
foundvar = 1;
}
foundcat = 1;
}
if (!foundcat) {
result = UNKNOWN_CATEGORY;
break;
}
if (!foundvar) {
result = FAILURE_UPDATE;
break;
}
} else if (!strcasecmp(action, "delete")) {
result = UNSPECIFIED_ARGUMENT;
break;
}
foundcat = 0;
foundvar = 0;
while ((category = ast_category_browse_filtered(cfg, cat, category, catfilter))) {
if (!ast_variable_delete(category, var, match, line)) {
foundvar = 1;
}
foundcat = 1;
}
if (!foundcat) {
result = UNKNOWN_CATEGORY;
break;
}
if (!foundvar && !ignoreerror) {
result = FAILURE_UPDATE;
break;
}
} else if (!strcasecmp(action, "append")) {
result = UNSPECIFIED_ARGUMENT;
break;
}
foundcat = 0;
while ((category = ast_category_browse_filtered(cfg, cat, category, catfilter))) {
if (!(v = ast_variable_new(var, value, dfn))) {
result = FAILURE_ALLOCATION;
break;
}
if (object || (match && !strcasecmp(match, "object"))) {
v->object = 1;
}
ast_variable_append(category, v);
foundcat = 1;
}
if (!foundcat) {
result = UNKNOWN_CATEGORY;
break;
}
} else if (!strcasecmp(action, "insert")) {
result = UNSPECIFIED_ARGUMENT;
break;
}
foundcat = 0;
while ((category = ast_category_browse_filtered(cfg, cat, category, catfilter))) {
if (!(v = ast_variable_new(var, value, dfn))) {
result = FAILURE_ALLOCATION;
break;
}
ast_variable_insert(category, v, line);
foundcat = 1;
}
if (!foundcat) {
result = UNKNOWN_CATEGORY;
break;
}
}
else {
ast_log(LOG_WARNING, "Action-%06d: %s not handled\n", x, action);
result = UNKNOWN_ACTION;
break;
ast_free(str1);
ast_free(str2);
return result;
static int action_updateconfig(struct mansession *s, const struct message *m)
{
struct ast_config *cfg;
const char *sfn = astman_get_header(m, "SrcFilename");
const char *dfn = astman_get_header(m, "DstFilename");
const char *rld = astman_get_header(m, "Reload");
George Joseph
committed
int preserve_effective_context = CONFIG_SAVE_FLAG_PRESERVE_EFFECTIVE_CONTEXT;
const char *preserve_effective_context_string = astman_get_header(m, "PreserveEffectiveContext");
struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
enum error_type result;
if (ast_strlen_zero(sfn) || ast_strlen_zero(dfn)) {
astman_send_error(s, m, "Filename not specified");
return 0;
}
Tilghman Lesher
committed
if (!(cfg = ast_config_load2(sfn, "manager", config_flags))) {
astman_send_error(s, m, "Config file not found");
return 0;
} else if (cfg == CONFIG_STATUS_FILEINVALID) {
astman_send_error(s, m, "Config file has invalid format");
return 0;
result = handle_updates(s, m, cfg, dfn);
if (!result) {
ast_include_rename(cfg, sfn, dfn); /* change the include references from dfn to sfn, so things match up */
George Joseph
committed
if (!ast_strlen_zero(preserve_effective_context_string) && !ast_true(preserve_effective_context_string)) {
preserve_effective_context = CONFIG_SAVE_FLAG_NONE;
}
res = ast_config_text_file_save2(dfn, cfg, "Manager", preserve_effective_context);
ast_config_destroy(cfg);
if (res) {
astman_send_error(s, m, "Save of config failed");
return 0;
}
astman_send_ack(s, m, NULL);
if (!ast_strlen_zero(rld)) {
if (ast_true(rld)) {