diff options
Diffstat (limited to 'main/manager.c')
-rw-r--r-- | main/manager.c | 1570 |
1 files changed, 1118 insertions, 452 deletions
diff --git a/main/manager.c b/main/manager.c index 8d5511669..5989e4786 100644 --- a/main/manager.c +++ b/main/manager.c @@ -22,7 +22,7 @@ * * \author Mark Spencer <markster@digium.com> * - * \extref OpenSSL http://www.openssl.org - for AMI/SSL + * \extref OpenSSL http://www.openssl.org - for AMI/SSL * * At the moment this file contains a number of functions, namely: * @@ -126,8 +126,10 @@ static int httptimeout = 60; static int manager_enabled = 0; static int webmanager_enabled = 0; +#define DEFAULT_REALM "asterisk" +static char global_realm[MAXHOSTNAMELEN]; /*!< Default realm */ + static int block_sockets; -static int num_sessions; static int manager_debug; /*!< enable some debugging code in the manager */ @@ -138,7 +140,8 @@ static int manager_debug; /*!< enable some debugging code in the manager */ * AMI session have managerid == 0; the entry is created upon a connect, * and destroyed with the socket. * HTTP sessions have managerid != 0, the value is used as a search key - * to lookup sessions (using the mansession_id cookie). + * to lookup sessions (using the mansession_id cookie, or nonce key from + * Digest Authentication http header). */ #define MAX_BLACKLIST_CMD_LEN 2 static struct { @@ -183,7 +186,6 @@ static struct { */ struct mansession_session { pthread_t ms_t; /*!< Execution thread, basically useless */ - ast_mutex_t __lock; /*!< Thread lock -- don't use in action callbacks, it's already taken care of */ /* XXX need to document which fields it is protecting */ struct sockaddr_in sin; /*!< address we are connecting from */ FILE *f; /*!< fdopen() on the underlying fd */ @@ -206,6 +208,9 @@ struct mansession_session { struct eventqent *last_ev; /*!< last event processed. */ int writetimeout; /*!< Timeout for ast_carefulwrite() */ int pending_event; /*!< Pending events indicator in case when waiting_thread is NULL */ + time_t noncetime; /*!< Timer for nonce value expiration */ + unsigned long oldnonce; /*!< Stale nonce value */ + unsigned long nc; /*!< incremental nonce counter */ AST_LIST_HEAD_NOLOCK(mansession_datastores, ast_datastore) datastores; /*!< Data stores on the session */ AST_LIST_ENTRY(mansession_session) list; }; @@ -221,9 +226,9 @@ struct mansession { int fd; }; -#define NEW_EVENT(m) (AST_LIST_NEXT(m->session->last_ev, eq_next)) +static struct ao2_container *sessions = NULL; -static AST_LIST_HEAD_STATIC(sessions, mansession_session); +#define NEW_EVENT(m) (AST_LIST_NEXT(m->session->last_ev, eq_next)) /*! \brief user descriptor, as read from the config file. * @@ -238,8 +243,9 @@ struct ast_manager_user { int readperm; /*! Authorization for reading */ int writeperm; /*! Authorization for writing */ int writetimeout; /*! Per user Timeout for ast_carefulwrite() */ - int displayconnects; /*!< XXX unused */ - int keep; /*!< mark entries created on a reload */ + int displayconnects; /*!< XXX unused */ + int keep; /*!< mark entries created on a reload */ + char *a1_hash; /*!< precalculated A1 for Digest auth */ AST_RWLIST_ENTRY(ast_manager_user) list; }; @@ -252,6 +258,9 @@ static AST_RWLIST_HEAD_STATIC(actions, manager_action); /*! \brief list of hooks registered */ static AST_RWLIST_HEAD_STATIC(manager_hooks, manager_custom_hook); +static struct eventqent *unref_event(struct eventqent *e); +static void ref_event(struct eventqent *e); + /*! \brief Add a custom hook to be called when an event is fired */ void ast_manager_register_hook(struct manager_custom_hook *hook) { @@ -274,7 +283,7 @@ void ast_manager_unregister_hook(struct manager_custom_hook *hook) * Event list management functions. * We assume that the event list always has at least one element, * and the delete code will not remove the last entry even if the - * + * */ #if 0 static time_t __deb(time_t start, const char *msg) @@ -336,8 +345,9 @@ static struct eventqent *grab_last(void) /* the list is never empty now, but may become so when * we optimize it in the future, so be prepared. */ - if (ret) + if (ret) { ast_atomic_fetchadd_int(&ret->usecount, 1); + } AST_LIST_UNLOCK(&all_events); return ret; } @@ -416,12 +426,14 @@ static int ast_instring(const char *bigstr, const char *smallstr, const char del do { if ((next = strchr(val, delim))) { - if (!strncmp(val, smallstr, (next - val))) + if (!strncmp(val, smallstr, (next - val))) { return 1; - else + } else { continue; - } else + } + } else { return !strcmp(smallstr, val); + } } while (*(val = (next + 1))); return 0; @@ -431,12 +443,14 @@ static int get_perm(const char *instr) { int x = 0, ret = 0; - if (!instr) + if (!instr) { return 0; + } for (x = 0; x < ARRAY_LEN(perms); x++) { - if (ast_instring(instr, perms[x].label, ',')) + if (ast_instring(instr, perms[x].label, ',')) { ret |= perms[x].num; + } } return ret; @@ -450,36 +464,93 @@ static int strings_to_mask(const char *string) { const char *p; - if (ast_strlen_zero(string)) + if (ast_strlen_zero(string)) { return -1; + } - for (p = string; *p; p++) - if (*p < '0' || *p > '9') + for (p = string; *p; p++) { + if (*p < '0' || *p > '9') { break; - if (!p) /* all digits */ + } + } + if (!p) { /* all digits */ return atoi(string); - if (ast_false(string)) + } + if (ast_false(string)) { return 0; + } if (ast_true(string)) { /* all permissions */ int x, ret = 0; - for (x = 0; x < ARRAY_LEN(perms); x++) + for (x = 0; x < ARRAY_LEN(perms); x++) { ret |= perms[x].num; + } return ret; } return get_perm(string); } -static int check_manager_session_inuse(const char *name) +/*! \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) { - struct mansession_session *session = NULL; + int refcount = ao2_ref(s, -1); + if (manager_debug) { + ast_log(LOG_DEBUG, "Mansession: %p refcount now %d\n", s, refcount - 1); + } + return s; +} - AST_LIST_LOCK(&sessions); - AST_LIST_TRAVERSE(&sessions, session, list) { - if (!strcasecmp(session->username, name)) - break; +static void session_destructor(void *obj) +{ + struct mansession_session *session = obj; + struct eventqent *eqe = session->last_ev; + + if (session->f != NULL) { + fclose(session->f); } - AST_LIST_UNLOCK(&sessions); + unref_event(eqe); +} + +/*! \brief Allocate manager session structure and add it to the list of sessions */ +static struct mansession_session *build_mansession(struct sockaddr_in sin) +{ + struct mansession_session *newsession; + + if (!(newsession = ao2_alloc(sizeof(*newsession), session_destructor))) { + return NULL; + } + memset(newsession, 0, sizeof(*newsession)); + newsession->fd = -1; + newsession->waiting_thread = AST_PTHREADT_NULL; + newsession->writetimeout = 100; + newsession->send_events = -1; + newsession->sin = sin; + + ao2_link(sessions, newsession); + + return unref_mansession(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) +{ + ao2_unlink(sessions, s); +} + + +static int check_manager_session_inuse(const char *name) +{ + struct mansession_session *session = ao2_find(sessions, (char*) name, OBJ_POINTER); + if (session) { + unref_mansession(session); + } return session ? 1 : 0; } @@ -493,8 +564,9 @@ 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)) + if (!strcasecmp(user->username, name)) { break; + } return user; } @@ -508,10 +580,11 @@ static int manager_displayconnects (struct mansession_session *session) int ret = 0; AST_RWLIST_RDLOCK(&users); - if ((user = get_manager_by_name_locked (session->username))) + if ((user = get_manager_by_name_locked (session->username))) { ret = user->displayconnects; + } AST_RWLIST_UNLOCK(&users); - + return ret; } @@ -524,7 +597,7 @@ static char *handle_showmancmd(struct ast_cli_entry *e, int cmd, struct ast_cli_ switch (cmd) { case CLI_INIT: e->command = "manager show command"; - e->usage = + e->usage = "Usage: manager show command <actionname>\n" " Shows the detailed description for a specific Asterisk manager interface command.\n"; return NULL; @@ -542,8 +615,9 @@ static char *handle_showmancmd(struct ast_cli_entry *e, int cmd, struct ast_cli_ return ret; } authority = ast_str_alloca(80); - if (a->argc != 4) + if (a->argc != 4) { return CLI_SHOWUSAGE; + } AST_RWLIST_RDLOCK(&actions); AST_RWLIST_TRAVERSE(&actions, cur, list) { @@ -569,17 +643,19 @@ static char *handle_mandebug(struct ast_cli_entry *e, int cmd, struct ast_cli_ar e->usage = "Usage: manager set debug [on|off]\n Show, enable, disable debugging of the manager code.\n"; return NULL; case CLI_GENERATE: - return NULL; + return NULL; } - if (a->argc == 3) + + 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")) + } else if (a->argc == 4) { + if (!strcasecmp(a->argv[3], "on")) { manager_debug = 1; - else if (!strcasecmp(a->argv[3], "off")) + } else if (!strcasecmp(a->argv[3], "off")) { manager_debug = 0; - else + } else { return CLI_SHOWUSAGE; + } } return CLI_SUCCESS; } @@ -595,15 +671,16 @@ static char *handle_showmanager(struct ast_cli_entry *e, int cmd, struct ast_cli switch (cmd) { case CLI_INIT: e->command = "manager show user"; - e->usage = + 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) + if (a->pos != 3) { return NULL; + } AST_RWLIST_RDLOCK(&users); AST_RWLIST_TRAVERSE(&users, user, list) { if ( !strncasecmp(a->word, user->username, l) && ++which > a->n ) { @@ -615,8 +692,9 @@ static char *handle_showmanager(struct ast_cli_entry *e, int cmd, struct ast_cli return ret; } - if (a->argc != 4) + if (a->argc != 4) { return CLI_SHOWUSAGE; + } AST_RWLIST_RDLOCK(&users); @@ -654,7 +732,7 @@ static char *handle_showmanagers(struct ast_cli_entry *e, int cmd, struct ast_cl switch (cmd) { case CLI_INIT: e->command = "manager show users"; - e->usage = + e->usage = "Usage: manager show users\n" " Prints a listing of all managers that are currently configured on that\n" " system.\n"; @@ -662,8 +740,9 @@ static char *handle_showmanagers(struct ast_cli_entry *e, int cmd, struct ast_cl case CLI_GENERATE: return NULL; } - if (a->argc != 3) + if (a->argc != 3) { return CLI_SHOWUSAGE; + } AST_RWLIST_RDLOCK(&users); @@ -683,9 +762,8 @@ static char *handle_showmanagers(struct ast_cli_entry *e, int cmd, struct ast_cl AST_RWLIST_UNLOCK(&users); - ast_cli(a->fd, "-------------------\n"); - ast_cli(a->fd, "%d manager users configured.\n", count_amu); - + ast_cli(a->fd,"-------------------\n" + "%d manager users configured.\n", count_amu); return CLI_SUCCESS; } @@ -699,13 +777,13 @@ static char *handle_showmancmds(struct ast_cli_entry *e, int cmd, struct ast_cli switch (cmd) { case CLI_INIT: e->command = "manager show commands"; - e->usage = + 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; - } + return NULL; + } authority = ast_str_alloca(80); ast_cli(a->fd, HSMC_FORMAT, "Action", "Privilege", "Synopsis"); ast_cli(a->fd, HSMC_FORMAT, "------", "---------", "--------"); @@ -726,26 +804,30 @@ static char *handle_showmanconn(struct ast_cli_entry *e, int cmd, struct ast_cli #define HSMCONN_FORMAT1 " %-15.15s %-15.15s %-10.10s %-10.10s %-8.8s %-8.8s %-5.5s %-5.5s\n" #define HSMCONN_FORMAT2 " %-15.15s %-15.15s %-10d %-10d %-8d %-8d %-5.5d %-5.5d\n" int count = 0; + struct ao2_iterator i; + switch (cmd) { case CLI_INIT: e->command = "manager show connected"; - e->usage = + 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; + return NULL; } ast_cli(a->fd, HSMCONN_FORMAT1, "Username", "IP Address", "Start", "Elapsed", "FileDes", "HttpCnt", "Read", "Write"); - AST_LIST_LOCK(&sessions); - AST_LIST_TRAVERSE(&sessions, session, list) { + i = ao2_iterator_init(sessions, 0); + while ((session = ao2_iterator_next(&i))) { + ao2_lock(session); ast_cli(a->fd, HSMCONN_FORMAT2, session->username, ast_inet_ntoa(session->sin.sin_addr), (int)(session->sessionstart), (int)(now - session->sessionstart), session->fd, session->inuse, session->readperm, session->writeperm); count++; + ao2_unlock(session); + unref_mansession(session); } - AST_LIST_UNLOCK(&sessions); ast_cli(a->fd, "%d users connected.\n", count); @@ -760,7 +842,7 @@ static char *handle_showmaneventq(struct ast_cli_entry *e, int cmd, struct ast_c switch (cmd) { case CLI_INIT: e->command = "manager show eventq"; - e->usage = + e->usage = "Usage: manager show eventq\n" " Prints a listing of all events pending in the Asterisk manger\n" "event queue.\n"; @@ -792,8 +874,9 @@ static char *handle_manager_reload(struct ast_cli_entry *e, int cmd, struct ast_ case CLI_GENERATE: return NULL; } - if (a->argc > 2) + if (a->argc > 2) { return CLI_SHOWUSAGE; + } reload_manager(); return CLI_SUCCESS; } @@ -810,12 +893,6 @@ static struct ast_cli_entry cli_manager[] = { AST_CLI_DEFINE(handle_manager_reload, "Reload manager configurations"), }; -/* - * Decrement the usecount for the event; if it goes to zero, - * (why check for e->next ?) wakeup the - * main thread, which is in charge of freeing the record. - * Returns the next record. - */ static struct eventqent *unref_event(struct eventqent *e) { ast_atomic_fetchadd_int(&e->usecount, -1); @@ -843,18 +920,14 @@ static void free_session(struct mansession_session *session) if (session->f != NULL) fclose(session->f); - ast_mutex_destroy(&session->__lock); ast_free(session); unref_event(eqe); } static void destroy_session(struct mansession_session *session) { - AST_LIST_LOCK(&sessions); - AST_LIST_REMOVE(&sessions, session, list); - ast_atomic_fetchadd_int(&num_sessions, -1); + ao2_unlink(sessions, session); free_session(session); - AST_LIST_UNLOCK(&sessions); } /* @@ -913,20 +986,24 @@ struct ast_variable *astman_get_variables(const struct message *m) for (x = 0; x < m->hdrcount; x++) { char *parse, *var, *val; - if (strncasecmp("Variable: ", m->headers[x], varlen)) + if (strncasecmp("Variable: ", m->headers[x], varlen)) { continue; + } parse = ast_strdupa(m->headers[x] + varlen); AST_STANDARD_APP_ARGS(args, parse); - if (!args.argc) + if (!args.argc) { continue; + } for (y = 0; y < args.argc; y++) { - if (!args.vars[y]) + if (!args.vars[y]) { continue; + } var = val = ast_strdupa(args.vars[y]); strsep(&val, "="); - if (!val || ast_strlen_zero(var)) + if (!val || ast_strlen_zero(var)) { continue; + } cur = ast_variable_new(var, val, ""); cur->next = head; head = cur; @@ -970,8 +1047,9 @@ void astman_append(struct mansession *s, const char *fmt, ...) va_list ap; struct ast_str *buf; - if (!(buf = ast_str_thread_get(&astman_append_buf, ASTMAN_APPEND_BUF_INITSIZE))) + if (!(buf = ast_str_thread_get(&astman_append_buf, ASTMAN_APPEND_BUF_INITSIZE))) { return; + } va_start(ap, fmt); ast_str_set_va(&buf, 0, fmt, ap); @@ -1006,16 +1084,19 @@ static void astman_send_response_full(struct mansession *s, const struct message const char *id = astman_get_header(m, "ActionID"); astman_append(s, "Response: %s\r\n", resp); - if (!ast_strlen_zero(id)) + if (!ast_strlen_zero(id)) { astman_append(s, "ActionID: %s\r\n", id); - if (listflag) + } + if (listflag) { astman_append(s, "Eventlist: %s\r\n", listflag); /* Start, complete, cancelled */ - if (msg == MSG_MOREDATA) + } + if (msg == MSG_MOREDATA) { return; - else if (msg) + } else if (msg) { astman_append(s, "Message: %s\r\n\r\n", msg); - else + } else { astman_append(s, "\r\n"); + } } void astman_send_response(struct mansession *s, const struct message *m, char *resp, char *msg) @@ -1052,10 +1133,11 @@ static int set_eventmask(struct mansession *s, const char *eventmask) { int maskint = strings_to_mask(eventmask); - ast_mutex_lock(&s->session->__lock); - if (maskint >= 0) + ao2_lock(s); + if (maskint >= 0) { s->session->send_events = maskint; - ast_mutex_unlock(&s->session->__lock); + } + ao2_unlock(s); return maskint; } @@ -1074,8 +1156,9 @@ static int authenticate(struct mansession *s, const struct message *m) int error = -1; struct ast_manager_user *user = NULL; - if (ast_strlen_zero(username)) /* missing username */ + if (ast_strlen_zero(username)) { /* missing username */ return -1; + } /* locate user in locked state */ AST_RWLIST_WRLOCK(&users); @@ -1099,14 +1182,16 @@ static int authenticate(struct mansession *s, const struct message *m) MD5Final(digest, &md5); for (x = 0; x < 16; x++) len += sprintf(md5key + len, "%2.2x", digest[x]); - if (!strcmp(md5key, key)) + if (!strcmp(md5key, key)) { error = 0; + } } else { - ast_debug(1, "MD5 authentication is not possible. challenge: '%s'\n", + ast_debug(1, "MD5 authentication is not possible. challenge: '%s'\n", S_OR(s->session->challenge, "")); } - } else if (password && user->secret && !strcmp(password, user->secret)) + } else if (password && user->secret && !strcmp(password, user->secret)) { error = 0; + } if (error) { ast_log(LOG_NOTICE, "%s failed to authenticate as '%s'\n", ast_inet_ntoa(s->session->sin.sin_addr), username); @@ -1115,14 +1200,14 @@ static int authenticate(struct mansession *s, const struct message *m) } /* auth complete */ - + 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; s->session->sessionstart = time(NULL); set_eventmask(s, astman_get_header(m, "Events")); - + AST_RWLIST_UNLOCK(&users); return 0; } @@ -1178,13 +1263,15 @@ static int action_getconfig(struct mansession *s, const struct message *m) if (ast_strlen_zero(category) || (!ast_strlen_zero(category) && !strcmp(category, cur_category))) { lineno = 0; astman_append(s, "Category-%06d: %s\r\n", catcount, cur_category); - for (v = ast_variable_browse(cfg, cur_category); v; v = v->next) + for (v = ast_variable_browse(cfg, 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 */ + 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"); @@ -1218,8 +1305,9 @@ static int action_listcategories(struct mansession *s, const struct message *m) astman_append(s, "Category-%06d: %s\r\n", catcount, category); catcount++; } - if (catcount == 0) /* TODO: actually, a config with no categories doesn't even get loaded */ + 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"); @@ -1227,14 +1315,15 @@ static int action_listcategories(struct mansession *s, const struct message *m) } - + /*! 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 == '\"') + if (*in == '\\' || *in == '\"') { *out++ = '\\'; + } *out++ = *in; } *out = '\0'; @@ -1281,11 +1370,13 @@ static int action_getconfigjson(struct mansession *s, const struct message *m) } json_escape(buf, category); astman_append(s, "%s\"%s\":[", comma1 ? "," : "", buf); - if (!comma1) + if (!comma1) { comma1 = 1; + } for (v = ast_variable_browse(cfg, category); v; v = v->next) { - if (comma2) + if (comma2) { astman_append(s, ","); + } if (buf_len < 2 * strlen(v->name) + 1) { buf_len *= 2; buf = alloca(buf_len); @@ -1298,8 +1389,9 @@ static int action_getconfigjson(struct mansession *s, const struct message *m) } json_escape(buf, v->value); astman_append(s, "%s\"", buf); - if (!comma2) + if (!comma2) { comma2 = 1; + } } astman_append(s, "]"); } @@ -1346,7 +1438,7 @@ static enum error_type handle_updates(struct mansession *s, const struct message object = 1; value++; } - + snprintf(hdr, sizeof(hdr), "Match-%06d", x); match = astman_get_header(m, hdr); @@ -1364,8 +1456,9 @@ static enum error_type handle_updates(struct mansession *s, const struct message } if (ast_strlen_zero(match)) { ast_category_append(cfg, category); - } else + } else { ast_category_insert(cfg, category, match); + } } else if (!strcasecmp(action, "renamecat")) { if (ast_strlen_zero(value)) { result = UNSPECIFIED_ARGUMENT; @@ -1418,15 +1511,16 @@ static enum error_type handle_updates(struct mansession *s, const struct message break; } if (!(category = ast_category_get(cfg, cat))) { - result = UNKNOWN_CATEGORY; + result = UNKNOWN_CATEGORY; break; } if (!(v = ast_variable_new(var, value, dfn))) { result = FAILURE_ALLOCATION; break; } - if (object || (match && !strcasecmp(match, "object"))) + if (object || (match && !strcasecmp(match, "object"))) { v->object = 1; + } ast_variable_append(category, v); } else if (!strcasecmp(action, "insert")) { if (ast_strlen_zero(var) || ast_strlen_zero(line)) { @@ -1497,8 +1591,9 @@ static int action_updateconfig(struct mansession *s, const struct message *m) } astman_send_ack(s, m, NULL); if (!ast_strlen_zero(rld)) { - if (ast_true(rld)) + if (ast_true(rld)) { rld = NULL; + } ast_module_reload(rld); } } else { @@ -1584,21 +1679,24 @@ static int action_waitevent(struct mansession *s, const struct message *m) const char *id = astman_get_header(m, "ActionID"); char idText[256]; - if (!ast_strlen_zero(id)) + if (!ast_strlen_zero(id)) { snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id); - else + } else { idText[0] = '\0'; + } if (!ast_strlen_zero(timeouts)) { sscanf(timeouts, "%i", &timeout); - if (timeout < -1) + if (timeout < -1) { timeout = -1; + } /* XXX maybe put an upper bound, or prevent the use of 0 ? */ } - ast_mutex_lock(&s->session->__lock); - if (s->session->waiting_thread != AST_PTHREADT_NULL) + ao2_lock(s); + if (s->session->waiting_thread != AST_PTHREADT_NULL) { pthread_kill(s->session->waiting_thread, SIGURG); + } if (s->session->managerid) { /* AMI-over-HTTP session */ /* @@ -1609,43 +1707,52 @@ static int action_waitevent(struct mansession *s, const struct message *m) time_t now = time(NULL); int max = s->session->sessiontimeout - now - 10; - if (max < 0) /* We are already late. Strange but possible. */ + if (max < 0) { /* We are already late. Strange but possible. */ max = 0; - if (timeout < 0 || timeout > max) + } + if (timeout < 0 || timeout > max) { timeout = max; - if (!s->session->send_events) /* make sure we record events */ + } + if (!s->session->send_events) { /* make sure we record events */ s->session->send_events = -1; + } } - ast_mutex_unlock(&s->session->__lock); + ao2_unlock(s); /* XXX should this go inside the lock ? */ s->session->waiting_thread = pthread_self(); /* let new events wake up this thread */ ast_debug(1, "Starting waiting for an event!\n"); for (x = 0; x < timeout || timeout < 0; x++) { - ast_mutex_lock(&s->session->__lock); - if (NEW_EVENT(s)) + ao2_lock(s); + if (NEW_EVENT(s)) { needexit = 1; + } /* We can have multiple HTTP session point to the same mansession entry. * The way we deal with it is not very nice: newcomers kick out the previous * HTTP session. XXX this needs to be improved. */ - if (s->session->waiting_thread != pthread_self()) + if (s->session->waiting_thread != pthread_self()) { needexit = 1; - if (s->session->needdestroy) + } + if (s->session->needdestroy) { needexit = 1; - ast_mutex_unlock(&s->session->__lock); - if (needexit) + } + ao2_unlock(s); + if (needexit) { break; + } if (s->session->managerid == 0) { /* AMI session */ - if (ast_wait_for_input(s->session->fd, 1000)) + if (ast_wait_for_input(s->session->fd, 1000)) { break; + } } else { /* HTTP session */ sleep(1); } } ast_debug(1, "Finished waiting for an event!\n"); - ast_mutex_lock(&s->session->__lock); + + ao2_lock(s); if (s->session->waiting_thread == pthread_self()) { struct eventqent *eqe; astman_send_response(s, m, "Success", "Waiting for Event completed."); @@ -1665,7 +1772,7 @@ static int action_waitevent(struct mansession *s, const struct message *m) } else { ast_debug(1, "Abandoning event request!\n"); } - ast_mutex_unlock(&s->session->__lock); + ao2_unlock(s); return 0; } @@ -1682,9 +1789,10 @@ static int action_listcommands(struct mansession *s, const struct message *m) astman_start_ack(s, m); AST_RWLIST_TRAVERSE(&actions, cur, list) { - if (s->session->writeperm & cur->authority || cur->authority == 0) + if (s->session->writeperm & cur->authority || cur->authority == 0) { astman_append(s, "%s: %s (Priv: %s)\r\n", cur->action, cur->synopsis, authority_to_str(cur->authority, &temp)); + } } astman_append(s, "\r\n"); @@ -1726,14 +1834,22 @@ static int action_logoff(struct mansession *s, const struct message *m) static int action_login(struct mansession *s, const struct message *m) { + + /* still authenticated - don't process again */ + if (s->session->authenticated) { + astman_send_ack(s, m, "Already authenticated"); + return 0; + } + if (authenticate(s, m)) { sleep(1); astman_send_error(s, m, "Authentication failed"); return -1; } s->session->authenticated = 1; - if (manager_displayconnects(s->session)) + if (manager_displayconnects(s->session)) { ast_verb(2, "%sManager '%s' logged on from %s\n", (s->session->managerid ? "HTTP " : ""), s->session->username, ast_inet_ntoa(s->session->sin.sin_addr)); + } ast_log(LOG_EVENT, "%sManager '%s' logged on from %s\n", (s->session->managerid ? "HTTP " : ""), s->session->username, ast_inet_ntoa(s->session->sin.sin_addr)); astman_send_ack(s, m, "Authentication accepted"); return 0; @@ -1744,12 +1860,13 @@ static int action_challenge(struct mansession *s, const struct message *m) const char *authtype = astman_get_header(m, "AuthType"); if (!strcasecmp(authtype, "MD5")) { - if (ast_strlen_zero(s->session->challenge)) + if (ast_strlen_zero(s->session->challenge)) { snprintf(s->session->challenge, sizeof(s->session->challenge), "%ld", ast_random()); - ast_mutex_lock(&s->session->__lock); + } + ao2_lock(s); astman_start_ack(s, m); astman_append(s, "Challenge: %s\r\n\r\n", s->session->challenge); - ast_mutex_unlock(&s->session->__lock); + ao2_unlock(s); } else { astman_send_error(s, m, "Must specify AuthType"); } @@ -1826,8 +1943,9 @@ static int action_setvar(struct mansession *s, const struct message *m) pbx_builtin_setvar_helper(c, varname, S_OR(varval, "")); - if (c) + if (c) { ast_channel_unlock(c); + } astman_send_ack(s, m, "Variable Set"); @@ -1877,15 +1995,16 @@ static int action_getvar(struct mansession *s, const struct message *m) pbx_retrieve_variable(c, varname, &varval, workspace, sizeof(workspace), NULL); } - if (c) + if (c) { ast_channel_unlock(c); + } astman_start_ack(s, m); astman_append(s, "Variable: %s\r\nValue: %s\r\n\r\n", varname, varval); return 0; } -static char mandescr_status[] = +static char mandescr_status[] = "Description: Lists channel status along with requested channel vars.\n" "Variables: (Names marked with * are required)\n" " *Channel: Name of the channel to query for status\n" @@ -1893,7 +2012,7 @@ static char mandescr_status[] = " ActionID: Optional ID for this transaction\n" "Will return the status information of each channel along with the\n" "value for the specified channel variables.\n"; - + /*! \brief Manager "status" command to show channels */ /* Needs documentation... */ @@ -1915,14 +2034,15 @@ static int action_status(struct mansession *s, const struct message *m) ); struct ast_str *str = ast_str_create(1000); - if (!ast_strlen_zero(id)) + if (!ast_strlen_zero(id)) { snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id); - else + } else { idText[0] = '\0'; + } - if (all) + if (all) { c = ast_channel_walk_locked(NULL); - else { + } else { c = ast_get_channel_by_name_locked(name); if (!c) { astman_send_error(s, m, "No such channel"); @@ -1958,10 +2078,11 @@ static int action_status(struct mansession *s, const struct message *m) } channels++; - if (c->_bridge) + if (c->_bridge) { snprintf(bridge, sizeof(bridge), "BridgedChannel: %s\r\nBridgedUniqueid: %s\r\n", c->_bridge->name, c->_bridge->uniqueid); - else + } else { bridge[0] = '\0'; + } if (c->pbx) { if (c->cdr) { elapsed_seconds = now.tv_sec - c->cdr->start.tv_sec; @@ -2012,8 +2133,9 @@ static int action_status(struct mansession *s, const struct message *m) ast_state2str(c->_state), bridge, c->uniqueid, ast_str_buffer(str), idText); } ast_channel_unlock(c); - if (!all) + if (!all) { break; + } c = ast_channel_walk_locked(c); } astman_append(s, @@ -2057,12 +2179,13 @@ static int action_sendtext(struct mansession *s, const struct message *m) res = ast_sendtext(c, textmsg); ast_channel_unlock(c); - - if (res > 0) + + if (res > 0) { astman_send_ack(s, m, "Success"); - else + } else { astman_send_error(s, m, "Failure"); - + } + return res; } @@ -2137,18 +2260,23 @@ static int action_redirect(struct mansession *s, const struct message *m) } else { res = -1; } - if (!res) + if (!res) { astman_send_ack(s, m, "Dual Redirect successful"); - else + } else { astman_send_error(s, m, "Secondary redirect failed"); - } else + } + } else { astman_send_ack(s, m, "Redirect successful"); - } else + } + } else { astman_send_error(s, m, "Redirect failed"); - if (chan) + } + if (chan) { ast_channel_unlock(chan); - if (chan2) + } + if (chan2) { ast_channel_unlock(chan2); + } return 0; } @@ -2170,7 +2298,7 @@ static int action_atxfer(struct mansession *s, const struct message *m) struct ast_call_feature *atxfer_feature = NULL; char *feature_code = NULL; - if (ast_strlen_zero(name)) { + if (ast_strlen_zero(name)) { astman_send_error(s, m, "No channel specified"); return 0; } @@ -2271,8 +2399,9 @@ static int action_command(struct mansession *s, const struct message *m) } astman_append(s, "Response: Follows\r\nPrivilege: Command\r\n"); - if (!ast_strlen_zero(id)) + if (!ast_strlen_zero(id)) { astman_append(s, "ActionID: %s\r\n", id); + } /* FIXME: Wedge a ActionID response in here, waiting for later changes */ ast_cli_command(fd, cmd); /* XXX need to change this to use a FILE * */ l = lseek(fd, 0, SEEK_END); /* how many chars available */ @@ -2296,8 +2425,9 @@ static int action_command(struct mansession *s, const struct message *m) close(fd); unlink(template); astman_append(s, "--END COMMAND--\r\n\r\n"); - if (final_buf) + if (final_buf) { ast_free(final_buf); + } return 0; } @@ -2340,8 +2470,9 @@ static void *fast_originate(void *data) in->vars, in->account, &chan); } - if (!chan) - snprintf(requested_channel, AST_CHANNEL_NAME, "%s/%s", in->tech, in->data); + if (!chan) { + snprintf(requested_channel, AST_CHANNEL_NAME, "%s/%s", in->tech, in->data); + } /* Tell the manager what happened with the channel */ manager_event(EVENT_FLAG_CALL, "OriginateResponse", "%s%s" @@ -2353,16 +2484,17 @@ static void *fast_originate(void *data) "Uniqueid: %s\r\n" "CallerIDNum: %s\r\n" "CallerIDName: %s\r\n", - in->idtext, ast_strlen_zero(in->idtext) ? "" : "\r\n", res ? "Failure" : "Success", - chan ? chan->name : requested_channel, in->context, in->exten, reason, + in->idtext, ast_strlen_zero(in->idtext) ? "" : "\r\n", res ? "Failure" : "Success", + chan ? chan->name : requested_channel, in->context, in->exten, reason, chan ? chan->uniqueid : "<null>", S_OR(in->cid_num, "<unknown>"), S_OR(in->cid_name, "<unknown>") ); /* Locked by ast_pbx_outgoing_exten or ast_pbx_outgoing_app */ - if (chan) + if (chan) { ast_channel_unlock(chan); + } ast_free(in); return NULL; } @@ -2434,13 +2566,15 @@ static int action_originate(struct mansession *s, const struct message *m) ast_copy_string(tmp2, callerid, sizeof(tmp2)); ast_callerid_parse(tmp2, &n, &l); if (n) { - if (ast_strlen_zero(n)) + if (ast_strlen_zero(n)) { n = NULL; + } } if (l) { ast_shrink_phone_number(l); - if (ast_strlen_zero(l)) + if (ast_strlen_zero(l)) { l = NULL; + } } if (!ast_strlen_zero(codecs)) { format = 0; @@ -2454,13 +2588,15 @@ static int action_originate(struct mansession *s, const struct message *m) if (!ast_strlen_zero(id)) snprintf(fast->idtext, sizeof(fast->idtext), "ActionID: %s", id); ast_copy_string(fast->tech, tech, sizeof(fast->tech)); - ast_copy_string(fast->data, data, sizeof(fast->data)); + ast_copy_string(fast->data, data, sizeof(fast->data)); ast_copy_string(fast->app, app, sizeof(fast->app)); ast_copy_string(fast->appdata, appdata, sizeof(fast->appdata)); - if (l) + if (l) { ast_copy_string(fast->cid_num, l, sizeof(fast->cid_num)); - if (n) + } + if (n) { ast_copy_string(fast->cid_name, n, sizeof(fast->cid_name)); + } fast->vars = vars; ast_copy_string(fast->context, context, sizeof(fast->context)); ast_copy_string(fast->exten, exten, sizeof(fast->exten)); @@ -2493,17 +2629,18 @@ static int action_originate(struct mansession *s, const struct message *m) } res = ast_pbx_outgoing_app(tech, format, data, to, app, appdata, &reason, 1, l, n, vars, account, NULL); } else { - if (exten && context && pi) + if (exten && context && pi) { res = ast_pbx_outgoing_exten(tech, format, data, to, context, exten, pi, &reason, 1, l, n, vars, account, NULL); - else { + } else { astman_send_error(s, m, "Originate with 'Exten' requires 'Context' and 'Priority'"); return 0; } } - if (!res) + if (!res) { astman_send_ack(s, m, "Originate successfully queued"); - else + } else { astman_send_error(s, m, "Originate failed"); + } return 0; } @@ -2591,8 +2728,9 @@ static int action_extensionstate(struct mansession *s, const struct message *m) astman_send_error(s, m, "Extension not specified"); return 0; } - if (ast_strlen_zero(context)) + if (ast_strlen_zero(context)) { context = "default"; + } status = ast_extension_state(NULL, context, exten); ast_get_hint(hint, sizeof(hint) - 1, NULL, 0, NULL, context, exten); astman_start_ack(s, m); @@ -2649,7 +2787,7 @@ static int process_events(struct mansession *s) { int ret = 0; - ast_mutex_lock(&s->session->__lock); + ao2_lock(s->session); if (s->session->f != NULL) { struct eventqent *eqe; @@ -2664,7 +2802,7 @@ static int process_events(struct mansession *s) s->session->last_ev = unref_event(s->session->last_ev); } } - ast_mutex_unlock(&s->session->__lock); + ao2_unlock(s->session); return ret; } @@ -2704,10 +2842,11 @@ static int action_coresettings(struct mansession *s, const struct message *m) const char *actionid = astman_get_header(m, "ActionID"); char idText[150]; - if (!ast_strlen_zero(actionid)) + if (!ast_strlen_zero(actionid)) { snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid); - else + } else { idText[0] = '\0'; + } astman_append(s, "Response: Success\r\n" "%s" @@ -2718,14 +2857,14 @@ static int action_coresettings(struct mansession *s, const struct message *m) "CoreMaxLoadAvg: %f\r\n" "CoreRunUser: %s\r\n" "CoreRunGroup: %s\r\n" - "CoreMaxFilehandles: %d\r\n" + "CoreMaxFilehandles: %d\r\n" "CoreRealTimeEnabled: %s\r\n" "CoreCDRenabled: %s\r\n" "CoreHTTPenabled: %s\r\n" "\r\n", idText, AMI_VERSION, - ast_get_version(), + ast_get_version(), ast_config_AST_SYSTEM_NAME, option_maxcalls, option_maxload, @@ -2753,10 +2892,11 @@ static int action_corestatus(struct mansession *s, const struct message *m) char reloadtime[150]; struct ast_tm tm; - if (!ast_strlen_zero(actionid)) + if (!ast_strlen_zero(actionid)) { snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid); - else + } else { idText[0] = '\0'; + } ast_localtime(&ast_startuptime, &tm, NULL); ast_strftime(startuptime, sizeof(startuptime), "%H:%M:%S", &tm); @@ -2789,10 +2929,11 @@ static int action_reload(struct mansession *s, const struct message *m) const char *module = astman_get_header(m, "Module"); int res = ast_module_reload(S_OR(module, NULL)); - if (res == 2) + if (res == 2) { astman_send_ack(s, m, "Module Reloaded"); - else + } else { astman_send_error(s, m, s == 0 ? "No such module" : "Module does not support reload"); + } return 0; } @@ -2802,7 +2943,7 @@ static char mandescr_coreshowchannels[] = "Variables:\n" " ActionID: Optional Action id for message matching.\n"; -/*! \brief Manager command "CoreShowChannels" - List currently defined channels +/*! \brief Manager command "CoreShowChannels" - List currently defined channels * and some information about them. */ static int action_coreshowchannels(struct mansession *s, const struct message *m) { @@ -2812,12 +2953,13 @@ static int action_coreshowchannels(struct mansession *s, const struct message *m int numchans = 0; int duration, durh, durm, durs; - if (!ast_strlen_zero(actionid)) + if (!ast_strlen_zero(actionid)) { snprintf(actionidtext, sizeof(actionidtext), "ActionID: %s\r\n", actionid); - else + } else { actionidtext[0] = '\0'; + } - astman_send_listack(s, m, "Channels will follow", "start"); + astman_send_listack(s, m, "Channels will follow", "start"); while ((c = ast_channel_walk_locked(c)) != NULL) { struct ast_channel *bc = ast_bridged_channel(c); @@ -2864,7 +3006,7 @@ static int action_coreshowchannels(struct mansession *s, const struct message *m return 0; } -static char mandescr_modulecheck[] = +static char mandescr_modulecheck[] = "Description: Checks if Asterisk module is loaded\n" "Variables: \n" " ActionID: <id> Action ID for this transaction. Will be returned.\n" @@ -2905,10 +3047,11 @@ static int manager_modulecheck(struct mansession *s, const struct message *m) version = ast_file_version_find(filename); #endif - if (!ast_strlen_zero(id)) + if (!ast_strlen_zero(id)) { snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id); - else + } else { idText[0] = '\0'; + } astman_append(s, "Response: Success\r\n%s", idText); #if !defined(LOW_MEMORY) astman_append(s, "Version: %s\r\n\r\n", version ? version : ""); @@ -2916,7 +3059,7 @@ static int manager_modulecheck(struct mansession *s, const struct message *m) return 0; } -static char mandescr_moduleload[] = +static char mandescr_moduleload[] = "Description: Loads, unloads or reloads an Asterisk module in a running system.\n" "Variables: \n" " ActionID: <id> Action ID for this transaction. Will be returned.\n" @@ -2933,37 +3076,42 @@ static int manager_moduleload(struct mansession *s, const struct message *m) const char *module = astman_get_header(m, "Module"); const char *loadtype = astman_get_header(m, "LoadType"); - if (!loadtype || strlen(loadtype) == 0) + if (!loadtype || strlen(loadtype) == 0) { astman_send_error(s, m, "Incomplete ModuleLoad action."); - if ((!module || strlen(module) == 0) && strcasecmp(loadtype, "reload") != 0) + } + if ((!module || strlen(module) == 0) && strcasecmp(loadtype, "reload") != 0) { astman_send_error(s, m, "Need module name"); + } if (!strcasecmp(loadtype, "load")) { res = ast_load_resource(module); - if (res) + if (res) { astman_send_error(s, m, "Could not load module."); - else + } else { astman_send_ack(s, m, "Module loaded."); + } } else if (!strcasecmp(loadtype, "unload")) { res = ast_unload_resource(module, AST_FORCE_SOFT); - if (res) + if (res) { astman_send_error(s, m, "Could not unload module."); - else + } else { astman_send_ack(s, m, "Module unloaded."); + } } else if (!strcasecmp(loadtype, "reload")) { if (module != NULL) { res = ast_module_reload(module); - if (res == 0) + if (res == 0) { astman_send_error(s, m, "No such module."); - else if (res == 1) + } else if (res == 1) { astman_send_error(s, m, "Module does not support reload action."); - else + } else { astman_send_ack(s, m, "Module reloaded."); + } } else { ast_module_reload(NULL); /* Reload all modules */ astman_send_ack(s, m, "All modules reloaded"); } - } else + } else astman_send_error(s, m, "Incomplete ModuleLoad action."); return 0; } @@ -2987,21 +3135,21 @@ static int process_message(struct mansession *s, const struct message *m) int ret = 0; struct manager_action *tmp; const char *user = astman_get_header(m, "Username"); + int (*call_func)(struct mansession *s, const struct message *m) = NULL; ast_copy_string(action, __astman_get_header(m, "Action", GET_HEADER_SKIP_EMPTY), sizeof(action)); - ast_debug(1, "Manager received command '%s'\n", action); if (ast_strlen_zero(action)) { - ast_mutex_lock(&s->session->__lock); + ao2_lock(s); astman_send_error(s, m, "Missing action in request"); - ast_mutex_unlock(&s->session->__lock); + ao2_unlock(s); return 0; } if (!s->session->authenticated && strcasecmp(action, "Login") && strcasecmp(action, "Logoff") && strcasecmp(action, "Challenge")) { - ast_mutex_lock(&s->session->__lock); + ao2_lock(s); astman_send_error(s, m, "Permission denied"); - ast_mutex_unlock(&s->session->__lock); + ao2_unlock(s); return 0; } @@ -3009,34 +3157,42 @@ static int process_message(struct mansession *s, const struct message *m) (!strcasecmp(action, "Login") || !strcasecmp(action, "Challenge"))) { if (check_manager_session_inuse(user)) { sleep(1); - ast_mutex_lock(&s->session->__lock); + ao2_lock(s); astman_send_error(s, m, "Login Already In Use"); - ast_mutex_unlock(&s->session->__lock); + ao2_unlock(s); return -1; } } AST_RWLIST_RDLOCK(&actions); AST_RWLIST_TRAVERSE(&actions, tmp, list) { - if (strcasecmp(action, tmp->action)) + if (strcasecmp(action, tmp->action)) { continue; - if (s->session->writeperm & tmp->authority || tmp->authority == 0) - ret = tmp->func(s, m); - else + } + if (s->session->writeperm & tmp->authority || tmp->authority == 0) { + call_func = tmp->func; + } else { astman_send_error(s, m, "Permission denied"); + tmp = NULL; + } break; } AST_RWLIST_UNLOCK(&actions); - if (!tmp) { + if (tmp && call_func) { + /* call AMI function after actions list are unlocked */ + ast_debug(1, "Running action '%s'\n", tmp->action); + ret = call_func(s, m); + } else { char buf[512]; snprintf(buf, sizeof(buf), "Invalid/unknown command: %s. Use Action: ListCommands to show available commands.", action); - ast_mutex_lock(&s->session->__lock); + ao2_lock(s); astman_send_error(s, m, buf); - ast_mutex_unlock(&s->session->__lock); + ao2_unlock(s); } - if (ret) + if (ret) { return ret; + } /* Once done with our message, deliver any pending events unless the requester doesn't want them as part of this response. */ @@ -3068,12 +3224,13 @@ static int get_input(struct mansession *s, char *output) */ for (x = 0; x < s->session->inlen; x++) { int cr; /* set if we have \r */ - if (src[x] == '\r' && x+1 < s->session->inlen && src[x+1] == '\n') + if (src[x] == '\r' && x+1 < s->session->inlen && src[x + 1] == '\n') { cr = 2; /* Found. Update length to include \r\n */ - else if (src[x] == '\n') + } else if (src[x] == '\n') { cr = 1; /* also accept \n only */ - else + } else { continue; + } memmove(output, src, x); /*... but trim \r\n */ output[x] = '\0'; /* terminate the string */ x += cr; /* number of bytes used */ @@ -3089,40 +3246,42 @@ static int get_input(struct mansession *s, char *output) res = 0; while (res == 0) { /* XXX do we really need this locking ? */ - ast_mutex_lock(&s->session->__lock); + ao2_lock(s); if (s->session->pending_event) { s->session->pending_event = 0; - ast_mutex_unlock(&s->session->__lock); + ao2_unlock(s); return 0; } s->session->waiting_thread = pthread_self(); - ast_mutex_unlock(&s->session->__lock); + ao2_unlock(s); res = ast_wait_for_input(s->session->fd, -1); /* return 0 on timeout ? */ - ast_mutex_lock(&s->session->__lock); + ao2_lock(s); s->session->waiting_thread = AST_PTHREADT_NULL; - ast_mutex_unlock(&s->session->__lock); + ao2_unlock(s); } if (res < 0) { /* If we get a signal from some other thread (typically because * there are new events queued), return 0 to notify the caller. */ - if (errno == EINTR || errno == EAGAIN) + if (errno == EINTR || errno == EAGAIN) { return 0; + } ast_log(LOG_WARNING, "poll() returned error: %s\n", strerror(errno)); return -1; } - ast_mutex_lock(&s->session->__lock); + + ao2_lock(s); res = fread(src + s->session->inlen, 1, maxlen - s->session->inlen, s->session->f); - if (res < 1) + if (res < 1) { res = -1; /* error return */ - else { + } else { s->session->inlen += res; src[s->session->inlen] = '\0'; res = 0; } - ast_mutex_unlock(&s->session->__lock); + ao2_unlock(s); return res; } @@ -3134,16 +3293,18 @@ static int do_message(struct mansession *s) for (;;) { /* Check if any events are pending and do them if needed */ - if (process_events(s)) + if (process_events(s)) { return -1; + } res = get_input(s, header_buf); if (res == 0) { continue; } else if (res > 0) { - if (ast_strlen_zero(header_buf)) + if (ast_strlen_zero(header_buf)) { return process_message(s, &m) ? -1 : 0; - else if (m.hdrcount < (AST_MAX_MANHEADERS - 1)) + } else if (m.hdrcount < (AST_MAX_MANHEADERS - 1)) { m.headers[m.hdrcount++] = ast_strdupa(header_buf); + } } else { return res; } @@ -3161,55 +3322,52 @@ static int do_message(struct mansession *s) static void *session_do(void *data) { struct ast_tcptls_session_instance *ser = data; - struct mansession_session *session = ast_calloc(1, sizeof(*session)); - struct mansession s = {.session = NULL, }; + struct mansession_session *session = build_mansession(ser->remote_address); + struct mansession s = { NULL, }; int flags; int res; - if (session == NULL) + if (session == NULL) { goto done; - - session->writetimeout = 100; - session->waiting_thread = AST_PTHREADT_NULL; + } + ao2_lock(sessions); flags = fcntl(ser->fd, F_GETFL); - if (!block_sockets) /* make sure socket is non-blocking */ + if (!block_sockets) { /* make sure socket is non-blocking */ flags |= O_NONBLOCK; - else + } else { flags &= ~O_NONBLOCK; + } fcntl(ser->fd, F_SETFL, flags); - ast_mutex_init(&session->__lock); - session->send_events = -1; /* Hook to the tail of the event queue */ session->last_ev = grab_last(); /* these fields duplicate those in the 'ser' structure */ - session->fd = ser->fd; - session->f = ser->f; + session->fd = s.fd = ser->fd; + session->f = s.f = ser->f; session->sin = ser->remote_address; s.session = session; AST_LIST_HEAD_INIT_NOLOCK(&session->datastores); - AST_LIST_LOCK(&sessions); - AST_LIST_INSERT_HEAD(&sessions, session, list); - ast_atomic_fetchadd_int(&num_sessions, 1); - AST_LIST_UNLOCK(&sessions); - + ao2_unlock(sessions); astman_append(&s, "Asterisk Call Manager/%s\r\n", AMI_VERSION); /* welcome prompt */ for (;;) { - if ((res = do_message(&s)) < 0) + if ((res = do_message(&s)) < 0) { break; + } } /* session is over, explain why and terminate */ if (session->authenticated) { - if (manager_displayconnects(session)) + if (manager_displayconnects(session)) { ast_verb(2, "Manager '%s' logged off from %s\n", session->username, ast_inet_ntoa(session->sin.sin_addr)); + } ast_log(LOG_EVENT, "Manager '%s' logged off from %s\n", session->username, ast_inet_ntoa(session->sin.sin_addr)); } else { - if (displayconnects) + if (displayconnects) { ast_verb(2, "Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(session->sin.sin_addr)); + } ast_log(LOG_EVENT, "Failed attempt from %s\n", ast_inet_ntoa(session->sin.sin_addr)); } @@ -3239,23 +3397,24 @@ static void purge_sessions(int n_max) { struct mansession_session *session; time_t now = time(NULL); + struct ao2_iterator i; - AST_LIST_LOCK(&sessions); - AST_LIST_TRAVERSE_SAFE_BEGIN(&sessions, session, list) { + i = ao2_iterator_init(sessions, 0); + while ((session = ao2_iterator_next(&i)) && n_max > 0) { + unref_mansession(session); + ao2_lock(session); if (session->sessiontimeout && (now > session->sessiontimeout) && !session->inuse) { - AST_LIST_REMOVE_CURRENT(list); - ast_atomic_fetchadd_int(&num_sessions, -1); if (session->authenticated && (VERBOSITY_ATLEAST(2)) && manager_displayconnects(session)) { ast_verb(2, "HTTP Manager '%s' timed out from %s\n", session->username, ast_inet_ntoa(session->sin.sin_addr)); } - free_session(session); /* XXX outside ? */ - if (--n_max <= 0) - break; + ao2_unlock(session); + session_destroy(session); + n_max--; + } else { + ao2_unlock(session); } } - AST_LIST_TRAVERSE_SAFE_END; - AST_LIST_UNLOCK(&sessions); } /* @@ -3267,8 +3426,9 @@ static int append_event(const char *str, int category) struct eventqent *tmp = ast_malloc(sizeof(*tmp) + strlen(str)); static int seq; /* sequence number */ - if (!tmp) + if (!tmp) { return -1; + } /* need to init all fields, because ast_malloc() does not */ tmp->usecount = 0; @@ -3299,13 +3459,11 @@ int __manager_event(int category, const char *event, va_list ap; struct timeval now; struct ast_str *buf; + struct ao2_iterator i; - /* Abort if there aren't any manager sessions */ - if (!num_sessions) - return 0; - - if (!(buf = ast_str_thread_get(&manager_event_buf, MANAGER_EVENT_BUF_INITSIZE))) + if (!(buf = ast_str_thread_get(&manager_event_buf, MANAGER_EVENT_BUF_INITSIZE))) { return -1; + } cat_str = authority_to_str(category, &auth); ast_str_set(&buf, 0, @@ -3336,21 +3494,22 @@ int __manager_event(int category, const char *event, append_event(ast_str_buffer(buf), category); /* Wake up any sleeping sessions */ - AST_LIST_LOCK(&sessions); - AST_LIST_TRAVERSE(&sessions, session, list) { - ast_mutex_lock(&session->__lock); - if (session->waiting_thread != AST_PTHREADT_NULL) + i = ao2_iterator_init(sessions, 0); + while ((session = ao2_iterator_next(&i))) { + ao2_lock(session); + if (session->waiting_thread != AST_PTHREADT_NULL) { pthread_kill(session->waiting_thread, SIGURG); - else + } else { /* We have an event to process, but the mansession is * not waiting for it. We still need to indicate that there * is an event waiting so that get_input processes the pending * event instead of polling. */ session->pending_event = 1; - ast_mutex_unlock(&session->__lock); + } + ao2_unlock(session); + unref_mansession(session); } - AST_LIST_UNLOCK(&sessions); AST_RWLIST_RDLOCK(&manager_hooks); AST_RWLIST_TRAVERSE(&manager_hooks, hook, list) { @@ -3419,10 +3578,11 @@ static int ast_manager_register_struct(struct manager_action *act) } } - if (prev) + if (prev) { AST_RWLIST_INSERT_AFTER(&actions, prev, act, list); - else + } else { AST_RWLIST_INSERT_HEAD(&actions, act, list); + } ast_verb(2, "Manager registered action %s\n", act->action); @@ -3437,8 +3597,9 @@ int ast_manager_register2(const char *action, int auth, int (*func)(struct manse { struct manager_action *cur = NULL; - if (!(cur = ast_calloc(1, sizeof(*cur)))) + if (!(cur = ast_calloc(1, sizeof(*cur)))) { return -1; + } cur->action = action; cur->authority = auth; @@ -3488,25 +3649,64 @@ static char *contenttype[] = { static struct mansession_session *find_session(uint32_t ident, int incinuse) { struct mansession_session *session; + struct ao2_iterator i; - if (ident == 0) + if (ident == 0) { return NULL; + } - AST_LIST_LOCK(&sessions); - AST_LIST_TRAVERSE(&sessions, session, list) { - ast_mutex_lock(&session->__lock); + i = ao2_iterator_init(sessions, 0); + while ((session = ao2_iterator_next(&i))) { + ao2_lock(session); if (session->managerid == ident && !session->needdestroy) { ast_atomic_fetchadd_int(&session->inuse, incinuse ? 1 : 0); + unref_mansession(session); break; } - ast_mutex_unlock(&session->__lock); + ao2_unlock(session); + unref_mansession(session); } - AST_LIST_UNLOCK(&sessions); return session; } -int astman_is_authed(uint32_t ident) +/*! + * locate an http session in the list. + * The search keys (nonce) and (username) is value from received + * "Authorization" http header. + * As well as in find_session() function, the value of the nonce can't be zero. + * (0 meansi, that the session used for AMI socket connection). + * Flag (stale) is set, if client used valid, but old, nonce value. + * + */ +static struct mansession_session *find_session_by_nonce(const char *username, unsigned long nonce, int *stale) +{ + struct mansession_session *session; + struct ao2_iterator i; + + if (nonce == 0 || username == NULL || stale == NULL) { + return NULL; + } + + i = ao2_iterator_init(sessions, 0); + while ((session = ao2_iterator_next(&i))) { + ao2_lock(session); + if (!strcasecmp(session->username, username) && session->managerid == nonce) { + *stale = 0; + unref_mansession(session); + break; + } else if (!strcasecmp(session->username, username) && session->oldnonce == nonce) { + *stale = 1; + unref_mansession(session); + break; + } + ao2_unlock(session); + unref_mansession(session); + } + return session; +} + +int astman_is_authed(uint32_t ident) { int authed; struct mansession_session *session; @@ -3516,7 +3716,7 @@ int astman_is_authed(uint32_t ident) authed = (session->authenticated != 0); - ast_mutex_unlock(&session->__lock); + ao2_unlock(session); return authed; } @@ -3525,18 +3725,24 @@ int astman_verify_session_readpermissions(uint32_t ident, int perm) { int result = 0; struct mansession_session *session; + struct ao2_iterator i; - AST_LIST_LOCK(&sessions); - AST_LIST_TRAVERSE(&sessions, session, list) { - ast_mutex_lock(&session->__lock); + if (ident == 0) { + return 0; + } + + i = ao2_iterator_init(sessions, 0); + while ((session = ao2_iterator_next(&i))) { + ao2_lock(session); if ((session->managerid == ident) && (session->readperm & perm)) { result = 1; - ast_mutex_unlock(&session->__lock); + ao2_unlock(session); + unref_mansession(session); break; } - ast_mutex_unlock(&session->__lock); + ao2_unlock(session); + unref_mansession(session); } - AST_LIST_UNLOCK(&sessions); return result; } @@ -3544,18 +3750,24 @@ int astman_verify_session_writepermissions(uint32_t ident, int perm) { int result = 0; struct mansession_session *session; + struct ao2_iterator i; - AST_LIST_LOCK(&sessions); - AST_LIST_TRAVERSE(&sessions, session, list) { - ast_mutex_lock(&session->__lock); + if (ident == 0) { + return 0; + } + + i = ao2_iterator_init(sessions, 0); + while ((session = ao2_iterator_next(&i))) { + ao2_lock(session); if ((session->managerid == ident) && (session->writeperm & perm)) { result = 1; - ast_mutex_unlock(&session->__lock); + ao2_unlock(session); + unref_mansession(session); break; } - ast_mutex_unlock(&session->__lock); + ao2_unlock(session); + unref_mansession(session); } - AST_LIST_UNLOCK(&sessions); return result; } @@ -3577,10 +3789,11 @@ static void xml_copy_escape(struct ast_str **out, const char *src, int mode) ast_str_append(out, 0, "%s", buf); dst = buf; space = sizeof(buf); - if (*src == '\0') + if (*src == '\0') { break; + } } - + if ( (mode & 2) && !isalnum(*src)) { *dst++ = '_'; space--; @@ -3625,29 +3838,11 @@ struct variable_count { int count; }; -static int compress_char(char c) -{ - c &= 0x7f; - if (c < 32) - return 0; - else if (c >= 'a' && c <= 'z') - return c - 64; - else if (c > 'z') - return '_'; - else - return c - 32; -} - static int variable_count_hash_fn(const void *vvc, const int flags) { const struct variable_count *vc = vvc; - int res = 0, i; - for (i = 0; i < 5; i++) { - if (vc->varname[i] == '\0') - break; - res += compress_char(vc->varname[i]) << (i * 6); - } - return res; + + return ast_str_hash(vc->varname); } static int variable_count_cmp_fn(void *obj, void *vstr, int flags) @@ -3676,7 +3871,7 @@ static int variable_count_cmp_fn(void *obj, void *vstr, int flags) * * General: the unformatted text is used as a value of * XML output: to be completed - * + * * \verbatim * Each section is within <response type="object" id="xxx"> * where xxx is taken from ajaxdest variable or defaults to unknown @@ -3689,7 +3884,7 @@ static int variable_count_cmp_fn(void *obj, void *vstr, int flags) * Sections (blank lines in the input) are separated by a <HR> * */ -static void xml_translate(struct ast_str **out, char *in, struct ast_variable *vars, enum output_format format) +static void xml_translate(struct ast_str **out, char *in, struct ast_variable *get_vars, enum output_format format) { struct ast_variable *v; const char *dest = NULL; @@ -3701,30 +3896,41 @@ static void xml_translate(struct ast_str **out, char *in, struct ast_variable *v struct variable_count *vc = NULL; struct ao2_container *vco = NULL; - for (v = vars; v; v = v->next) { - if (!dest && !strcasecmp(v->name, "ajaxdest")) - dest = v->value; - else if (!objtype && !strcasecmp(v->name, "ajaxobjtype")) - objtype = v->value; + if (xml) { + /* dest and objtype need only for XML format */ + for (v = get_vars; v; v = v->next) { + if (!strcasecmp(v->name, "ajaxdest")) { + dest = v->value; + } else if (!strcasecmp(v->name, "ajaxobjtype")) { + objtype = v->value; + } + } + if (ast_strlen_zero(dest)) { + dest = "unknown"; + } + if (ast_strlen_zero(objtype)) { + objtype = "generic"; + } } - if (!dest) - dest = "unknown"; - if (!objtype) - objtype = "generic"; /* we want to stop when we find an empty line */ while (in && *in) { val = strsep(&in, "\r\n"); /* mark start and end of line */ - if (in && *in == '\n') /* remove trailing \n if any */ + if (in && *in == '\n') { /* remove trailing \n if any */ in++; + } ast_trim_blanks(val); ast_debug(5, "inobj %d in_data %d line <%s>\n", inobj, in_data, val); if (ast_strlen_zero(val)) { - if (in_data) { /* close data */ + /* empty line */ + if (in_data) { + /* close data in Opaque mode */ ast_str_append(out, 0, xml ? "'" : "</td></tr>\n"); in_data = 0; } + if (inobj) { + /* close block */ ast_str_append(out, 0, xml ? " /></response>\n" : "<tr><td colspan=\"2\"><hr></td></tr>\r\n"); inobj = 0; @@ -3734,54 +3940,57 @@ static void xml_translate(struct ast_str **out, char *in, struct ast_variable *v continue; } - /* we expect Name: value lines */ - if (in_data) { - var = NULL; - } else { - var = strsep(&val, ":"); - if (val) { /* found the field name */ - val = ast_skip_blanks(val); - ast_trim_blanks(var); - } else { /* field name not found, move to opaque mode */ - val = var; - var = "Opaque-data"; - } - } - if (!inobj) { - if (xml) + /* start new block */ + if (xml) { ast_str_append(out, 0, "<response type='object' id='%s'><%s", dest, objtype); - else - ast_str_append(out, 0, "<body>\n"); + } vco = ao2_container_alloc(37, variable_count_hash_fn, variable_count_cmp_fn); inobj = 1; } - if (!in_data) { /* build appropriate line start */ - ast_str_append(out, 0, xml ? " " : "<tr><td>"); - if ((vc = ao2_find(vco, var, 0))) - vc->count++; - else { - /* Create a new entry for this one */ - vc = ao2_alloc(sizeof(*vc), NULL); - vc->varname = var; - vc->count = 1; - ao2_link(vco, vc); - } - xml_copy_escape(out, var, xml ? 1 | 2 : 0); - if (vc->count > 1) - ast_str_append(out, 0, "-%d", vc->count); - ao2_ref(vc, -1); - ast_str_append(out, 0, xml ? "='" : "</td><td>"); - if (!strcmp(var, "Opaque-data")) - in_data = 1; + if (in_data) { + /* Process data field in Opaque mode */ + xml_copy_escape(out, val, 0); /* data field */ + ast_str_append(out, 0, xml ? "\n" : "<br>\n"); + continue; } + + /* We expect "Name: value" line here */ + var = strsep(&val, ":"); + if (val) { + /* found the field name */ + val = ast_skip_blanks(val); + ast_trim_blanks(var); + } else { + /* field name not found, switch to opaque mode */ + val = var; + var = "Opaque-data"; + in_data = 1; + } + + + ast_str_append(out, 0, xml ? " " : "<tr><td>"); + if ((vc = ao2_find(vco, var, 0))) { + vc->count++; + } else { + /* Create a new entry for this one */ + vc = ao2_alloc(sizeof(*vc), NULL); + vc->varname = var; + vc->count = 1; + ao2_link(vco, vc); + } + + xml_copy_escape(out, var, xml ? 1 | 2 : 0); /* data name */ + if (vc->count > 1) { + ast_str_append(out, 0, "-%d", vc->count); + } + ao2_ref(vc, -1); + ast_str_append(out, 0, xml ? "='" : "</td><td>"); xml_copy_escape(out, val, 0); /* data field */ - if (!in_data) - ast_str_append(out, 0, xml ? "'" : "</td></tr>\n"); - else - ast_str_append(out, 0, xml ? "\n" : "<br>\n"); + ast_str_append(out, 0, xml ? "'" : "</td></tr>\n"); } + if (inobj) { ast_str_append(out, 0, xml ? " /></response>\n" : "<tr><td colspan=\"2\"><hr></td></tr>\r\n"); @@ -3789,43 +3998,55 @@ static void xml_translate(struct ast_str **out, char *in, struct ast_variable *v } } -static struct ast_str *generic_http_callback(enum output_format format, - struct sockaddr_in *remote_address, const char *uri, enum ast_http_method method, - struct ast_variable *params, int *status, - char **title, int *contentlength) +static int generic_http_callback(struct ast_tcptls_session_instance *ser, + enum ast_http_method method, + enum output_format format, + struct sockaddr_in *remote_address, const char *uri, + struct ast_variable *get_params, + struct ast_variable *headers) { struct mansession s = {.session = NULL, }; struct mansession_session *session = NULL; uint32_t ident = 0; int blastaway = 0; - struct ast_variable *v; + struct ast_variable *v, *cookies, *params = get_params; char template[] = "/tmp/ast-http-XXXXXX"; /* template for temporary file */ - struct ast_str *out = NULL; + struct ast_str *http_header = NULL, *out = NULL; struct message m = { 0 }; unsigned int x; size_t hdrlen; - for (v = params; v; v = v->next) { + if (method != AST_HTTP_GET && method != AST_HTTP_HEAD && method != AST_HTTP_POST) { + ast_http_error(ser, 501, "Not Implemented", "Attempt to use unimplemented / unsupported method"); + return -1; + } + + cookies = ast_http_get_cookies(headers); + for (v = cookies; v; v = v->next) { if (!strcasecmp(v->name, "mansession_id")) { sscanf(v->value, "%x", &ident); break; } } + if (cookies) { + ast_variables_destroy(cookies); + } if (!(session = find_session(ident, 1))) { + + /**/ /* Create new session. * While it is not in the list we don't need any locking */ - if (!(session = ast_calloc(1, sizeof(*session)))) { - *status = 500; - goto generic_callback_out; + if (!(session = build_mansession(*remote_address))) { + ast_http_error(ser, 500, "Server Error", "Internal Server Error (out of memory)\n"); + return -1; } + ao2_lock(session); session->sin = *remote_address; session->fd = -1; session->waiting_thread = AST_PTHREADT_NULL; session->send_events = 0; - ast_mutex_init(&session->__lock); - ast_mutex_lock(&session->__lock); session->inuse = 1; /*!\note There is approximately a 1 in 1.8E19 chance that the following * calculation will produce 0, which is an invalid ID, but due to the @@ -3835,24 +4056,35 @@ static struct ast_str *generic_http_callback(enum output_format format, while ((session->managerid = ast_random() ^ (unsigned long) session) == 0); session->last_ev = grab_last(); AST_LIST_HEAD_INIT_NOLOCK(&session->datastores); - AST_LIST_LOCK(&sessions); - AST_LIST_INSERT_HEAD(&sessions, session, list); - ast_atomic_fetchadd_int(&num_sessions, 1); - AST_LIST_UNLOCK(&sessions); } + ao2_unlock(session); - s.session = session; + http_header = ast_str_create(128); + out = ast_str_create(2048); - ast_mutex_unlock(&session->__lock); - - if (!(out = ast_str_create(1024))) { - *status = 500; + if (http_header == NULL || out == NULL) { + ast_http_error(ser, 500, "Server Error", "Internal Server Error (ast_str_create() out of memory)\n"); goto generic_callback_out; } + s.session = session; s.fd = mkstemp(template); /* create a temporary file for command output */ unlink(template); + if (s.fd <= -1) { + ast_http_error(ser, 500, "Server Error", "Internal Server Error (mkstemp failed)\n"); + goto generic_callback_out; + } s.f = fdopen(s.fd, "w+"); + if (!s.f) { + ast_log(LOG_WARNING, "HTTP Manager, fdopen failed: %s!\n", strerror(errno)); + ast_http_error(ser, 500, "Server Error", "Internal Server Error (fdopen failed)\n"); + close(s.fd); + goto generic_callback_out; + } + + if (method == AST_HTTP_POST) { + params = ast_http_get_post_vars(ser, headers); + } for (x = 0, v = params; v && (x < AST_MAX_MANHEADERS); x++, v = v->next) { hdrlen = strlen(v->name) + strlen(v->value) + 3; @@ -3877,14 +4109,13 @@ static struct ast_str *generic_http_callback(enum output_format format, session->needdestroy = 1; } - ast_str_append(&out, 0, - "Content-type: text/%s\r\n" - "Cache-Control: no-cache;\r\n" - "Set-Cookie: mansession_id=\"%08x\"; Version=\"1\"; Max-Age=%d\r\n" - "Pragma: SuppressEvents\r\n" - "\r\n", - contenttype[format], - session->managerid, httptimeout); + ast_str_append(&http_header, 0, + "Content-type: text/%s\r\n" + "Cache-Control: no-cache;\r\n" + "Set-Cookie: mansession_id=\"%08x\"; Version=\"1\"; Max-Age=%d" + "Pragma: SuppressEvents\r\n", + contenttype[format], + session->managerid, httptimeout); if (format == FORMAT_XML) { ast_str_append(&out, 0, "<ajax-response>\n"); @@ -3897,7 +4128,7 @@ static struct ast_str *generic_http_callback(enum output_format format, #define ROW_FMT "<tr><td colspan=\"2\" bgcolor=\"#f1f1ff\">%s</td></tr>\r\n" #define TEST_STRING \ - "<form action=\"manager\">\n\ + "<form action=\"manager\" method=\"post\">\n\ Action: <select name=\"action\">\n\ <option value=\"\">-----></option>\n\ <option value=\"login\">login</option>\n\ @@ -3919,15 +4150,16 @@ static struct ast_str *generic_http_callback(enum output_format format, if (s.f != NULL) { /* have temporary output */ char *buf; size_t l = ftell(s.f); - + if (l) { if (MAP_FAILED == (buf = mmap(NULL, l, PROT_READ | PROT_WRITE, MAP_PRIVATE, s.fd, 0))) { ast_log(LOG_WARNING, "mmap failed. Manager output was not processed\n"); } else { - if (format == FORMAT_XML || format == FORMAT_HTML) + if (format == FORMAT_XML || format == FORMAT_HTML) { xml_translate(&out, buf, params, format); - else + } else { ast_str_append(&out, 0, "%s", buf); + } munmap(buf, l); } } else if (format == FORMAT_XML || format == FORMAT_HTML) { @@ -3940,10 +4172,11 @@ static struct ast_str *generic_http_callback(enum output_format format, if (format == FORMAT_XML) { ast_str_append(&out, 0, "</ajax-response>\n"); - } else if (format == FORMAT_HTML) + } else if (format == FORMAT_HTML) { ast_str_append(&out, 0, "</table></body>\r\n"); + } - ast_mutex_lock(&session->__lock); + ao2_lock(session); /* Reset HTTP timeout. If we're not authenticated, keep it extremely short */ session->sessiontimeout = time(NULL) + ((session->authenticated || httptimeout < 5) ? httptimeout : 5); @@ -3953,42 +4186,381 @@ static struct ast_str *generic_http_callback(enum output_format format, blastaway = 1; } else { ast_debug(1, "Need destroy, but can't do it yet!\n"); - if (session->waiting_thread != AST_PTHREADT_NULL) + if (session->waiting_thread != AST_PTHREADT_NULL) { pthread_kill(session->waiting_thread, SIGURG); + } session->inuse--; } - } else + } else { session->inuse--; - ast_mutex_unlock(&session->__lock); + } + ao2_unlock(session); + + ast_http_send(ser, method, 200, NULL, http_header, out, 0, 0); + http_header = out = NULL; - if (blastaway) - destroy_session(session); generic_callback_out: - if (*status != 200) - return ast_http_error(500, "Server Error", NULL, "Internal Server Error (out of memory)\n"); - return out; + /* Clear resource */ + + if (method == AST_HTTP_POST && params) { + ast_variables_destroy(params); + } + if (http_header) { + ast_free(http_header); + } + if (out) { + ast_free(out); + } + if (session->f) { + fclose(session->f); + } + + if (session != NULL && blastaway) { + session_destroy(session); + } + return 0; } -static struct ast_str *manager_http_callback(struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *params, struct ast_variable *headers, int *status, char **title, int *contentlength) +static int auth_http_callback(struct ast_tcptls_session_instance *ser, + enum ast_http_method method, + enum output_format format, + struct sockaddr_in *remote_address, const char *uri, + struct ast_variable *get_params, + struct ast_variable *headers) { - return generic_http_callback(FORMAT_HTML, &ser->remote_address, uri, method, params, status, title, contentlength); + struct mansession_session *session = NULL; + struct mansession s = { NULL, }; + struct ast_variable *v, *params = get_params; + char template[] = "/tmp/ast-http-XXXXXX"; /* template for temporary file */ + struct ast_str *http_header = NULL, *out = NULL; + size_t result_size = 512; + struct message m = { 0 }; + unsigned int x; + size_t hdrlen; + + time_t time_now = time(NULL); + unsigned long nonce = 0, nc; + struct ast_http_digest d = { NULL, }; + struct ast_manager_user *user = NULL; + int stale = 0; + char resp_hash[256]=""; + /* Cache for user data */ + char u_username[80]; + int u_readperm; + int u_writeperm; + int u_writetimeout; + int u_displayconnects; + + if (method != AST_HTTP_GET && method != AST_HTTP_HEAD && method != AST_HTTP_POST) { + ast_http_error(ser, 501, "Not Implemented", "Attempt to use unimplemented / unsupported method"); + return -1; + } + + /* Find "Authorization: " header */ + for (v = headers; v; v = v->next) { + if (!strcasecmp(v->name, "Authorization")) { + break; + } + } + + if (!v || ast_strlen_zero(v->value)) { + goto out_401; /* Authorization Header not present - send auth request */ + } + + /* Digest found - parse */ + if (ast_string_field_init(&d, 128)) { + ast_http_error(ser, 500, "Server Error", "Internal Server Error (out of memory)\n"); + return -1; + } + + if (ast_parse_digest(v->value, &d, 0, 1)) { + /* Error in Digest - send new one */ + nonce = 0; + goto out_401; + } + if (sscanf(d.nonce, "%lx", &nonce) != 1) { + ast_log(LOG_WARNING, "Received incorrect nonce in Digest <%s>\n", d.nonce); + nonce = 0; + goto out_401; + } + + AST_RWLIST_WRLOCK(&users); + user = get_manager_by_name_locked(d.username); + if(!user) { + AST_RWLIST_UNLOCK(&users); + ast_log(LOG_NOTICE, "%s tried to authenticate with nonexistent user '%s'\n", ast_inet_ntoa(remote_address->sin_addr), d.username); + nonce = 0; + goto out_401; + } + + /* --- We have User for this auth, now check ACL */ + if (user->ha && !ast_apply_ha(user->ha, remote_address)) { + AST_RWLIST_UNLOCK(&users); + ast_log(LOG_NOTICE, "%s failed to pass IP ACL as '%s'\n", ast_inet_ntoa(remote_address->sin_addr), d.username); + ast_http_error(ser, 403, "Permission denied", "Permission denied\n"); + return -1; + } + + /* --- We have auth, so check it */ + + /* compute the expected response to compare with what we received */ + { + char a2[256]; + char a2_hash[256]; + char resp[256]; + + /* XXX Now request method are hardcoded in A2 */ + snprintf(a2, sizeof(a2), "%s:%s", ast_get_http_method(method), d.uri); + ast_md5_hash(a2_hash, a2); + + if (d.qop) { + /* RFC 2617 */ + snprintf(resp, sizeof(resp), "%s:%08lx:%s:%s:auth:%s", user->a1_hash, nonce, d.nc, d.cnonce, a2_hash); + } else { + /* RFC 2069 */ + snprintf(resp, sizeof(resp), "%s:%08lx:%s", user->a1_hash, nonce, a2_hash); + } + ast_md5_hash(resp_hash, resp); + } + + if (!d.nonce || strncasecmp(d.response, resp_hash, strlen(resp_hash))) { + /* Something was wrong, so give the client to try with a new challenge */ + AST_RWLIST_UNLOCK(&users); + nonce = 0; + goto out_401; + } + + /* + * User are pass Digest authentication. + * Now, cache the user data and unlock user list. + */ + ast_copy_string(u_username, user->username, sizeof(u_username)); + u_readperm = user->readperm; + u_writeperm = user->writeperm; + u_displayconnects = user->displayconnects; + u_writetimeout = user->writetimeout; + AST_RWLIST_UNLOCK(&users); + + if (!(session = find_session_by_nonce(d.username, nonce, &stale))) { + /* + * Create new session. + * While it is not in the list we don't need any locking + */ + if (!(session = build_mansession(*remote_address))) { + ast_http_error(ser, 500, "Server Error", "Internal Server Error (out of memory)\n"); + return -1; + } + ao2_lock(session); + + ast_copy_string(session->username, u_username, sizeof(session->username)); + session->managerid = nonce; + session->last_ev = grab_last(); + AST_LIST_HEAD_INIT_NOLOCK(&session->datastores); + + session->readperm = u_readperm; + session->writeperm = u_writeperm; + session->writetimeout = u_writetimeout; + + if (u_displayconnects) { + ast_verb(2, "HTTP Manager '%s' logged in from %s\n", session->username, ast_inet_ntoa(session->sin.sin_addr)); + } + session->noncetime = session->sessionstart = time_now; + session->authenticated = 1; + + ast_log(LOG_EVENT, "HTTP Manager '%s' logged in from %s\n", session->username, ast_inet_ntoa(session->sin.sin_addr)); + + } else if (stale) { + /* + * Session found, but nonce is stale. + * + * This could be because an old request (w/old nonce) arrived. + * + * This may be as the result of http proxy usage (separate delay or + * multipath) or in a situation where a page was refreshed too quickly + * (seen in Firefox). + * + * In this situation, we repeat the 401 auth with the current nonce + * value. + */ + nonce = session->managerid; + ao2_unlock(session); + stale = 1; + goto out_401; + } else { + sscanf(d.nc, "%lx", &nc); + if (session->nc >= nc || ((time_now - session->noncetime) > 62) ) { + /* + * Nonce time expired (> 2 minutes) or something wrong with nonce + * counter. + * + * Create new nonce key and resend Digest auth request. Old nonce + * is saved for stale checking... + */ + session->nc = 0; /* Reset nonce counter */ + session->oldnonce = session->managerid; + nonce = session->managerid = ast_random(); + session->noncetime = time_now; + ao2_unlock(session); + stale = 1; + goto out_401; + } else { + session->nc = nc; /* All OK, save nonce counter */ + } + } + + + /* Reset session timeout. */ + session->sessiontimeout = time(NULL) + (httptimeout > 5 ? httptimeout : 5); + ao2_unlock(session); + + s.session = session; + s.fd = mkstemp(template); /* create a temporary file for command output */ + unlink(template); + if (s.fd <= -1) { + ast_http_error(ser, 500, "Server Error", "Internal Server Error (mkstemp failed)\n"); + goto auth_callback_out; + } + s.f = fdopen(s.fd, "w+"); + if (!s.f) { + ast_log(LOG_WARNING, "HTTP Manager, fdopen failed: %s!\n", strerror(errno)); + ast_http_error(ser, 500, "Server Error", "Internal Server Error (fdopen failed)\n"); + close(s.fd); + goto auth_callback_out; + } + + if (method == AST_HTTP_POST) { + params = ast_http_get_post_vars(ser, headers); + } + + for (x = 0, v = params; v && (x < AST_MAX_MANHEADERS); x++, v = v->next) { + hdrlen = strlen(v->name) + strlen(v->value) + 3; + m.headers[m.hdrcount] = alloca(hdrlen); + snprintf((char *) m.headers[m.hdrcount], hdrlen, "%s: %s", v->name, v->value); + ast_verb(4, "HTTP Manager add header %s\n", m.headers[m.hdrcount]); + m.hdrcount = x + 1; + } + + if (process_message(&s, &m)) { + if (u_displayconnects) { + ast_verb(2, "HTTP Manager '%s' logged off from %s\n", session->username, ast_inet_ntoa(session->sin.sin_addr)); + } + ast_log(LOG_EVENT, "HTTP Manager '%s' logged off from %s\n", session->username, ast_inet_ntoa(session->sin.sin_addr)); + + session->needdestroy = 1; + } + + if (s.f) { + result_size = ftell(s.f); /* Calculate approx. size of result */ + } + + http_header = ast_str_create(80); + out = ast_str_create(result_size * 2 + 512); + + if (http_header == NULL || out == NULL) { + ast_http_error(ser, 500, "Server Error", "Internal Server Error (ast_str_create() out of memory)\n"); + goto auth_callback_out; + } + + ast_str_append(&http_header, 0, "Content-type: text/%s", contenttype[format]); + + if (format == FORMAT_XML) { + ast_str_append(&out, 0, "<ajax-response>\n"); + } else if (format == FORMAT_HTML) { + ast_str_append(&out, 0, + "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n" + "<html><head>\r\n" + "<title>Asterisk™ Manager Interface</title>\r\n" + "</head><body style=\"background-color: #ffffff;\">\r\n" + "<form method=\"POST\">\r\n" + "<table align=\"center\" style=\"background-color: #f1f1f1;\" width=\"500\">\r\n" + "<tr><th colspan=\"2\" style=\"background-color: #f1f1ff;\"><h1>Manager Tester</h1></th></tr>\r\n" + "<tr><th colspan=\"2\" style=\"background-color: #f1f1ff;\">Action: <input name=\"action\" /> Cmd: <input name=\"command\" /><br>" + "<input type=\"submit\" value=\"Send request\" /></th></tr>\r\n"); + } + + if (s.f != NULL) { /* have temporary output */ + char *buf; + size_t l = ftell(s.f); + + if (l) { + if ((buf = mmap(NULL, l, PROT_READ | PROT_WRITE, MAP_SHARED, s.fd, 0))) { + if (format == FORMAT_XML || format == FORMAT_HTML) { + xml_translate(&out, buf, params, format); + } else { + ast_str_append(&out, 0, "%s", buf); + } + munmap(buf, l); + } + } else if (format == FORMAT_XML || format == FORMAT_HTML) { + xml_translate(&out, "", params, format); + } + fclose(s.f); + s.f = NULL; + s.fd = -1; + } + + if (format == FORMAT_XML) { + ast_str_append(&out, 0, "</ajax-response>\n"); + } else if (format == FORMAT_HTML) { + ast_str_append(&out, 0, "</table></form></body></html>\r\n"); + } + + ast_http_send(ser, method, 200, NULL, http_header, out, 0, 0); + http_header = out = NULL; + +auth_callback_out: + /* Clear resources and unlock manager session */ + if (method == AST_HTTP_POST && params) { + ast_variables_destroy(params); + } + + ast_free(http_header); + ast_free(out); + + ao2_lock(session); + if (session->f) { + fclose(session->f); + } + session->f = NULL; + session->fd = -1; + ao2_unlock(session); + + if (session->needdestroy) { + ast_debug(1, "Need destroy, doing it now!\n"); + session_destroy(session); + } + ast_string_field_free_memory(&d); + return 0; + +out_401: + if (!nonce) { + nonce = ast_random(); + } + + ast_http_auth(ser, global_realm, nonce, nonce, stale, NULL); + ast_string_field_free_memory(&d); + return 0; } -static struct ast_str *mxml_http_callback(struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *params, struct ast_variable *headers, int *status, char **title, int *contentlength) +static int manager_http_callback(struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *get_params, struct ast_variable *headers) { - return generic_http_callback(FORMAT_XML, &ser->remote_address, uri, method, params, status, title, contentlength); + return generic_http_callback(ser, method, FORMAT_HTML, &ser->remote_address, uri, get_params, headers); } -static struct ast_str *rawman_http_callback(struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *params, struct ast_variable *headers, int *status, char **title, int *contentlength) +static int mxml_http_callback(struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *get_params, struct ast_variable *headers) { - return generic_http_callback(FORMAT_RAW, &ser->remote_address, uri, method, params, status, title, contentlength); + return generic_http_callback(ser, method, FORMAT_XML, &ser->remote_address, uri, get_params, headers); +} + +static int rawman_http_callback(struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *get_params, struct ast_variable *headers) +{ + return generic_http_callback(ser, method, FORMAT_RAW, &ser->remote_address, uri, get_params, headers); } struct ast_http_uri rawmanuri = { .description = "Raw HTTP Manager Event Interface", .uri = "rawman", .callback = rawman_http_callback, - .supports_get = 1, .data = NULL, .key = __FILE__, }; @@ -3997,7 +4569,6 @@ struct ast_http_uri manageruri = { .description = "HTML Manager Event Interface", .uri = "manager", .callback = manager_http_callback, - .supports_get = 1, .data = NULL, .key = __FILE__, }; @@ -4006,7 +4577,50 @@ struct ast_http_uri managerxmluri = { .description = "XML Manager Event Interface", .uri = "mxml", .callback = mxml_http_callback, - .supports_get = 1, + .data = NULL, + .key = __FILE__, +}; + + +/* Callback with Digest authentication */ +static int auth_manager_http_callback(struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *get_params, struct ast_variable *headers) +{ + return auth_http_callback(ser, method, FORMAT_HTML, &ser->remote_address, uri, get_params, headers); +} + +static int auth_mxml_http_callback(struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *get_params, struct ast_variable *headers) +{ + return auth_http_callback(ser, method, FORMAT_XML, &ser->remote_address, uri, get_params, headers); +} + +static int auth_rawman_http_callback(struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *get_params, struct ast_variable *headers) +{ + return auth_http_callback(ser, method, FORMAT_RAW, &ser->remote_address, uri, get_params, headers); +} + +struct ast_http_uri arawmanuri = { + .description = "Raw HTTP Manager Event Interface w/Digest authentication", + .uri = "arawman", + .has_subtree = 0, + .callback = auth_rawman_http_callback, + .data = NULL, + .key = __FILE__, +}; + +struct ast_http_uri amanageruri = { + .description = "HTML Manager Event Interface w/Digest authentication", + .uri = "amanager", + .has_subtree = 0, + .callback = auth_manager_http_callback, + .data = NULL, + .key = __FILE__, +}; + +struct ast_http_uri amanagerxmluri = { + .description = "XML Manager Event Interface w/Digest authentication", + .uri = "amxml", + .has_subtree = 0, + .callback = auth_mxml_http_callback, .data = NULL, .key = __FILE__, }; @@ -4027,7 +4641,7 @@ struct ast_tls_config ami_tls_cfg; static struct ast_tcptls_session_args ami_desc = { .accept_fd = -1, .master = AST_PTHREADT_NULL, - .tls_cfg = NULL, + .tls_cfg = NULL, .poll_timeout = 5000, /* wake up every 5 seconds */ .periodic_fn = purge_old_stuff, .name = "AMI server", @@ -4038,7 +4652,7 @@ static struct ast_tcptls_session_args ami_desc = { static struct ast_tcptls_session_args amis_desc = { .accept_fd = -1, .master = AST_PTHREADT_NULL, - .tls_cfg = &ami_tls_cfg, + .tls_cfg = &ami_tls_cfg, .poll_timeout = -1, /* the other does the periodic cleanup */ .name = "AMI TLS server", .accept_fn = ast_tcptls_server_root, /* thread doing the accept() */ @@ -4057,6 +4671,8 @@ static int __init_manager(int reload) struct ast_manager_user *user = NULL; struct ast_variable *var; struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 }; + char a1[256]; + char a1_hash[256]; manager_enabled = 0; @@ -4101,8 +4717,9 @@ static int __init_manager(int reload) /* Append placeholder event so master_eventq never runs dry */ append_event("Event: Placeholder\r\n\r\n", 0); } - if ((cfg = ast_config_load2("manager.conf", "manager", config_flags)) == CONFIG_STATUS_FILEUNCHANGED) + if ((cfg = ast_config_load2("manager.conf", "manager", config_flags)) == CONFIG_STATUS_FILEUNCHANGED) { return 0; + } displayconnects = 1; if (!cfg) { @@ -4111,26 +4728,29 @@ static int __init_manager(int reload) } /* default values */ + ast_copy_string(global_realm, S_OR(ast_config_AST_SYSTEM_NAME, DEFAULT_REALM), sizeof(global_realm)); memset(&ami_desc.local_address, 0, sizeof(struct sockaddr_in)); memset(&amis_desc.local_address, 0, sizeof(amis_desc.local_address)); amis_desc.local_address.sin_port = htons(5039); ami_desc.local_address.sin_port = htons(DEFAULT_MANAGER_PORT); ami_tls_cfg.enabled = 0; - if (ami_tls_cfg.certfile) + if (ami_tls_cfg.certfile) { ast_free(ami_tls_cfg.certfile); + } ami_tls_cfg.certfile = ast_strdup(AST_CERTFILE); - if (ami_tls_cfg.cipher) + if (ami_tls_cfg.cipher) { ast_free(ami_tls_cfg.cipher); + } ami_tls_cfg.cipher = ast_strdup(""); for (var = ast_variable_browse(cfg, "general"); var; var = var->next) { val = var->value; - if (!strcasecmp(var->name, "sslenable")) + if (!strcasecmp(var->name, "sslenable")) { ami_tls_cfg.enabled = ast_true(val); - else if (!strcasecmp(var->name, "sslbindport")) + } else if (!strcasecmp(var->name, "sslbindport")) { amis_desc.local_address.sin_port = htons(atoi(val)); - else if (!strcasecmp(var->name, "sslbindaddr")) { + } else if (!strcasecmp(var->name, "sslbindaddr")) { if ((hp = ast_gethostbyname(val, &ahp))) { memcpy(&amis_desc.local_address.sin_addr, hp->h_addr, sizeof(amis_desc.local_address.sin_addr)); have_sslbindaddr = 1; @@ -4156,7 +4776,7 @@ static int __init_manager(int reload) ast_log(LOG_WARNING, "Invalid address '%s' specified, using 0.0.0.0\n", val); memset(&ami_desc.local_address.sin_addr, 0, sizeof(ami_desc.local_address.sin_addr)); } - } else if (!strcasecmp(var->name, "allowmultiplelogin")) { + } else if (!strcasecmp(var->name, "allowmultiplelogin")) { allowmultiplelogin = ast_true(val); } else if (!strcasecmp(var->name, "displayconnects")) { displayconnects = ast_true(val); @@ -4169,17 +4789,19 @@ static int __init_manager(int reload) } else { ast_log(LOG_NOTICE, "Invalid keyword <%s> = <%s> in manager.conf [general]\n", var->name, val); - } + } } - if (manager_enabled) + if (manager_enabled) { ami_desc.local_address.sin_family = AF_INET; - if (!have_sslbindaddr) + } + if (!have_sslbindaddr) { amis_desc.local_address.sin_addr = ami_desc.local_address.sin_addr; - if (ami_tls_cfg.enabled) + } + if (ami_tls_cfg.enabled) { amis_desc.local_address.sin_family = AF_INET; + } - AST_RWLIST_WRLOCK(&users); /* First, get users from users.conf */ @@ -4189,9 +4811,10 @@ static int __init_manager(int reload) int genhasmanager = ast_true(ast_variable_retrieve(ucfg, "general", "hasmanager")); while ((cat = ast_category_browse(ucfg, cat))) { - if (!strcasecmp(cat, "general")) + if (!strcasecmp(cat, "general")) { continue; - + } + hasmanager = ast_variable_retrieve(ucfg, cat, "hasmanager"); if ((!hasmanager && genhasmanager) || ast_true(hasmanager)) { const char *user_secret = ast_variable_retrieve(ucfg, cat, "secret"); @@ -4199,13 +4822,14 @@ static int __init_manager(int reload) const char *user_write = ast_variable_retrieve(ucfg, cat, "write"); const char *user_displayconnects = ast_variable_retrieve(ucfg, cat, "displayconnects"); const char *user_writetimeout = ast_variable_retrieve(ucfg, cat, "writetimeout"); - + /* Look for an existing entry, * if none found - create one and add it to the list */ if (!(user = get_manager_by_name_locked(cat))) { - if (!(user = ast_calloc(1, sizeof(*user)))) + if (!(user = ast_calloc(1, sizeof(*user)))) { break; + } /* Copy name over */ ast_copy_string(user->username, cat, sizeof(user->username)); @@ -4220,36 +4844,45 @@ static int __init_manager(int reload) user->writetimeout = 100; } - if (!user_secret) + if (!user_secret) { user_secret = ast_variable_retrieve(ucfg, "general", "secret"); - if (!user_read) + } + if (!user_read) { user_read = ast_variable_retrieve(ucfg, "general", "read"); - if (!user_write) + } + if (!user_write) { user_write = ast_variable_retrieve(ucfg, "general", "write"); - if (!user_displayconnects) + } + if (!user_displayconnects) { user_displayconnects = ast_variable_retrieve(ucfg, "general", "displayconnects"); - if (!user_writetimeout) + } + if (!user_writetimeout) { user_writetimeout = ast_variable_retrieve(ucfg, "general", "writetimeout"); + } if (!ast_strlen_zero(user_secret)) { - if (user->secret) + if (user->secret) { ast_free(user->secret); + } user->secret = ast_strdup(user_secret); } - if (user_read) + if (user_read) { user->readperm = get_perm(user_read); - if (user_write) + } + if (user_write) { user->writeperm = get_perm(user_write); - if (user_displayconnects) + } + if (user_displayconnects) { user->displayconnects = ast_true(user_displayconnects); - + } if (user_writetimeout) { int value = atoi(user_writetimeout); - if (value < 100) + if (value < 100) { ast_log(LOG_WARNING, "Invalid writetimeout value '%s' at users.conf line %d\n", var->value, var->lineno); - else + } else { user->writetimeout = value; + } } } } @@ -4261,13 +4894,15 @@ static int __init_manager(int reload) while ((cat = ast_category_browse(cfg, cat))) { struct ast_ha *oldha; - if (!strcasecmp(cat, "general")) + if (!strcasecmp(cat, "general")) { continue; + } /* Look for an existing entry, if none found - create one and add it to the list */ if (!(user = get_manager_by_name_locked(cat))) { - if (!(user = ast_calloc(1, sizeof(*user)))) + if (!(user = ast_calloc(1, sizeof(*user)))) { break; + } /* Copy name over */ ast_copy_string(user->username, cat, sizeof(user->username)); @@ -4290,8 +4925,9 @@ static int __init_manager(int reload) var = ast_variable_browse(cfg, cat); for (; var; var = var->next) { if (!strcasecmp(var->name, "secret")) { - if (user->secret) + if (user->secret) { ast_free(user->secret); + } user->secret = ast_strdup(var->value); } else if (!strcasecmp(var->name, "deny") || !strcasecmp(var->name, "permit")) { @@ -4304,12 +4940,14 @@ static int __init_manager(int reload) user->displayconnects = ast_true(var->value); } else if (!strcasecmp(var->name, "writetimeout")) { int value = atoi(var->value); - if (value < 100) + if (value < 100) { ast_log(LOG_WARNING, "Invalid writetimeout value '%s' at line %d\n", var->value, var->lineno); - else + } else { user->writetimeout = value; - } else + } + } else { ast_debug(1, "%s is an unknown option.\n", var->name); + } } ast_free_ha(oldha); } @@ -4319,13 +4957,25 @@ static int __init_manager(int reload) AST_RWLIST_TRAVERSE_SAFE_BEGIN(&users, user, list) { if (user->keep) { /* valid record. clear flag for the next round */ user->keep = 0; + + /* Calculate A1 for Digest auth */ + snprintf(a1, sizeof(a1), "%s:%s:%s", user->username, global_realm, user->secret); + ast_md5_hash(a1_hash,a1); + if (user->a1_hash) { + ast_free(user->a1_hash); + } + user->a1_hash = ast_strdup(a1_hash); continue; } /* We do not need to keep this user so take them out of the list */ AST_RWLIST_REMOVE_CURRENT(list); /* Free their memory now */ - if (user->secret) + if (user->a1_hash) { + ast_free(user->a1_hash); + } + if (user->secret) { ast_free(user->secret); + } ast_free_ha(user->ha); ast_free(user); } @@ -4333,11 +4983,21 @@ static int __init_manager(int reload) AST_RWLIST_UNLOCK(&users); + if (!reload) { + /* If you have a NULL hash fn, you only need a single bucket */ + sessions = ao2_container_alloc(1, NULL, mansession_cmp_fn); + } + if (webmanager_enabled && manager_enabled) { if (!webregged) { + ast_http_uri_link(&rawmanuri); ast_http_uri_link(&manageruri); ast_http_uri_link(&managerxmluri); + + ast_http_uri_link(&arawmanuri); + ast_http_uri_link(&amanageruri); + ast_http_uri_link(&amanagerxmluri); webregged = 1; } } else { @@ -4345,18 +5005,24 @@ static int __init_manager(int reload) ast_http_uri_unlink(&rawmanuri); ast_http_uri_unlink(&manageruri); ast_http_uri_unlink(&managerxmluri); + + ast_http_uri_unlink(&arawmanuri); + ast_http_uri_unlink(&amanageruri); + ast_http_uri_unlink(&amanagerxmluri); webregged = 0; } } - if (newhttptimeout > 0) + if (newhttptimeout > 0) { httptimeout = newhttptimeout; + } manager_event(EVENT_FLAG_SYSTEM, "Reload", "Module: Manager\r\nStatus: %s\r\nMessage: Manager reload Requested\r\n", manager_enabled ? "Enabled" : "Disabled"); ast_tcptls_server_start(&ami_desc); - if (ast_ssl_setup(amis_desc.tls_cfg)) + if (ast_ssl_setup(amis_desc.tls_cfg)) { ast_tcptls_server_start(&amis_desc); + } return 0; } @@ -4385,7 +5051,7 @@ int astman_datastore_remove(struct mansession *s, struct ast_datastore *datastor struct ast_datastore *astman_datastore_find(struct mansession *s, const struct ast_datastore_info *info, const char *uid) { struct ast_datastore *datastore = NULL; - + if (info == NULL) return NULL; |