#include <sys/time.h>
#include <signal.h>
#include <sys/mman.h>
+#include <sys/types.h>
+#include <regex.h>
#include "asterisk/channel.h"
#include "asterisk/file.h"
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 */
+ struct ao2_container *whitefilters;
+ struct ao2_container *blackfilters;
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 */
int writetimeout; /*! Per user Timeout for ast_carefulwrite() */
int displayconnects; /*!< XXX unused */
int keep; /*!< mark entries created on a reload */
+ struct ao2_container *whitefilters;
+ struct ao2_container *blackfilters;
char *a1_hash; /*!< precalculated A1 for Digest auth */
AST_RWLIST_ENTRY(ast_manager_user) list;
};
return s;
}
+static void event_filter_destructor(void *obj)
+{
+ regex_t *regex_filter = obj;
+ regfree(regex_filter);
+}
+
static void session_destructor(void *obj)
{
struct mansession_session *session = obj;
if (eqe) {
ast_atomic_fetchadd_int(&eqe->usecount, -1);
}
+
+ ao2_t_callback(session->whitefilters, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL, "unlink all white filters");
+ ao2_t_callback(session->blackfilters, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL, "unlink all black filters");
+ ao2_t_ref(session->whitefilters, -1 , "decrement ref for white container, should be last one");
+ ao2_t_ref(session->blackfilters, -1 , "decrement ref for black container, should be last one");
}
/*! \brief Allocate manager session structure and add it to the list of sessions */
const char *password = astman_get_header(m, "Secret");
int error = -1;
struct ast_manager_user *user = NULL;
+ regex_t *regex_filter;
+ struct ao2_iterator filter_iter;
if (ast_strlen_zero(username)) { /* missing username */
return -1;
/* 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;
+ s->session->whitefilters = ao2_container_alloc(1, NULL, NULL);
+ s->session->blackfilters = ao2_container_alloc(1, NULL, NULL);
+ if (s->session->whitefilters && s->session->blackfilters) {
+ 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_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_iterator_destroy(&filter_iter);
+ } else {
+ ast_log(LOG_WARNING, "Allocation for filters failed, no filtering will occur.\n");
+ }
s->session->sessionstart = time(NULL);
s->session->sessionstart_tv = ast_tvnow();
set_eventmask(s, astman_get_header(m, "Events"));
return 0;
}
+static int whitefilter_cmp_fn(void *obj, void *arg, void *data, int flags)
+{
+ regex_t *regex_filter = obj;
+ const char *eventdata = arg;
+ int *result = data;
+
+ if (!regexec(regex_filter, eventdata, 0, NULL, 0)) {
+ *result = 1;
+ return (CMP_MATCH | CMP_STOP);
+ }
+
+ return 0;
+}
+
+static int blackfilter_cmp_fn(void *obj, void *arg, void *data, int flags)
+{
+ regex_t *regex_filter = obj;
+ const char *eventdata = arg;
+ int *result = data;
+
+ if (regexec(regex_filter, eventdata, 0, NULL, 0)) {
+ *result = 1;
+ return (CMP_MATCH | CMP_STOP);
+ }
+
+ return 0;
+}
+
+static int match_filter(struct mansession *s, char *eventdata)
+{
+ int result = 0;
+
+ ast_debug(3, "Examining event:\n%s\n", eventdata);
+ if (!ao2_container_count(s->session->whitefilters) && !ao2_container_count(s->session->blackfilters)) {
+ return 1; /* no filtering means match all */
+ } else if (ao2_container_count(s->session->whitefilters) && !ao2_container_count(s->session->blackfilters)) {
+ /* white filters only: implied black all filter processed first, then white filters */
+ ao2_t_callback_data(s->session->whitefilters, OBJ_NODATA, whitefilter_cmp_fn, eventdata, &result, "find filter in session filter container");
+ } else if (!ao2_container_count(s->session->whitefilters) && ao2_container_count(s->session->blackfilters)) {
+ /* black filters only: implied white all filter processed first, then black filters */
+ ao2_t_callback_data(s->session->blackfilters, OBJ_NODATA, blackfilter_cmp_fn, eventdata, &result, "find filter in session filter container");
+ } else {
+ /* white and black filters: implied black all filter processed first, then white filters, and lastly black filters */
+ ao2_t_callback_data(s->session->whitefilters, OBJ_NODATA, whitefilter_cmp_fn, eventdata, &result, "find filter in session filter container");
+ if (result) {
+ result = 0;
+ ao2_t_callback_data(s->session->blackfilters, OBJ_NODATA, blackfilter_cmp_fn, eventdata, &result, "find filter in session filter container");
+ }
+ }
+
+ return result;
+}
+
/*!
* Send any applicable events to the client listening on this socket.
* Wait only for a finite time on each event, and drop all events whether
if (!ret && s->session->authenticated &&
(s->session->readperm & eqe->category) == eqe->category &&
(s->session->send_events & eqe->category) == eqe->category) {
- if (send_string(s, eqe->eventdata) < 0) {
- ret = -1; /* don't send more */
- }
+ if (match_filter(s, eqe->eventdata)) {
+ if (send_string(s, eqe->eventdata) < 0)
+ ret = -1; /* don't send more */
+ }
}
s->session->last_ev = eqe;
}
/* Default displayconnect from [general] */
user->displayconnects = displayconnects;
user->writetimeout = 100;
+ user->whitefilters = ao2_container_alloc(1, NULL, NULL);
+ user->blackfilters = ao2_container_alloc(1, NULL, NULL);
/* Insert into list */
AST_RWLIST_INSERT_TAIL(&users, user, list);
+ } else {
+ ao2_t_callback(user->whitefilters, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL, "unlink all white filters");
+ ao2_t_callback(user->blackfilters, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL, "unlink all black filters");
}
/* Make sure we keep this user and don't destroy it during cleanup */
} else {
user->writetimeout = value;
}
+ } else if (!strcasecmp(var->name, "eventfilter")) {
+ const char *value = var->value;
+ regex_t *new_filter = ao2_t_alloc(sizeof(*new_filter), event_filter_destructor, "event_filter allocation");
+ if (new_filter) {
+ int is_blackfilter;
+ if (value[0] == '!') {
+ is_blackfilter = 1;
+ value++;
+ } else {
+ is_blackfilter = 0;
+ }
+ if (regcomp(new_filter, value, 0)) {
+ ao2_t_ref(new_filter, -1, "failed to make regx");
+ } else {
+ if (is_blackfilter) {
+ ao2_t_link(user->blackfilters, new_filter, "link new filter into black user container");
+ } else {
+ ao2_t_link(user->whitefilters, new_filter, "link new filter into white user container");
+ }
+ }
+ }
} else {
ast_debug(1, "%s is an unknown option.\n", var->name);
}
}
/* We do not need to keep this user so take them out of the list */
AST_RWLIST_REMOVE_CURRENT(list);
+ ast_debug(4, "Pruning user '%s'\n", user->username);
/* Free their memory now */
if (user->a1_hash) {
ast_free(user->a1_hash);
if (user->secret) {
ast_free(user->secret);
}
+ ao2_t_callback(user->whitefilters, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL, "unlink all white filters");
+ ao2_t_callback(user->blackfilters, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL, "unlink all black filters");
+ ao2_t_ref(user->whitefilters, -1, "decrement ref for white container, should be last one");
+ ao2_t_ref(user->blackfilters, -1, "decrement ref for black container, should be last one");
ast_free_ha(user->ha);
ast_free(user);
}