Index: include/asterisk/utils.h
===================================================================
--- include/asterisk/utils.h	(revision 148468)
+++ include/asterisk/utils.h	(working copy)
@@ -642,6 +642,31 @@
 
 #define ARRAY_LEN(a) (sizeof(a) / sizeof(a[0]))
 
+
+/* Definition for Digest authorization */
+struct ast_http_digest {
+	char username[80];
+	char nonce[32];
+	char uri[256];
+	char realm[256];
+	char domain[256];
+	char response[256];
+	int qop;		/* Flag set to 1, if we send/recv qop="quth" */
+	char cnonce[256];
+	char opaque[32];
+	char nc[32];
+};
+
+/*!
+ *\brief Parse digest authorization header.
+ *\return Returns -1 if we have no auth or something wrong with digest.
+ *\note This function may be used for Digest request and responce header.
+ * request arg is set to nonzero, if we parse Digest Request.
+ * pedantic arg can be set to nonzero if we need to do addition Digest check.
+ */
+int ast_parse_digest(const char *digest, struct ast_http_digest *d, int request, int pedantic);
+
+
 #ifdef AST_DEVMODE
 #define ast_assert(a) _ast_assert(a, # a, __FILE__, __LINE__, __PRETTY_FUNCTION__)
 static void force_inline _ast_assert(int condition, const char *condition_str, 
Index: include/asterisk/http.h
===================================================================
--- include/asterisk/http.h	(revision 148468)
+++ include/asterisk/http.h	(working copy)
@@ -52,28 +52,40 @@
  * be run earlier in the startup process so modules have it available.
  */
 
+/*! \brief HTTP Request methods known by Asterisk */
+enum ast_http_method {
+	AST_HTTP_UNKNOWN = 0,	/*!< Unknown response */
+	AST_HTTP_GET,
+	AST_HTTP_POST,
+	AST_HTTP_HEAD,
+	AST_HTTP_PUT		/*!< Not supported in Asterisk */
+};
 
+struct ast_http_uri;
+
 /*! \brief HTTP Callbacks take the socket
 
-   \note The method and the path as arguments and should
-   return the content, allocated with malloc().  Status should be changed to reflect
-   the status of the request if it isn't 200 and title may be set to a malloc()'d string
-   to an appropriate title for non-200 responses.  Content length may also be specified. 
-\verbatim   
-   The return value may include additional headers at the front and MUST include a blank 
-   line with \r\n to provide separation between user headers and content (even if no
-   content is specified) 
+   \note The callback function get server instance, uri, http method,
+   get method variable (if present in uri) and http headers as arguments
+   and should ast_http_send() function for sending content allocated
+   with malloc() and/or content from opeened file descriptor.
+   
+   Status and status text should be put as argument in ast_http_send()
+   function to reflect the status of the request. (200 or 304, for example).
+   Content length calculated by ast_http_send() function internality. 
+
+   Static content indicated in argument passed to ast_http_send() function.
+
+\verbatim
+   All addition http response headers must be send as separate malloc'ated
+   string, and must be not existed in responce content! No addition line
+   with \r\n are required.
 \endverbatim
+
+   For error responce can be used ast_http_error() function.
 */
+typedef int (*ast_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);
 
-enum ast_http_method {
-	AST_HTTP_GET = 0,
-	AST_HTTP_POST,
-};
-struct ast_http_uri;
-
-typedef struct ast_str *(*ast_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);
-
 /*! \brief Definition of a URI handler */
 struct ast_http_uri {
 	AST_LIST_ENTRY(ast_http_uri) entry;
@@ -81,12 +93,6 @@
 	const char *uri;
 	ast_http_callback callback;
 	unsigned int has_subtree:1;
-	/*! This handler serves static content */
-	unsigned int static_content:1;
-	/*! This handler accepts GET requests */
-	unsigned int supports_get:1;
-	/*! This handler accepts POST requests */
-	unsigned int supports_post:1;
 	/*! Structure is malloc'd */
 	unsigned int mallocd:1;
 	/*! Data structure is malloc'd */
@@ -97,6 +103,9 @@
 	const char *key;
 };
 
+/* Get cookie from Request headers */
+struct ast_variable *ast_http_get_cookies(struct ast_variable *headers);
+
 /*! \brief Register a URI handler */
 int ast_http_uri_link(struct ast_http_uri *urihandler);
 
@@ -106,10 +115,48 @@
 /*! \brief Unregister all handlers with matching key */
 void ast_http_uri_unlink_all_with_key(const char *key);
 
-/*! \brief Return an ast_str malloc()'d string containing an HTTP error message */
-struct ast_str *ast_http_error(int status, const char *title, const char *extra_header, const char *text);
+/*! \brief Return http method name string */
+char *ast_get_http_method(enum ast_http_method method);
 
+/*! \brief Return mime type based on extension */
+char *ast_ftype2mtype(const char *ftype);
+
+/*! \brief Return manager id, if exist, from request headers */
+uint32_t manid_from_vars(struct ast_variable *headers);
+
+/*! \brief Generic function for sending http/1.1 responce.
+  \note Function make http responce header from status_code,
+  status_header and http_header.
+
+  Extra http header MUST BE only present in http_header argument.
+  Argument "out" contain only content of response (no headers!).
+
+  Http content can be constructed from mallocated argument "out", if
+  it is not NULL.
+  And, if fd is not null, from opened (for reading) file.
+
+  This function calculate content-lenght http header itself.
+ 
+  if http_header or out argument have not NULL value, this function
+  free memory, mallocated by caller and close http socket before exit.
+  
+  This function don't close file, identifide in fd variable.
+ */
+void ast_http_send(struct ast_tcptls_session_instance *ser, enum ast_http_method method, int status_code, const char *status_title, struct ast_str *http_header, struct ast_str *out, const int fd, unsigned int static_content);
+
+/* \brief Send http "401 Unauthorized" responce and close socket*/
+void ast_http_auth(struct ast_tcptls_session_instance *ser, const char *realm, const unsigned long nonce, const unsigned long opaque, int stale, const char *text);
+
+/* \brief Send HTTP error message and close socket */
+void ast_http_error(struct ast_tcptls_session_instance *ser, int status, const char *title, const char *text);
+
 /*! \brief Return the current prefix */
 void ast_http_prefix(char *buf, int len);
 
+
+/* \brief get post variables from client Request Entity-Body, if content type
+ is application/x-www-form-urlencoded. */
+struct ast_variable *get_post_vars(struct ast_tcptls_session_instance *ser, struct ast_variable *headers);
+
+
 #endif /* _ASTERISK_SRV_H */
Index: main/utils.c
===================================================================
--- main/utils.c	(revision 148468)
+++ main/utils.c	(working copy)
@@ -1760,6 +1760,115 @@
 	return 0;
 }
 
+
+/*!
+ *\brief Parse digest authorization header.
+ *\return Returns -1 if we have no auth or something wrong with digest.
+ *\note	This function may be used for Digest request and responce header.
+ * request arg is set to nonzero, if we parse Digest Request.
+ * pedantic arg can be set to nonzero if we need to do addition Digest check.
+ */
+int ast_parse_digest(const char *digest, struct ast_http_digest *d, int request, int pedantic) {
+	int i;
+	char *c, key[512], val[512], tmp[512];
+
+	if(ast_strlen_zero(digest) || d == NULL)
+		return -1;
+
+	ast_copy_string(tmp, digest, sizeof(tmp));
+
+	c = ast_skip_blanks(tmp);
+	
+	if (strncasecmp(tmp, "Digest ", strlen("Digest "))) {
+		ast_log(LOG_WARNING, "Missing Digest.\n");
+		return -1;
+	}
+	c += strlen("Digest ");
+
+	/* clear digest data */
+	memset(d, 0, sizeof(struct ast_http_digest));
+
+	/* lookup for keys/value pair */
+        while (*c && *(c = ast_skip_blanks(c))) {
+
+		/* find key */
+		i = 0;
+		while (*c && *c != '=' && *c != ',' && !isspace(*c) )
+			key[i++] = *c++;
+		key[i] = '\0';
+		c = ast_skip_blanks(c);
+		if (*c == '=') {
+			c = ast_skip_blanks(++c);
+			i = 0;
+			if (*c == '\"') {
+				/* in quotes. Skip first and look for last */
+				c++;
+				while (*c && *c != '\"') {
+					if (*c == '\\' && c[1] != '\0') /* unescape chars */
+						c++;
+					val[i++] = *c++;
+				}
+			} else {
+				/* token */
+				while (*c && *c != ',' && !isspace(*c) )
+					val[i++] = *c++;
+			}
+			val[i] = '\0';
+		}
+
+		while (*c && *c != ',')
+			c++;
+		if (*c)
+			c++;
+
+		if (!strcasecmp(key, "username"))
+			strncpy(d->username, val, sizeof(d->username)-1);
+		else if (!strcasecmp(key, "realm"))
+			strncpy(d->realm, val, sizeof(d->realm)-1);
+		else if (!strcasecmp(key, "nonce"))
+			strncpy(d->nonce, val, sizeof(d->nonce)-1);
+		else if (!strcasecmp(key, "uri"))
+			strncpy(d->uri, val, sizeof(d->uri)-1);
+		else if (!strcasecmp(key, "domain"))
+			strncpy(d->domain, val, sizeof(d->domain)-1);
+		else if (!strcasecmp(key, "response"))
+			strncpy(d->response, val, sizeof(d->response)-1);
+		else if (!strcasecmp(key, "algorithm")) {
+			if (strcasecmp(val, "MD5")) {
+				ast_log(LOG_WARNING, "Digest algorithm: \"%s\" not supported.\n", val);
+				return -1;
+			}
+		} else if (!strcasecmp(key, "cnonce"))
+			strncpy(d->cnonce, val, sizeof(d->cnonce)-1);
+		else if (!strcasecmp(key, "opaque"))
+			strncpy(d->opaque, val, sizeof(d->opaque)-1);
+		else if (!strcasecmp(key, "qop") && !strcasecmp(val, "auth"))
+			d->qop = 1;
+		else if (!strcasecmp(key, "nc")) {
+			unsigned long u;
+			if (sscanf(val, "%lx", &u) != 1) {
+				ast_log(LOG_WARNING, "Incorrect Digest nc value: \"%s\".\n", val);
+				return -1;
+			}
+			strncpy(d->nc, val, sizeof(d->nc)-1);
+		}
+	}
+	/* Digest checkout */
+	if (!*d->realm || !*d->nonce)
+		return -1; /* "realm" and "nonce" MUST be always exist */
+
+	if (!request) {
+		/* Additional check for Digest response */
+		if (!*d->username || !*d->uri || !*d->response)
+			return -1;
+		
+		if(pedantic && d->qop && (!*d->cnonce || !*d->nc))
+			return -1;
+	}
+	
+	return 0;
+}
+
 #ifndef __AST_DEBUG_MALLOC
 int _ast_asprintf(char **ret, const char *file, int lineno, const char *func, const char *fmt, ...)
 {
Index: main/manager.c
===================================================================
--- main/manager.c	(revision 148468)
+++ main/manager.c	(working copy)
@@ -125,7 +125,11 @@
 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;
+#define MAX_SESSIONS		256		/*!< Maximum active sessions */
 static int num_sessions;
 
 static int manager_debug;	/*!< enable some debugging code in the manager */
@@ -137,7 +141,8 @@
  * 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 {
@@ -149,7 +154,6 @@
 
 struct mansession {
 	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 */
@@ -172,14 +176,18 @@
 	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) list;
 };
 
 #define NEW_EVENT(m)	(AST_LIST_NEXT(m->last_ev, eq_next))
 
-static AST_LIST_HEAD_STATIC(sessions, mansession);
+static struct ao2_container *sessions;
 
+
 /*! \brief user descriptor, as read from the config file.
  *
  * \note It is still missing some fields -- e.g. we can have multiple permit and deny
@@ -193,8 +201,9 @@
 	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;
 };
 
@@ -291,8 +300,9 @@
 	/* 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;
 }
@@ -353,8 +363,9 @@
 		}
 	}
 
-	if ((*res)->used == 0)	/* replace empty string with something sensible */
+	if ((*res)->used == 0) { /* replace empty string with something sensible */
 		ast_str_append(res, 0, "<none>");
+	}
 
 	return (*res)->str;
 }
@@ -370,12 +381,14 @@
 
 	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;
@@ -385,12 +398,14 @@
 {
 	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;
@@ -404,36 +419,112 @@
 {
 	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);
 }
 
+/*
+ * 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);
+	return AST_LIST_NEXT(e, eq_next);
+}
+
+static void ref_event(struct eventqent *e)
+{
+	ast_atomic_fetchadd_int(&e->usecount, 1);
+}
+
+/*! \brief Unreference manager session object.
+     If no more references, then go ahead and delete it */
+static struct mansession *unref_mansession(struct mansession *s)
+{
+	int refcount = ao2_ref(s, -1);
+        if (manager_debug) {
+		ast_log(LOG_DEBUG, "Mansession: %p refcount now %d\n", s, refcount - 1);
+	}
+	return s;
+}
+
+static void session_destructor(void *obj)
+{
+	struct mansession *s = obj;
+	struct eventqent *eqe = s->last_ev;
+
+	if (s->f != NULL) {
+		fclose(s->f);
+	}
+	unref_event(eqe);
+}
+
+/*! \brief Allocate manager session structure and add it to the list of sessions */
+static struct mansession *build_mansession(struct sockaddr_in sin)
+{
+	struct mansession *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;
+	ast_atomic_fetchadd_int(&num_sessions, 1);
+
+	ao2_link(sessions, newsession);
+
+	return unref_mansession(newsession);
+}
+
+static int mansession_cmp_fn(void *obj, void *arg, int flags)
+{
+	struct mansession *s = obj;
+	char *str = arg;
+	return !strcasecmp(s->username, str) ? CMP_MATCH : 0;
+}
+
+static void session_destroy(struct mansession *s)
+{
+	ao2_unlink(sessions, s);
+	ast_atomic_fetchadd_int(&num_sessions, -1);
+}
+
+
 static int check_manager_session_inuse(const char *name)
 {
-	struct mansession *session = NULL;
+	struct mansession *session = ao2_find(sessions, (char*) name, OBJ_POINTER);
 
-	AST_LIST_LOCK(&sessions);
-	AST_LIST_TRAVERSE(&sessions, session, list) {
-		if (!strcasecmp(session->username, name)) 
-			break;
+	if (session) {
+		unref_mansession(session);
 	}
-	AST_LIST_UNLOCK(&sessions);
-
 	return session ? 1 : 0;
 }
 
@@ -447,8 +538,9 @@
 	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;
 }
 
@@ -462,8 +554,9 @@
 	int ret = 0;
 
 	AST_RWLIST_RDLOCK(&users);
-	if ((user = get_manager_by_name_locked (s->username)))
+	if ((user = get_manager_by_name_locked (s->username))) {
 		ret = user->displayconnects;
+	}
 	AST_RWLIST_UNLOCK(&users);
 	
 	return ret;
@@ -496,8 +589,9 @@
 		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) {
@@ -525,15 +619,17 @@
 	case CLI_GENERATE:
 		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;
 }
@@ -556,8 +652,9 @@
 	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 ) {
@@ -569,8 +666,9 @@
 		return ret;
 	}
 
-	if (a->argc != 4)
+	if (a->argc != 4) {
 		return CLI_SHOWUSAGE;
+	}
 
 	AST_RWLIST_RDLOCK(&users);
 
@@ -616,8 +714,9 @@
 	case CLI_GENERATE:
 		return NULL;
 	}
-	if (a->argc != 3)
+	if (a->argc != 3) {
 		return CLI_SHOWUSAGE;
+	}
 
 	AST_RWLIST_RDLOCK(&users);
 
@@ -637,9 +736,8 @@
 
 	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;
 }
 
@@ -680,6 +778,8 @@
 #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";
@@ -694,12 +794,14 @@
 
 	ast_cli(a->fd, HSMCONN_FORMAT1, "Username", "IP Address", "Start", "Elapsed", "FileDes", "HttpCnt", "Read", "Write");
 
-	AST_LIST_LOCK(&sessions);
-	AST_LIST_TRAVERSE(&sessions, s, list) {
+	i = ao2_iterator_init(sessions, 0);
+	while ((s = ao2_iterator_next(&i))) {
+		ao2_lock(s);
 		ast_cli(a->fd, HSMCONN_FORMAT2, s->username, ast_inet_ntoa(s->sin.sin_addr), (int)(s->sessionstart), (int)(now - s->sessionstart), s->fd, s->inuse, s->readperm, s->writeperm);
 		count++;
+		ao2_unlock(s);
+		unref_mansession(s);
 	}
-	AST_LIST_UNLOCK(&sessions);
 
 	ast_cli(a->fd, "%d users connected.\n", count);
 
@@ -746,8 +848,9 @@
 	case CLI_GENERATE:
 		return NULL;
 	}
-	if (a->argc > 2)
+	if (a->argc > 2) {
 		return CLI_SHOWUSAGE;
+	}
 	reload_manager();
 	return CLI_SUCCESS;
 }
@@ -765,53 +868,6 @@
 };
 
 /*
- * 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);
-	return AST_LIST_NEXT(e, eq_next);
-}
-
-static void ref_event(struct eventqent *e)
-{
-	ast_atomic_fetchadd_int(&e->usecount, 1);
-}
-
-/*
- * destroy a session, leaving the usecount
- */
-static void free_session(struct mansession *s)
-{
-	struct eventqent *eqe = s->last_ev;
-	struct ast_datastore *datastore;
-
-	/* Get rid of each of the data stores on the session */
-	while ((datastore = AST_LIST_REMOVE_HEAD(&s->datastores, entry))) {
-		/* Free the data store */
-		ast_datastore_free(datastore);
-	}
-
-	if (s->f != NULL)
-		fclose(s->f);
-	ast_mutex_destroy(&s->__lock);
-	ast_free(s);
-	unref_event(eqe);
-}
-
-static void destroy_session(struct mansession *s)
-{
-	AST_LIST_LOCK(&sessions);
-	AST_LIST_REMOVE(&sessions, s, list);
-	ast_atomic_fetchadd_int(&num_sessions, -1);
-	free_session(s);
-	AST_LIST_UNLOCK(&sessions);
-}
-
-/*
  * Generic function to return either the first or the last matching header
  * from a list of variables, possibly skipping empty strings.
  * At the moment there is only one use of this function in this file,
@@ -867,20 +923,24 @@
 	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;
@@ -905,18 +965,21 @@
 		int elapsed;
 		struct pollfd fd;
 		n = fwrite(src, 1, len, s->f);	/* try to write the string, non blocking */
-		if (n == len /* ok */ || n < 0 /* error */)
+		if (n == len /* ok */ || n < 0 /* error */) {
 			break;
+		}
 		len -= n;	/* skip already written data */
 		src += n;
 		fd.fd = s->fd;
 		fd.events = POLLOUT;
 		n = -1;		/* error marker */
 		elapsed = ast_tvdiff_ms(ast_tvnow(), start);
-		if (elapsed > s->writetimeout)
+		if (elapsed > s->writetimeout) {
 			break;
-		if (poll(&fd, 1, s->writetimeout - elapsed) < 1)
+		}
+		if (poll(&fd, 1, s->writetimeout - elapsed) < 1) {
 			break;
+		}
 	}
 	fflush(s->f);
 	return n < 0 ? -1 : 0;
@@ -941,17 +1004,19 @@
 	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);
 	va_end(ap);
 
-	if (s->f != NULL)
+	if (s->f != NULL) {
 		send_string(s, buf->str);
-	else
+	} else {
 		ast_verbose("fd == -1 in astman_append, should not happen\n");
+	}
 }
 
 /*! \note NOTE: XXX this comment is unclear and possibly wrong.
@@ -976,16 +1041,19 @@
 	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)
@@ -1022,10 +1090,11 @@
 {
 	int maskint = strings_to_mask(eventmask);
 
-	ast_mutex_lock(&s->__lock);
-	if (maskint >= 0)
+	ao2_lock(s);
+	if (maskint >= 0) {
 		s->send_events = maskint;
-	ast_mutex_unlock(&s->__lock);
+	}
+	ao2_unlock(s);
 
 	return maskint;
 }
@@ -1044,8 +1113,9 @@
 	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);
@@ -1069,14 +1139,16 @@
 			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", 
 				S_OR(s->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->sin.sin_addr), username);
@@ -1143,13 +1215,15 @@
 		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");
+	}
 	ast_config_destroy(cfg);
 	astman_append(s, "\r\n");
 
@@ -1183,8 +1257,9 @@
 		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");
+	}
 	ast_config_destroy(cfg);
 	astman_append(s, "\r\n");
 
@@ -1198,8 +1273,9 @@
 static void json_escape(char *out, const char *in)
 {
 	for (; *in; in++) {
-		if (*in == '\\' || *in == '\"')
+		if (*in == '\\' || *in == '\"') {
 			*out++ = '\\';
+		}
 		*out++ = *in;
 	}
 	*out = '\0';
@@ -1246,11 +1322,13 @@
 		}
 		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);
@@ -1263,8 +1341,9 @@
 			}
 			json_escape(buf, v->value);
 			astman_append(s, "%s\"", buf);
-			if (!comma2)
+			if (!comma2) {
 				comma2 = 1;
+			}
 		}
 		astman_append(s, "]");
 	}
@@ -1291,8 +1370,9 @@
 
 		snprintf(hdr, sizeof(hdr), "Action-%06d", x);
 		action = astman_get_header(m, hdr);
-		if (ast_strlen_zero(action))
+		if (ast_strlen_zero(action)) {
 			break;
+		}
 		snprintf(hdr, sizeof(hdr), "Cat-%06d", x);
 		cat = astman_get_header(m, hdr);
 		snprintf(hdr, sizeof(hdr), "Var-%06d", x);
@@ -1319,8 +1399,9 @@
 			}
 			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(cat) || ast_strlen_zero(value)) {
 				result = UNSPECIFIED_ARGUMENT;
@@ -1388,8 +1469,9 @@
 				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(cat) || ast_strlen_zero(var) || ast_strlen_zero(line)) {
@@ -1460,8 +1542,9 @@
 		}
 		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 {
@@ -1520,8 +1603,9 @@
 	if ((fd = open(filepath->str, O_CREAT | O_EXCL, AST_FILE_MODE)) != -1) {
 		close(fd);
 		astman_send_ack(s, m, "New configuration file created successfully");
-	} else 
+	} else {
 		astman_send_error(s, m, strerror(errno));
+	}
 
 	return 0;
 }
@@ -1543,21 +1627,24 @@
 	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->__lock);
-	if (s->waiting_thread != AST_PTHREADT_NULL)
+	ao2_lock(s);
+	if (s->waiting_thread != AST_PTHREADT_NULL) {
 		pthread_kill(s->waiting_thread, SIGURG);
+	}
 
 	if (s->managerid) { /* AMI-over-HTTP session */
 		/*
@@ -1568,43 +1655,52 @@
 		time_t now = time(NULL);
 		int max = s->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->send_events)	/* make sure we record events */
+		}
+		if (!s->send_events) {	/* make sure we record events */
 			s->send_events = -1;
+		}
 	}
-	ast_mutex_unlock(&s->__lock);
+	ao2_unlock(s);
 
 	/* XXX should this go inside the lock ? */
 	s->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->__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->waiting_thread != pthread_self())
+		if (s->waiting_thread != pthread_self()) {
 			needexit = 1;
-		if (s->needdestroy)
+		}
+		if (s->needdestroy) {
 			needexit = 1;
-		ast_mutex_unlock(&s->__lock);
-		if (needexit)
+		}
+		ao2_unlock(s);
+		if (needexit) {
 			break;
+		}
 		if (s->managerid == 0) {	/* AMI session */
-			if (ast_wait_for_input(s->fd, 1000))
+			if (ast_wait_for_input(s->fd, 1000)) {
 				break;
+			}
 		} else {	/* HTTP session */
 			sleep(1);
 		}
 	}
 	ast_debug(1, "Finished waiting for an event!\n");
-	ast_mutex_lock(&s->__lock);
+
+	ao2_lock(s);
 	if (s->waiting_thread == pthread_self()) {
 		struct eventqent *eqe;
 		astman_send_response(s, m, "Success", "Waiting for Event completed.");
@@ -1624,7 +1720,7 @@
 	} else {
 		ast_debug(1, "Abandoning event request!\n");
 	}
-	ast_mutex_unlock(&s->__lock);
+	ao2_unlock(s);
 	return 0;
 }
 
@@ -1641,9 +1737,10 @@
 
 	astman_start_ack(s, m);
 	AST_RWLIST_TRAVERSE(&actions, cur, list) {
-		if (s->writeperm & cur->authority || cur->authority == 0)
+		if (s->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");
 
@@ -1685,14 +1782,22 @@
 
 static int action_login(struct mansession *s, const struct message *m)
 {
+
+	/* still authenticated - don't process again */
+	if (s->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->authenticated = 1;
-	if (manager_displayconnects(s))
+	if (manager_displayconnects(s)) {
 		ast_verb(2, "%sManager '%s' logged on from %s\n", (s->managerid ? "HTTP " : ""), s->username, ast_inet_ntoa(s->sin.sin_addr));
+	}
 	ast_log(LOG_EVENT, "%sManager '%s' logged on from %s\n", (s->managerid ? "HTTP " : ""), s->username, ast_inet_ntoa(s->sin.sin_addr));
 	astman_send_ack(s, m, "Authentication accepted");
 	return 0;
@@ -1703,12 +1808,13 @@
 	const char *authtype = astman_get_header(m, "AuthType");
 
 	if (!strcasecmp(authtype, "MD5")) {
-		if (ast_strlen_zero(s->challenge))
+		if (ast_strlen_zero(s->challenge)) {
 			snprintf(s->challenge, sizeof(s->challenge), "%ld", ast_random());
-		ast_mutex_lock(&s->__lock);
+		}
+		ao2_lock(s);
 		astman_start_ack(s, m);
 		astman_append(s, "Challenge: %s\r\n\r\n", s->challenge);
-		ast_mutex_unlock(&s->__lock);
+		ao2_unlock(s);
 	} else {
 		astman_send_error(s, m, "Must specify AuthType");
 	}
@@ -1768,8 +1874,9 @@
 
 	pbx_builtin_setvar_helper(c, varname, S_OR(varval, ""));
 
-	if (c)
+	if (c) {
 		ast_channel_unlock(c);
+	}
 
 	astman_send_ack(s, m, "Variable Set");
 
@@ -1811,8 +1918,9 @@
 		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);
 
@@ -1849,14 +1957,15 @@
 	);
 	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");
@@ -1891,10 +2000,11 @@
 		}
 
 		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;
@@ -1945,8 +2055,9 @@
 			ast_state2str(c->_state), bridge, c->uniqueid, str->str, idText);
 		}
 		ast_channel_unlock(c);
-		if (!all)
+		if (!all) {
 			break;
+		}
 		c = ast_channel_walk_locked(c);
 	}
 	astman_append(s,
@@ -1991,10 +2102,11 @@
 	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;
 }
@@ -2055,22 +2167,28 @@
 	res = ast_async_goto(chan, context, exten, pi);
 	if (!res) {
 		if (!ast_strlen_zero(name2)) {
-			if (chan2)
+			if (chan2) {
 				res = ast_async_goto(chan2, context, exten, pi);
-			else
+			} 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;
 }
 
@@ -2193,8 +2311,9 @@
 	}
 
 	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 */
@@ -2216,8 +2335,9 @@
 	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;
 }
 
@@ -2259,8 +2379,9 @@
 			in->vars, in->account, &chan);
 	}
 
-	if (!chan)
+	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"
@@ -2279,8 +2400,9 @@
 		);
 
 	/* Locked by ast_pbx_outgoing_exten or ast_pbx_outgoing_app */
-	if (chan)
+	if (chan) {
 		ast_channel_unlock(chan);
+	}
 	ast_free(in);
 	return NULL;
 }
@@ -2352,13 +2474,15 @@
 	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;
@@ -2375,10 +2499,12 @@
    			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));
@@ -2411,17 +2537,18 @@
 		}
 		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;
 }
 
@@ -2509,8 +2636,9 @@
 		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);
@@ -2567,7 +2695,7 @@
 {
 	int ret = 0;
 
-	ast_mutex_lock(&s->__lock);
+	ao2_lock(s);
 	if (s->f != NULL) {
 		struct eventqent *eqe;
 
@@ -2582,7 +2710,7 @@
 			s->last_ev = unref_event(s->last_ev);
 		}
 	}
-	ast_mutex_unlock(&s->__lock);
+	ao2_unlock(s);
 	return ret;
 }
 
@@ -2622,10 +2750,11 @@
 	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"
@@ -2671,10 +2800,11 @@
 	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);
@@ -2707,10 +2837,11 @@
 	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;
 }
 
@@ -2730,10 +2861,11 @@
 	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");	
 
@@ -2822,10 +2954,11 @@
 	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 : "");
@@ -2850,32 +2983,37 @@
 	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");
@@ -2904,21 +3042,22 @@
 	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->__lock);
+		ao2_lock(s);
 		astman_send_error(s, m, "Missing action in request");
-		ast_mutex_unlock(&s->__lock);
+		ao2_unlock(s);
 		return 0;
 	}
 
 	if (!s->authenticated && strcasecmp(action, "Login") && strcasecmp(action, "Logoff") && strcasecmp(action, "Challenge")) {
-		ast_mutex_lock(&s->__lock);
+		ao2_lock(s);
 		astman_send_error(s, m, "Permission denied");
-		ast_mutex_unlock(&s->__lock);
+		ao2_unlock(s);
 		return 0;
 	}
 
@@ -2926,34 +3065,40 @@
 		(!strcasecmp(action, "Login") || !strcasecmp(action, "Challenge"))) {
 		if (check_manager_session_inuse(user)) {
 			sleep(1);
-			ast_mutex_lock(&s->__lock);
+			ao2_lock(s);
 			astman_send_error(s, m, "Login Already In Use");
-			ast_mutex_unlock(&s->__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->writeperm & tmp->authority || tmp->authority == 0)
-			ret = tmp->func(s, m);
-		else
+		}
+		if (s->writeperm & tmp->authority || tmp->authority == 0) {
+			call_func = tmp->func;
+		} else {
 			astman_send_error(s, m, "Permission denied");
+		}
 		break;
 	}
 	AST_RWLIST_UNLOCK(&actions);
 
-	if (!tmp) {
+	if (tmp) {
+		/* call AMI function after actions list are unlocked */
+		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->__lock);
+		ao2_lock(s);
 		astman_send_error(s, m, buf);
-		ast_mutex_unlock(&s->__lock);
+		ao2_unlock(s);
 	}
-	if (ret)
+	if (ret) {
 		return ret;
+	}
 	/* Once done with our message, deliver any pending events */
 	return process_events(s);
 }
@@ -2979,12 +3124,13 @@
 	 */
 	for (x = 0; x < s->inlen; x++) {
 		int cr;	/* set if we have \r */
-		if (src[x] == '\r' && x+1 < s->inlen && src[x+1] == '\n')
+		if (src[x] == '\r' && x+1 < s->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 */
@@ -3000,40 +3146,42 @@
 	res = 0;
 	while (res == 0) {
 		/* XXX do we really need this locking ? */
-		ast_mutex_lock(&s->__lock);
+		ao2_lock(s);
 		if (s->pending_event) {
 			s->pending_event = 0;
-			ast_mutex_unlock(&s->__lock);
+			ao2_unlock(s);
 			return 0;
 		}
 		s->waiting_thread = pthread_self();
-		ast_mutex_unlock(&s->__lock);
+		ao2_unlock(s);
 
 		res = ast_wait_for_input(s->fd, -1);	/* return 0 on timeout ? */
 
-		ast_mutex_lock(&s->__lock);
+		ao2_lock(s);
 		s->waiting_thread = AST_PTHREADT_NULL;
-		ast_mutex_unlock(&s->__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->__lock);
+
+	ao2_lock(s);
 	res = fread(src + s->inlen, 1, maxlen - s->inlen, s->f);
-	if (res < 1)
+	if (res < 1) {
 		res = -1;	/* error return */
-	else {
+	} else {
 		s->inlen += res;
 		src[s->inlen] = '\0';
 		res = 0;
 	}
-	ast_mutex_unlock(&s->__lock);
+	ao2_unlock(s);
 	return res;
 }
 
@@ -3045,16 +3193,18 @@
 
 	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;
 		}
@@ -3072,25 +3222,23 @@
 static void *session_do(void *data)
 {
 	struct ast_tcptls_session_instance *ser = data;
-	struct mansession *s = ast_calloc(1, sizeof(*s));
+	struct mansession *s = build_mansession(ser->requestor);
 	int flags;
 	int res;
 
-	if (s == NULL)
+	if (s == NULL) {
 		goto done;
+	}
+	ao2_lock(sessions);
 
-	s->writetimeout = 100;
-	s->waiting_thread = AST_PTHREADT_NULL;
-
 	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(&s->__lock);
-	s->send_events = -1;
 	/* Hook to the tail of the event queue */
 	s->last_ev = grab_last();
 
@@ -3101,24 +3249,23 @@
 
 	AST_LIST_HEAD_INIT_NOLOCK(&s->datastores);
 
-	AST_LIST_LOCK(&sessions);
-	AST_LIST_INSERT_HEAD(&sessions, s, 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 (s->authenticated) {
-			if (manager_displayconnects(s))
+		if (manager_displayconnects(s)) {
 			ast_verb(2, "Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
+		}
 		ast_log(LOG_EVENT, "Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
 	} else {
-			if (displayconnects)
+		if (displayconnects) {
 			ast_verb(2, "Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(s->sin.sin_addr));
+		}
 		ast_log(LOG_EVENT, "Failed attempt from %s\n", ast_inet_ntoa(s->sin.sin_addr));
 	}
 
@@ -3135,7 +3282,7 @@
 	*/
 	usleep(1);
 
-	destroy_session(s);
+	session_destroy(s);
 
 done:
 	ao2_ref(ser, -1);
@@ -3148,23 +3295,24 @@
 {
 	struct mansession *s;
 	time_t now = time(NULL);
+	struct ao2_iterator i;
 
-	AST_LIST_LOCK(&sessions);
-	AST_LIST_TRAVERSE_SAFE_BEGIN(&sessions, s, list) {
+	i = ao2_iterator_init(sessions, 0);
+	while ((s = ao2_iterator_next(&i)) && n_max > 0) {
+		unref_mansession(s);
+		ao2_lock(s);
 		if (s->sessiontimeout && (now > s->sessiontimeout) && !s->inuse) {
-			AST_LIST_REMOVE_CURRENT(list);
-			ast_atomic_fetchadd_int(&num_sessions, -1);
 			if (s->authenticated && (VERBOSITY_ATLEAST(2)) && manager_displayconnects(s)) {
 				ast_verb(2, "HTTP Manager '%s' timed out from %s\n",
 					s->username, ast_inet_ntoa(s->sin.sin_addr));
 			}
-			free_session(s);	/* XXX outside ? */
-			if (--n_max <= 0)
-				break;
+			ao2_unlock(s);
+			session_destroy(s);
+			n_max--;
+		} else {
+			ao2_unlock(s);
 		}
 	}
-	AST_LIST_TRAVERSE_SAFE_END;
-	AST_LIST_UNLOCK(&sessions);
 }
 
 /*
@@ -3176,8 +3324,9 @@
 	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;
@@ -3208,13 +3357,16 @@
 	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)
+	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,
@@ -3245,21 +3397,22 @@
 	append_event(buf->str, category);
 
 	/* Wake up any sleeping sessions */
-	AST_LIST_LOCK(&sessions);
-	AST_LIST_TRAVERSE(&sessions, s, list) {
-		ast_mutex_lock(&s->__lock);
-		if (s->waiting_thread != AST_PTHREADT_NULL)
+	i = ao2_iterator_init(sessions, 0);
+	while ((s = ao2_iterator_next(&i))) {
+		ao2_lock(s);
+		if (s->waiting_thread != AST_PTHREADT_NULL) {
 			pthread_kill(s->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.
 			 */
 			s->pending_event = 1;
-		ast_mutex_unlock(&s->__lock);
+		}
+		ao2_unlock(s);
+		unref_mansession(s);
 	}
-	AST_LIST_UNLOCK(&sessions);
 
 	AST_RWLIST_RDLOCK(&manager_hooks);
 	AST_RWLIST_TRAVERSE(&manager_hooks, hook, list) {
@@ -3320,10 +3473,11 @@
 		}
 	}
 	
-	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);
 
@@ -3338,8 +3492,9 @@
 {
 	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;
@@ -3386,24 +3541,63 @@
 static struct mansession *find_session(uint32_t ident, int incinuse)
 {
 	struct mansession *s;
+	struct ao2_iterator i;
 
-	if (ident == 0)
+	if (ident == 0) {
 		return NULL;
+	}
 
-	AST_LIST_LOCK(&sessions);
-	AST_LIST_TRAVERSE(&sessions, s, list) {
-		ast_mutex_lock(&s->__lock);
+	i = ao2_iterator_init(sessions, 0);
+	while ((s = ao2_iterator_next(&i))) {
+		ao2_lock(s);
 		if (s->managerid == ident && !s->needdestroy) {
 			ast_atomic_fetchadd_int(&s->inuse, incinuse ? 1 : 0);
+			unref_mansession(s);
 			break;
 		}
-		ast_mutex_unlock(&s->__lock);
+		ao2_unlock(s);
+		unref_mansession(s);
 	}
-	AST_LIST_UNLOCK(&sessions);
 
 	return s;
 }
 
+/*!
+ * 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 *find_session_by_nonce(const char *username, unsigned long nonce, int *stale)
+{
+	struct mansession *s;
+	struct ao2_iterator i;
+
+	if (nonce == 0 || username == NULL || stale == NULL) {
+		return NULL;
+	}
+	
+	i = ao2_iterator_init(sessions, 0);
+	while ((s = ao2_iterator_next(&i))) {
+		ao2_lock(s);
+		if (!strcasecmp(s->username, username) && s->managerid == nonce) {
+			*stale = 0;
+			unref_mansession(s);
+			break;
+		} else if (!strcasecmp(s->username, username) && s->oldnonce == nonce) {
+			*stale = 1;
+			unref_mansession(s);
+			break;
+		}
+		ao2_unlock(s);
+		unref_mansession(s);
+	}
+	return s;
+}
+
 int astman_is_authed(uint32_t ident) 
 {
 	int authed;
@@ -3414,7 +3608,7 @@
 
 	authed = (s->authenticated != 0);
 
-	ast_mutex_unlock(&s->__lock);
+	ao2_unlock(s);
 
 	return authed;
 }
@@ -3423,18 +3617,24 @@
 {
 	int result = 0;
 	struct mansession *s;
+	struct ao2_iterator i;
 
-	AST_LIST_LOCK(&sessions);
-	AST_LIST_TRAVERSE(&sessions, s, list) {
-		ast_mutex_lock(&s->__lock);
+	if (ident == 0) {
+		return 0;
+	}
+
+	i = ao2_iterator_init(sessions, 0);
+	while ((s = ao2_iterator_next(&i))) {
+		ao2_lock(s);
 		if ((s->managerid == ident) && (s->readperm & perm)) {
 			result = 1;
-			ast_mutex_unlock(&s->__lock);
+			ao2_unlock(s);
+			unref_mansession(s);
 			break;
 		}
-		ast_mutex_unlock(&s->__lock);
+		ao2_unlock(s);
+		unref_mansession(s);
 	}
-	AST_LIST_UNLOCK(&sessions);
 	return result;
 }
 
@@ -3442,18 +3642,24 @@
 {
 	int result = 0;
 	struct mansession *s;
+	struct ao2_iterator i;
 
-	AST_LIST_LOCK(&sessions);
-	AST_LIST_TRAVERSE(&sessions, s, list) {
-		ast_mutex_lock(&s->__lock);
+	if (ident == 0) {
+		return 0;
+	}
+
+	i = ao2_iterator_init(sessions, 0);
+	while ((s = ao2_iterator_next(&i))) {
+		ao2_lock(s);
 		if ((s->managerid == ident) && (s->writeperm & perm)) {
 			result = 1;
-			ast_mutex_unlock(&s->__lock);
+			ao2_unlock(s);
+			unref_mansession(s);
 			break;
 		}
-		ast_mutex_unlock(&s->__lock);
+		ao2_unlock(s);
+		unref_mansession(s);
 	}
-	AST_LIST_UNLOCK(&sessions);
 	return result;
 }
 
@@ -3475,8 +3681,9 @@
 			ast_str_append(out, 0, "%s", buf);
 			dst = buf;
 			space = sizeof(buf);
-			if (*src == '\0')
+			if (*src == '\0') {
 				break;
+			}
 		}
 			
 		if ( (mode & 2) && !isalnum(*src)) {
@@ -3523,29 +3730,11 @@
 	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)
@@ -3587,7 +3776,7 @@
  *   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;
@@ -3599,30 +3788,41 @@
 	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 */
+		if (ast_strlen_zero(val)) { 
+			/* 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;
@@ -3632,54 +3832,57 @@
 			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) {
+			/* 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;
+		}
 
-		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;
+
+		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");
@@ -3687,42 +3890,57 @@
 	}
 }
 
-static struct ast_str *generic_http_callback(enum output_format format,
-					     struct sockaddr_in *requestor, 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 *requestor, const char *uri,
+					     struct ast_variable *get_params,
+					     struct ast_variable *headers)
 {
 	struct mansession *s = 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 (!(s = find_session(ident, 1))) {
+
+		if (num_sessions >= MAX_SESSIONS) {
+			/* Limit the active session numbers */
+			ast_http_error(ser, 500, "Server Error", "Internal Server Error (to many connections)\n");
+			return -1;
+		}
+		
+		/**/
 		/* Create new session.
 		 * While it is not in the list we don't need any locking
 		 */
-		if (!(s = ast_calloc(1, sizeof(*s)))) {
-			*status = 500;
-			goto generic_callback_out;
+		if (!(s = build_mansession(*requestor))) {
+			ast_http_error(ser, 500, "Server Error", "Internal Server Error (out of memory)\n");
+			return -1;
 		}
-		s->sin = *requestor;
-		s->fd = -1;
-		s->waiting_thread = AST_PTHREADT_NULL;
+		ao2_lock(s);
 		s->send_events = 0;
-		ast_mutex_init(&s->__lock);
-		ast_mutex_lock(&s->__lock);
 		s->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
@@ -3732,23 +3950,34 @@
 		while ((s->managerid = rand() ^ (unsigned long) s) == 0);
 		s->last_ev = grab_last();
 		AST_LIST_HEAD_INIT_NOLOCK(&s->datastores);
-		AST_LIST_LOCK(&sessions);
-		AST_LIST_INSERT_HEAD(&sessions, s, list);
-		ast_atomic_fetchadd_int(&num_sessions, 1);
-		AST_LIST_UNLOCK(&sessions);
 	}
+	ao2_unlock(s);
 
-	ast_mutex_unlock(&s->__lock);
+	http_header = ast_str_create(128);
+	out = ast_str_create(2048);
 
-	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->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");
+		goto generic_callback_out;
+	}
 
+	if (method == AST_HTTP_POST) {
+		params = 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);
@@ -3772,13 +4001,12 @@
 		s->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"
-		       "\r\n",
-			contenttype[format],
-			s->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",
+		contenttype[format],
+		s->managerid, httptimeout);
 
 	if (format == FORMAT_XML) {
 		ast_str_append(&out, 0, "<ajax-response>\n");
@@ -3791,7 +4019,7 @@
 
 #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=\"\">-----&gt;</option>\n\
 		<option value=\"login\">login</option>\n\
@@ -3816,10 +4044,11 @@
 		
 		if (l) {
 			if ((buf = mmap(NULL, l, PROT_READ | PROT_WRITE, MAP_SHARED, s->fd, 0))) {
-				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) {
@@ -3832,10 +4061,11 @@
 
 	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(&s->__lock);
+	ao2_lock(s);
 	/* Reset HTTP timeout.  If we're not authenticated, keep it extremely short */
 	s->sessiontimeout = time(NULL) + ((s->authenticated || httptimeout < 5) ? httptimeout : 5);
 
@@ -3845,42 +4075,380 @@
 			blastaway = 1;
 		} else {
 			ast_debug(1, "Need destroy, but can't do it yet!\n");
-			if (s->waiting_thread != AST_PTHREADT_NULL)
+			if (s->waiting_thread != AST_PTHREADT_NULL) {
 				pthread_kill(s->waiting_thread, SIGURG);
+			}
 			s->inuse--;
 		}
-	} else
+	} else {
 		s->inuse--;
-	ast_mutex_unlock(&s->__lock);
+	}
+	ao2_unlock(s);
 
-	if (blastaway)
-		destroy_session(s);
+	ast_http_send(ser, method, 200, NULL, http_header, out, 0, 0);
+	http_header = out = NULL;
+
 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(s->f) {
+		fclose(s->f);
+	}
+
+	if (s != NULL && blastaway) {
+		session_destroy(s);
+	}
+	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 *requestor, const char *uri,
+					     struct ast_variable *get_params,
+					     struct ast_variable *headers)
 {
-	return generic_http_callback(FORMAT_HTML, &ser->requestor, uri, method, params, status, title, contentlength);
+	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 = 0;
+	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;
+	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_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(requestor->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, requestor)) {
+		AST_RWLIST_UNLOCK(&users);
+		ast_log(LOG_NOTICE, "%s failed to pass IP ACL as '%s'\n", ast_inet_ntoa(requestor->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.
+	 */
+	strncpy(u_username, user->username, sizeof(u_username)-1);
+	u_readperm = user->readperm;
+	u_writeperm = user->writeperm;
+	u_displayconnects = user->displayconnects;
+	AST_RWLIST_UNLOCK(&users);
+
+	if (!(s = find_session_by_nonce(d.username, nonce, &stale))) {
+		
+		if (num_sessions >= MAX_SESSIONS) {
+			/* Limit the active session numbers */
+			ast_http_error(ser, 500, "Server Error", "Internal Server Error (to many connections)\n");
+			return -1;
+		}
+		
+		/*
+		 * Create new session.
+		 * While it is not in the list we don't need any locking
+		 */
+		if (!(s = build_mansession(*requestor))) {
+			ast_http_error(ser, 500, "Server Error", "Internal Server Error (out of memory)\n");
+			return -1;
+		}
+		ao2_lock(s);
+
+		ast_copy_string(s->username, u_username, sizeof(s->username));
+		s->managerid = nonce;
+		s->last_ev = grab_last();
+		AST_LIST_HEAD_INIT_NOLOCK(&s->datastores);
+
+		s->readperm = u_readperm;
+		s->writeperm = u_writeperm;
+		s->writetimeout = u_writetimeout;
+
+		if (u_displayconnects) {
+			ast_verb(2, "HTTP Manager '%s' logged in from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
+		}
+		s->noncetime = s->sessionstart = time_now;
+		s->authenticated = 1;
+		
+		ast_log(LOG_EVENT, "HTTP Manager '%s' logged in from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
+
+	} else if(stale) {
+		/*
+		 * Session found, but nonce is staled.
+		 *
+		 * It may be caused if old request (w/old nonce) arrived.
+		 * 
+		 * Or, if http request with lower nonce_count value
+		 * arrive after request w/higher nonce counter and
+		 * new auth request was beed send before.
+		 * 
+		 * This may be as the result of http proxy usage 
+		 * (separate delay or multipath) or in situation,
+		 * if page are refreshed to fast (found in Firefox).
+		 *
+		 * In this situation reask the auth information with
+		 * actual nonce value.
+		 */
+		nonce = s->managerid;
+		ao2_unlock(s);
+		stale = 1;
+		goto out_401;
+	} else {
+		sscanf(d.nc, "%lx", &nc);
+		if (s->nc >= nc || ((time_now - s->noncetime) > 62) ) {
+			/*
+			 * Nonce time expired (> 2 minutes)
+			 * or something wrong with nonce counter.
+			 *
+			 * Create new nonce key, put him in managerid field
+			 * in manager session struct and resend Digest auth
+			 * request.
+			 * Save expired nonce value in s->oldnonce field for
+			 * stale checking...
+			 */
+			s->nc = 0; /* Reset nonce counter */
+			s->oldnonce = s->managerid;
+			nonce = s->managerid = ast_random();
+			s->noncetime = time_now;
+			ao2_lock(s);
+			stale = 1;
+			goto out_401;
+		} else {
+			s->nc = nc; /* All OK, save nonce counter */
+		}
+	}
+
+
+	/* Reset session timeout. */
+	s->sessiontimeout = time(NULL) + (httptimeout > 5 ? httptimeout : 5);
+	ao2_unlock(s);
+
+	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");
+		goto auth_callback_out;
+	}
+	
+	if (method == AST_HTTP_POST) {
+		params = 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", s->username, ast_inet_ntoa(s->sin.sin_addr));
+		}
+		ast_log(LOG_EVENT, "HTTP Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
+
+		s->needdestroy = 1;
+	}
+
+	if (s->f) {
+		result_size = ftell(s->f); /* Calculate aprox. 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&trade; 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 (result_size) {
+		char *buf;
+		if ((buf = mmap(NULL, result_size, 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, result_size);
+		}
+	} else if (format == FORMAT_XML || format == FORMAT_HTML) {
+		xml_translate(&out, "", params, format);
+	}
+
+	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 resource and unlock manager session */
+	if (method == AST_HTTP_POST && params) {
+		ast_variables_destroy(params);
+	}
+	if (http_header) {
+		ast_free(http_header);
+	}
+	if (out) {
+		ast_free(out);
+	}
+
+	ao2_lock(s);
+	if(s->f) {
+		fclose(s->f);
+	}
+	s->f = NULL;
+	s->fd = -1;
+	ao2_unlock(s);
+
+	if (s->needdestroy) {
+		ast_debug(1, "Need destroy, doing it now!\n");
+		session_destroy(s);
+	}
+	return 0;
+	
+out_401:
+	if (!nonce) {
+		nonce = ast_random();
+	}
+	
+	ast_http_auth(ser, global_realm, nonce, nonce, stale, NULL);
+	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->requestor, uri, method, params, status, title, contentlength);
+	return generic_http_callback(ser, method, FORMAT_HTML, &ser->requestor, 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->requestor, uri, method, params, status, title, contentlength);
+	return generic_http_callback(ser, method, FORMAT_XML, &ser->requestor, 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->requestor, 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__,
 };
@@ -3889,7 +4457,6 @@
 	.description = "HTML Manager Event Interface",
 	.uri = "manager",
 	.callback = manager_http_callback,
-	.supports_get = 1,
 	.data = NULL,
 	.key = __FILE__,
 };
@@ -3898,11 +4465,54 @@
 	.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->requestor, 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->requestor, 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->requestor, 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__,
+};
+
 static int registered = 0;
 static int webregged = 0;
 
@@ -3949,6 +4559,8 @@
 	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;
 
@@ -3993,8 +4605,9 @@
 		/* 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) {
@@ -4003,26 +4616,29 @@
 	}
 
 	/* default values */
+	ast_copy_string(global_realm, S_OR(ast_config_AST_SYSTEM_NAME, DEFAULT_REALM), sizeof(global_realm));
 	memset(&ami_desc.sin, 0, sizeof(struct sockaddr_in));
 	memset(&amis_desc.sin, 0, sizeof(amis_desc.sin));
 	amis_desc.sin.sin_port = htons(5039);
 	ami_desc.sin.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.sin.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.sin.sin_addr, hp->h_addr, sizeof(amis_desc.sin.sin_addr));
 				have_sslbindaddr = 1;
@@ -4064,12 +4680,15 @@
 		}	
 	}
 
-	if (manager_enabled)
+	if (manager_enabled) {
 		ami_desc.sin.sin_family = AF_INET;
-	if (!have_sslbindaddr)
+	}
+	if (!have_sslbindaddr) {
 		amis_desc.sin.sin_addr = ami_desc.sin.sin_addr;
-	if (ami_tls_cfg.enabled)
+	}
+	if (ami_tls_cfg.enabled) {
 		amis_desc.sin.sin_family = AF_INET;
+	}
 
 	
 	AST_RWLIST_WRLOCK(&users);
@@ -4081,8 +4700,9 @@
 		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)) {
@@ -4096,8 +4716,9 @@
 				 * 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));
@@ -4112,36 +4733,45 @@
 					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;
+					}
 				}
 			}
 		}
@@ -4153,13 +4783,15 @@
 	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));
 
@@ -4182,8 +4814,9 @@
 		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")) {
@@ -4196,12 +4829,14 @@
 				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);
 	}
@@ -4211,13 +4846,25 @@
 	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);
 	}
@@ -4225,11 +4872,20 @@
 
 	AST_RWLIST_UNLOCK(&users);
 
+	if (!reload) {
+		sessions = ao2_container_alloc(100, 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 {
@@ -4237,18 +4893,24 @@
 			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;
 }
 
Index: main/http.c
===================================================================
--- main/http.c	(revision 148468)
+++ main/http.c	(working copy)
@@ -93,10 +93,11 @@
 
 /*! \brief Limit the kinds of files we're willing to serve up */
 static struct {
-	const char *ext;
-	const char *mtype;
+	char *ext;
+	char *mtype;
 } mimetypes[] = {
 	{ "png", "image/png" },
+	{ "xml", "text/xml" },
 	{ "jpg", "image/jpeg" },
 	{ "js", "application/x-javascript" },
 	{ "wav", "audio/x-wav" },
@@ -114,32 +115,52 @@
 
 static AST_RWLIST_HEAD_STATIC(uri_redirects, http_uri_redirect);
 
-static const char *ftype2mtype(const char *ftype, char *wkspace, int wkspacelen)
+const struct  ast_cfhttp_methods_text {
+	enum ast_http_method method;
+	char * const text;
+} ast_http_methods_text[] = {
+	{ AST_HTTP_UNKNOWN,     "UNKNOWN" },
+	{ AST_HTTP_GET,         "GET" },
+	{ AST_HTTP_POST,        "POST" },
+	{ AST_HTTP_HEAD,        "HEAD" },
+	{ AST_HTTP_PUT,         "PUT" }
+};
+
+/*! \brief Return http method name string */
+char *ast_get_http_method(enum ast_http_method method) {
+	return ast_http_methods_text[method].text;
+}
+
+
+/*! \brief Return mime type based on extension */
+char *ast_ftype2mtype(const char *ftype)
 {
 	int x;
 
 	if (ftype) {
-		for (x = 0; x < ARRAY_LEN(mimetypes); x++) {
+		for (x = 0;x < ARRAY_LEN(mimetypes); x++) {
 			if (!strcasecmp(ftype, mimetypes[x].ext)) {
 				return mimetypes[x].mtype;
 			}
 		}
 	}
-
-	snprintf(wkspace, wkspacelen, "text/%s", S_OR(ftype, "plain"));
-
-	return wkspace;
+	return NULL;
 }
 
-static uint32_t manid_from_vars(struct ast_variable *sid) {
-	uint32_t mngid;
-
-	while (sid && strcmp(sid->name, "mansession_id"))
-		sid = sid->next;
+uint32_t manid_from_vars(struct ast_variable *headers) {
+	uint32_t mngid = 0;
+	struct ast_variable *v, *cookies;
 	
-	if (!sid || sscanf(sid->value, "%x", &mngid) != 1)
-		return 0;
-	
+	cookies = ast_http_get_cookies(headers);
+	for (v = cookies; v; v = v->next) {
+		if (!strcasecmp(v->name, "mansession_id")) {
+			sscanf(v->value, "%x", &mngid);
+			break;
+		}
+	}
+	if (cookies) {
+		ast_variables_destroy(cookies);
+	}
 	return mngid;
 }
 
@@ -150,30 +171,36 @@
 	}
 }
 
-static struct ast_str *static_callback(struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *vars, struct ast_variable *headers, int *status, char **title, int *contentlength)
+static int static_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_vars, struct ast_variable *headers)
 {
 	char *path;
 	char *ftype;
-	const char *mtype;
+	char *mtype;
 	char wkspace[80];
 	struct stat st;
 	int len;
 	int fd;
-	struct timeval now = ast_tvnow();
-	char buf[256];
+	struct ast_str *http_header;
+	struct timeval tv;
 	struct ast_tm tm;
+	char timebuf[80], etag[20];
+	struct ast_variable *v;
+	int not_modified = 0;
+	
+	if (method != AST_HTTP_GET && method != AST_HTTP_HEAD) {
+		ast_http_error(ser, 501, "Not Implemented", "Attempt to use unimplemented / unsupported method");
+		return -1;
+	}
 
 	/* Yuck.  I'm not really sold on this, but if you don't deliver static content it makes your configuration 
 	   substantially more challenging, but this seems like a rather irritating feature creep on Asterisk. */
 	if (!enablestatic || ast_strlen_zero(uri)) {
 		goto out403;
 	}
-
 	/* Disallow any funny filenames at all */
 	if ((uri[0] < 33) || strchr("./|~@#$%^&*() \t", uri[0])) {
 		goto out403;
 	}
-
 	if (strstr(uri, "/..")) {
 		goto out403;
 	}
@@ -181,8 +208,10 @@
 	if ((ftype = strrchr(uri, '.'))) {
 		ftype++;
 	}
-
-	mtype = ftype2mtype(ftype, wkspace, sizeof(wkspace));
+	mtype = ast_ftype2mtype(ftype);
+	if (!mtype) {
+		snprintf(wkspace, sizeof(wkspace), "text/%s", ftype ? ftype : "plain");
+	}
 	
 	/* Cap maximum length */
 	if ((len = strlen(uri) + strlen(ast_config_AST_DATA_DIR) + strlen("/static-http/") + 5) > 1024) {
@@ -194,100 +223,115 @@
 	if (stat(path, &st)) {
 		goto out404;
 	}
-
 	if (S_ISDIR(st.st_mode)) {
 		goto out404;
 	}	
-
-	if ((fd = open(path, O_RDONLY)) < 0) {
+	fd = open(path, O_RDONLY);
+	if (fd < 0) {
 		goto out403;
 	}
 
-	if (strstr(path, "/private/") && !astman_is_authed(manid_from_vars(vars))) {
+	if (strstr(path, "/private/") && !astman_is_authed(manid_from_vars(headers))) {
 		goto out403;
 	}
 
-	ast_strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S %Z", ast_localtime(&now, &tm, "GMT"));
-	fprintf(ser->f, "HTTP/1.1 200 OK\r\n"
-		"Server: Asterisk/%s\r\n"
-		"Date: %s\r\n"
-		"Connection: close\r\n"
-		"Cache-Control: no-cache, no-store\r\n"
-		"Content-Length: %d\r\n"
-		"Content-type: %s\r\n\r\n",
-		ast_get_version(), buf, (int) st.st_size, mtype);
+	/* make "Etag:" http header value */
+	sprintf(etag, "\"%d\"", st.st_mtime);
 
-	while ((len = read(fd, buf, sizeof(buf))) > 0) {
-		fwrite(buf, 1, len, ser->f);
+	/* make "Last-Modified:" http header value */
+	tv.tv_sec = st.st_mtime;
+	tv.tv_usec = 0;
+	ast_strftime(timebuf, sizeof(timebuf), "%a, %d %b %Y %H:%M:%S GMT", ast_localtime(&tv, &tm, "GMT"));
+	
+	/* check received "If-None-Match" request header and Etag value for file */
+	for (v = headers; v; v = v->next) {
+		if (!strcasecmp(v->name, "If-None-Match")) {
+			if (!strcasecmp(v->value, etag)) {
+				not_modified = 1;
+			}
+			break;
+		}
 	}
 
+	if ( (http_header = ast_str_create(255)) == NULL) {
+		return -1;
+	}
+
+	ast_str_set(&http_header, 0, "Content-type: %s\r\n"
+		"ETag: %s\r\n"
+		"Last-Modified: %s",
+		mtype,
+		etag,
+		timebuf);
+	
+	if (not_modified) {
+		ast_http_send(ser, method, 304, "Not Modified", http_header, NULL, 0, 1);
+	} else {
+		ast_http_send(ser, method, 200, NULL, http_header, NULL, fd, 1); /* static content flag is set */
+	}
 	close(fd);
+	return 0;
 
-	return NULL;
-
 out404:
-	return ast_http_error((*status = 404),
-			      (*title = ast_strdup("Not Found")),
-			       NULL, "The requested URL was not found on this server.");
+	ast_http_error(ser, 404, "Not Found", "The requested URL was not found on this server.");
+	return -1;
 
 out403:
-	return ast_http_error((*status = 403),
-			      (*title = ast_strdup("Access Denied")),
-			      NULL, "You do not have permission to access the requested URL.");
+	ast_http_error(ser, 403, "Access Denied", "You do not have permission to access the requested URL.");
+	return -1;
 }
 
-
-static struct ast_str *httpstatus_callback(struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *vars, struct ast_variable *headers, int *status, char **title, int *contentlength)
+static int httpstatus_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_vars, struct ast_variable *headers)
 {
-	struct ast_str *out = ast_str_create(512);
-	struct ast_variable *v;
+	struct ast_str *out;
+	struct ast_variable *v, *cookies = NULL;
 
-	if (out == NULL) {
-		return out;
+	if (method != AST_HTTP_GET && method != AST_HTTP_HEAD) {
+		ast_http_error(ser, 501, "Not Implemented", "Attempt to use unimplemented / unsupported method");
+		return -1;
 	}
+	
+	if ( (out = ast_str_create(512)) == NULL) {
+		return -1;
+	}
 
 	ast_str_append(&out, 0,
-		       "\r\n"
-		       "<title>Asterisk HTTP Status</title>\r\n"
-		       "<body bgcolor=\"#ffffff\">\r\n"
-		       "<table bgcolor=\"#f1f1f1\" align=\"center\"><tr><td bgcolor=\"#e0e0ff\" colspan=\"2\" width=\"500\">\r\n"
-		       "<h2>&nbsp;&nbsp;Asterisk&trade; HTTP Status</h2></td></tr>\r\n");
+		"<title>Asterisk HTTP Status</title>\r\n"
+		"<body bgcolor=\"#ffffff\">\r\n"
+		"<table bgcolor=\"#f1f1f1\" align=\"center\"><tr><td bgcolor=\"#e0e0ff\" colspan=\"2\" width=\"500\">\r\n"
+		"<h2>&nbsp;&nbsp;Asterisk&trade; HTTP Status</h2></td></tr>\r\n");
+
 	ast_str_append(&out, 0, "<tr><td><i>Prefix</i></td><td><b>%s</b></td></tr>\r\n", prefix);
 	ast_str_append(&out, 0, "<tr><td><i>Bind Address</i></td><td><b>%s</b></td></tr>\r\n",
-		       ast_inet_ntoa(http_desc.oldsin.sin_addr));
+			ast_inet_ntoa(http_desc.oldsin.sin_addr));
 	ast_str_append(&out, 0, "<tr><td><i>Bind Port</i></td><td><b>%d</b></td></tr>\r\n",
-		       ntohs(http_desc.oldsin.sin_port));
-
+			ntohs(http_desc.oldsin.sin_port));
 	if (http_tls_cfg.enabled) {
 		ast_str_append(&out, 0, "<tr><td><i>SSL Bind Port</i></td><td><b>%d</b></td></tr>\r\n",
-			       ntohs(https_desc.oldsin.sin_port));
+			ntohs(https_desc.oldsin.sin_port));
 	}
-
 	ast_str_append(&out, 0, "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
-
-	for (v = vars; v; v = v->next) {
-		if (strncasecmp(v->name, "cookie_", 7)) {
-			ast_str_append(&out, 0, "<tr><td><i>Submitted Variable '%s'</i></td><td>%s</td></tr>\r\n", v->name, v->value);
-		}
+	for (v = get_vars; v; v = v->next) {
+		ast_str_append(&out, 0, "<tr><td><i>Submitted GET Variable '%s'</i></td><td>%s</td></tr>\r\n", v->name, v->value);
 	}
-
 	ast_str_append(&out, 0, "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
 
-	for (v = vars; v; v = v->next) {
-		if (!strncasecmp(v->name, "cookie_", 7)) {
-			ast_str_append(&out, 0, "<tr><td><i>Cookie '%s'</i></td><td>%s</td></tr>\r\n", v->name, v->value);
-		}
+	cookies = ast_http_get_cookies(headers);
+	for (v = cookies; v; v = v->next) {
+		ast_str_append(&out, 0, "<tr><td><i>Cookie '%s'</i></td><td>%s</td></tr>\r\n", v->name, v->value);
 	}
+	ast_variables_destroy(cookies);
 
 	ast_str_append(&out, 0, "</table><center><font size=\"-1\"><i>Asterisk and Digium are registered trademarks of Digium, Inc.</i></font></center></body>\r\n");
-	return out;
+	ast_http_send(ser, method, 200, NULL, NULL, out, 0, 0);
+	return 0;
 }
 
 static struct ast_http_uri statusuri = {
 	.callback = httpstatus_callback,
 	.description = "Asterisk HTTP General Status",
 	.uri = "httpstatus",
-	.supports_get = 1,
+	.has_subtree = 0,
 	.data = NULL,
 	.key = __FILE__,
 };
@@ -297,36 +341,139 @@
 	.description = "Asterisk HTTP Static Delivery",
 	.uri = "static",
 	.has_subtree = 1,
-	.static_content = 1,
-	.supports_get = 1,
 	.data = NULL,
 	.key= __FILE__,
 };
+
+
+/* send http/1.1 responce */
+/* free content variable and close socket*/
+void ast_http_send(struct ast_tcptls_session_instance *ser, enum ast_http_method method, int status_code, const char *status_title, struct ast_str *http_header, struct ast_str *out, const int fd, unsigned int static_content)
+{
+	struct timeval now = ast_tvnow();
+	struct ast_tm tm;
+	char timebuf[80];
+	int content_length = 0;
+
+	if (!ser || 0 == ser->f) {
+		return;
+	}
+
+	ast_strftime(timebuf, sizeof(timebuf), "%a, %d %b %Y %H:%M:%S GMT", ast_localtime(&now, &tm, "GMT"));
+
+	/* calc conetnt length */
+	if (out) {
+		content_length += strlen(out->str);
+	}
 	
-struct ast_str *ast_http_error(int status, const char *title, const char *extra_header, const char *text)
+	if (fd) {
+		content_length += lseek(fd, 0, SEEK_END);
+		lseek(fd, 0, SEEK_SET);
+	}
+	
+	/* send http header */
+	fprintf(ser->f, "HTTP/1.1 %d %s\r\n"
+		"Server: Asterisk/%s\r\n"
+		"Date: %s\r\n"
+		"Connection: close\r\n"
+		"%s"
+		"Content-Length: %d\r\n"
+		"%s\r\n\r\n",
+		status_code, status_title ? status_title : "OK",
+		ast_get_version(),
+		timebuf,
+		static_content ? "" : "Cache-Control: no-cache, no-store\r\n",
+		content_length,
+		http_header ? http_header->str : ""
+		);
+
+	/* send content */
+	if (method != AST_HTTP_HEAD || status_code >= 400) {
+		if (out) {
+			fprintf(ser->f, "%s", out->str);
+		}
+
+		if (fd) {
+			char buf[256];
+			int len;
+			while ((len = read(fd, buf, sizeof(buf))) > 0) {
+				fwrite(buf, len, 1, ser->f);
+			}
+		}
+	}
+	
+	if (http_header) {
+		ast_free(http_header);
+	}
+	if (out) {
+		ast_free(out);
+	}
+	
+	fclose(ser->f);
+	ser->f = 0;
+	return;
+}
+
+/* Send http "401 Unauthorized" responce and close socket*/
+void ast_http_auth(struct ast_tcptls_session_instance *ser, const char *realm, const unsigned long nonce, const unsigned long opaque, int stale, const char *text)
 {
+	struct ast_str *http_headers = ast_str_create(128);
 	struct ast_str *out = ast_str_create(512);
+	
+	if (http_headers == NULL || out == NULL) {
+		return;
+	}
 
-	if (out == NULL) {
-		return out;
+	ast_str_set(&http_headers, 0,
+		"WWW-authenticate: Digest algorithm=MD5, realm=\"%s\", nonce=\"%08lx\", qop=\"auth\", opaque=\"%08lx\"%s\r\n"
+		"Content-type: text/html",
+		realm ? realm : "Asterisk",
+		nonce,
+		opaque,
+		stale ? ", stale=true" : "");
+
+	ast_str_set(&out, 0,
+		"<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n"
+		"<html><head>\r\n"
+		"<title>401 Unauthorized</title>\r\n"
+		"</head><body>\r\n"
+		"<h1>401 Unauthorized</h1>\r\n"
+		"<p>%s</p>\r\n"
+		"<hr />\r\n"
+		"<address>Asterisk Server</address>\r\n"
+		"</body></html>\r\n",
+		text ? text : "");
+	
+	ast_http_send(ser, AST_HTTP_UNKNOWN, 401, "Unauthorized", http_headers, out, 0, 0);
+	return;
+}
+
+/* send http error responce and close socket*/
+void ast_http_error(struct ast_tcptls_session_instance *ser, int status_code, const char *status_title, const char *text)
+{
+	struct ast_str *http_headers = ast_str_create(40);
+	struct ast_str *out = ast_str_create(256);
+	
+	if (http_headers == NULL && out == NULL) {
+		return;
 	}
 
+	ast_str_set(&http_headers, 0, "Content-type: text/html");
+	
 	ast_str_set(&out, 0,
-		    "Content-type: text/html\r\n"
-		    "%s"
-		    "\r\n"
-		    "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n"
-		    "<html><head>\r\n"
-		    "<title>%d %s</title>\r\n"
-		    "</head><body>\r\n"
-		    "<h1>%s</h1>\r\n"
-		    "<p>%s</p>\r\n"
-		    "<hr />\r\n"
-		    "<address>Asterisk Server</address>\r\n"
-		    "</body></html>\r\n",
-		    (extra_header ? extra_header : ""), status, title, title, text);
-
-	return out;
+		"<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n"
+		"<html><head>\r\n"
+		"<title>%d %s</title>\r\n"
+		"</head><body>\r\n"
+		"<h1>%s</h1>\r\n"
+		"<p>%s</p>\r\n"
+		"<hr />\r\n"
+		"<address>Asterisk Server</address>\r\n"
+		"</body></html>\r\n",
+			status_code, status_title, status_title, text);
+	
+	ast_http_send(ser, AST_HTTP_UNKNOWN, status_code, status_title, http_headers, out, 0, 0);
+	return;
 }
 
 /*! \brief 
@@ -342,27 +489,20 @@
 {
 	struct ast_http_uri *uri;
 	int len = strlen(urih->uri);
-
-	if (!(urih->supports_get || urih->supports_post)) {
-		ast_log(LOG_WARNING, "URI handler does not provide either GET or POST method: %s (%s)\n", urih->uri, urih->description);
-		return -1;
-	}
-
+	
 	AST_RWLIST_WRLOCK(&uris);
 
-	if (AST_RWLIST_EMPTY(&uris) || strlen(AST_RWLIST_FIRST(&uris)->uri) <= len) {
+	if ( AST_RWLIST_EMPTY(&uris) || strlen(AST_RWLIST_FIRST(&uris)->uri) <= len ) {
 		AST_RWLIST_INSERT_HEAD(&uris, urih, entry);
 		AST_RWLIST_UNLOCK(&uris);
-
 		return 0;
 	}
 
 	AST_RWLIST_TRAVERSE(&uris, uri, entry) {
-		if (AST_RWLIST_NEXT(uri, entry) &&
-		    strlen(AST_RWLIST_NEXT(uri, entry)->uri) <= len) {
+		if ( AST_RWLIST_NEXT(uri, entry) 
+			&& strlen(AST_RWLIST_NEXT(uri, entry)->uri) <= len ) {
 			AST_RWLIST_INSERT_AFTER(&uris, uri, urih, entry);
 			AST_RWLIST_UNLOCK(&uris); 
-
 			return 0;
 		}
 	}
@@ -417,82 +557,109 @@
 	ast_uri_decode(s);
 }
 
-static struct ast_str *handle_uri(struct ast_tcptls_session_instance *ser, char *uri, enum ast_http_method method,
-				  int *status, char **title, int *contentlength, struct ast_variable **cookies, struct ast_variable *headers, 
-				  unsigned int *static_content)
+/*
+ * get post variables from client Request Entity-Body, if content type is
+ * application/x-www-form-urlencoded
+ */
+struct ast_variable *get_post_vars(struct ast_tcptls_session_instance *ser, struct ast_variable *headers) {
+	int content_length = 0;
+	struct ast_variable *v, *post_vars=NULL, *prev = NULL;
+	char *buf, *var, *val;
+
+	for (v = headers; v; v = v->next) {
+		if (!strcasecmp(v->name, "Content-Type")) {
+			if (strcasecmp(v->value, "application/x-www-form-urlencoded")) {
+				return NULL;
+			}
+			break;
+		}
+	}
+	
+	for (v = headers; v; v = v->next) {
+		if (!strcasecmp(v->name, "Content-Length")) {
+			content_length = atoi(v->value) + 1;
+			break;
+		}
+	}
+
+	if (!content_length) {
+		return NULL;
+	}
+	
+	if (!(buf = alloca(content_length))) {
+		return NULL;
+	}
+	if (!fgets(buf, content_length, ser->f)) {
+		return NULL;
+	}
+
+	while ((val = strsep(&buf, "&"))) {
+		var = strsep(&val, "=");
+		if (val) {
+			http_decode(val);
+		} else  {
+			val = "";
+		}
+		http_decode(var);
+		if ((v = ast_variable_new(var, val, ""))) {
+			if (post_vars) {
+				prev->next = v;
+			} else {
+				post_vars = v;
+			}
+			prev = v;
+		}
+	}
+	return post_vars;
+}
+
+static int handle_uri(struct ast_tcptls_session_instance *ser, char *uri, enum ast_http_method method, 
+	struct ast_variable *headers)
 {
 	char *c;
-	struct ast_str *out = NULL;
+	int res = -1;
 	char *params = uri;
-	struct ast_http_uri *urih = NULL;
+	struct ast_http_uri *urih=NULL;
 	int l;
-	struct ast_variable *vars = NULL, *v, *prev = NULL;
+	struct ast_variable *get_vars=NULL, *v, *prev = NULL;
 	struct http_uri_redirect *redirect;
-	int saw_method = 0;
 
-	/* preserve previous behavior of only support URI parameters on GET requests */
-	if (method == AST_HTTP_GET) {
-		strsep(&params, "?");
-		
-		/* Extract arguments from the request and store them in variables.
-		 * Note that a request can have multiple arguments with the same
-		 * name, and we store them all in the list of variables.
-		 * It is up to the application to handle multiple values.
-		 */
-		if (params) {
-			char *var, *val;
-			
-			while ((val = strsep(&params, "&"))) {
-				var = strsep(&val, "=");
-				if (val) {
-					http_decode(val);
+	strsep(&params, "?");
+	/* Extract arguments from the request and store them in variables. */
+	if (params) {
+		char *var, *val;
+
+		while ((val = strsep(&params, "&"))) {
+			var = strsep(&val, "=");
+			if (val) {
+				http_decode(val);
+			} else  {
+				val = "";
+			}
+			http_decode(var);
+			if ((v = ast_variable_new(var, val, ""))) {
+				if (get_vars) {
+					prev->next = v;
 				} else {
-					val = "";
+					get_vars = v;
 				}
-				http_decode(var);
-				if ((v = ast_variable_new(var, val, ""))) {
-					if (vars) {
-						prev->next = v;
-					} else {
-						vars = v;
-					}
-					prev = v;
-				}
+				prev = v;
 			}
 		}
 	}
-
-	/*
-	 * Append the cookies to the list of variables.
-	 * This saves a pass in the cookies list, but has the side effect
-	 * that a variable might mask a cookie with the same name if the
-	 * application stops at the first match.
-	 * Note that this is the same behaviour as $_REQUEST variables in PHP.
-	 */
-	if (prev) {
-		prev->next = *cookies;
-	} else {
-		vars = *cookies;
-	}
-	*cookies = NULL;
-
 	http_decode(uri);
 
 	AST_RWLIST_RDLOCK(&uri_redirects);
 	AST_RWLIST_TRAVERSE(&uri_redirects, redirect, entry) {
 		if (!strcasecmp(uri, redirect->target)) {
-			char buf[512];
+			struct ast_str *http_header = ast_str_create(128);
+			ast_str_set(&http_header, 0, "Location: %s\r\n", redirect->dest);
+			ast_http_send(ser, method, 302, "Moved Temporarily", http_header, NULL, 0, 0);
 
-			snprintf(buf, sizeof(buf), "Location: %s\r\n", redirect->dest);
-			out = ast_http_error((*status = 302),
-					     (*title = ast_strdup("Moved Temporarily")),
-					     buf, "Redirecting...");
-
 			break;
 		}
 	}
 	AST_RWLIST_UNLOCK(&uri_redirects);
-
 	if (redirect) {
 		goto cleanup;
 	}
@@ -505,70 +672,31 @@
 		AST_RWLIST_RDLOCK(&uris);
 		AST_RWLIST_TRAVERSE(&uris, urih, entry) {
 			ast_debug(2, "match request [%s] with handler [%s] len %d\n", uri, urih->uri, l);
-			if (!saw_method) {
-				switch (method) {
-				case AST_HTTP_GET:
-					if (urih->supports_get) {
-						saw_method = 1;
-					}
-					break;
-				case AST_HTTP_POST:
-					if (urih->supports_post) {
-						saw_method = 1;
-					}
-					break;
-				}
-			}
-
 			l = strlen(urih->uri);
 			c = uri + l;	/* candidate */
-
-			if (strncasecmp(urih->uri, uri, l) || /* no match */
-			    (*c && *c != '/')) { /* substring */
+			if (strncasecmp(urih->uri, uri, l) /* no match */
+			    || (*c && *c != '/')) { /* substring */ 
 				continue;
 			}
-
 			if (*c == '/') {
 				c++;
 			}
-
 			if (!*c || urih->has_subtree) {
-				if (((method == AST_HTTP_GET) && urih->supports_get) ||
-				    ((method == AST_HTTP_POST) && urih->supports_post)) {
-					uri = c;
-
-					break;
-				}
+				uri = c;
+				break;
 			}
 		}
-
-		if (!urih) {
-			AST_RWLIST_UNLOCK(&uris);
-		}
+		AST_RWLIST_UNLOCK(&uris);
 	}
-
-	if (method == AST_HTTP_POST && !astman_is_authed(manid_from_vars(vars))) {
-		out = ast_http_error((*status = 403),
-			      (*title = ast_strdup("Access Denied")),
-			      NULL, "You do not have permission to access the requested URL.");
-	} else if (urih) {
-		*static_content = urih->static_content;
-		out = urih->callback(ser, urih, uri, method, vars, headers, status, title, contentlength);
-		AST_RWLIST_UNLOCK(&uris);
-	} else if (saw_method) {
-		out = ast_http_error((*status = 404),
-				     (*title = ast_strdup("Not Found")), NULL,
-				     "The requested URL was not found on this server.");
+	if (urih) {
+		res = urih->callback(ser, urih, uri, method, get_vars, headers);
 	} else {
-		out = ast_http_error((*status = 501),
-				     (*title = ast_strdup("Not Implemented")), NULL,
-				     "Attempt to use unimplemented / unsupported method");
+		ast_http_error(ser, 404, "Not Found", "The requested URL was not found on this server.");
 	}
 
 cleanup:
-	ast_variables_destroy(vars);
-
-	return out;
+	ast_variables_destroy(get_vars);
+	return res;
 }
 
 #ifdef DO_SSL
@@ -579,7 +707,6 @@
 #define HOOK_T ssize_t
 #define LEN_T size_t
 #endif
-
 /*!
  * replacement read/write functions for SSL support.
  * We use wrappers rather than SSL_read/SSL_write directly so
@@ -621,9 +748,6 @@
 	char *cur;
 	struct ast_variable *vars = NULL, *var;
 
-	/* Skip Cookie: */
-	cookies += 8;
-
 	while ((cur = strsep(&cookies, ";"))) {
 		char *name, *val;
 		
@@ -653,128 +777,117 @@
 	return vars;
 }
 
+/* get cookie from Request headers */
+struct ast_variable *ast_http_get_cookies(struct ast_variable *headers)
+{
+	struct ast_variable *v, *cookies=NULL;
+	
+	for (v = headers; v; v = v->next) {
+                if (!strncasecmp(v->name, "Cookie", 6)) {
+			if (cookies) { 
+				ast_variables_destroy(cookies); 
+			}
+
+			cookies = parse_cookies((char *)v->value);
+		}
+	}
+	return cookies;
+}
+
+
 static void *httpd_helper_thread(void *data)
 {
 	char buf[4096];
-	char cookie[4096];
+	char header_line[4096];
 	struct ast_tcptls_session_instance *ser = data;
-	struct ast_variable *vars=NULL, *headers = NULL;
-	char *uri, *title=NULL;
-	int status = 200, contentlength = 0;
-	struct ast_str *out = NULL;
-	unsigned int static_content = 0;
+	struct ast_variable *headers = NULL;
 	struct ast_variable *tail = headers;
+	char *uri, *method;
+	enum ast_http_method http_method = AST_HTTP_UNKNOWN;
 
 	if (!fgets(buf, sizeof(buf), ser->f)) {
 		goto done;
 	}
 
-	uri = ast_skip_nonblanks(buf);	/* Skip method */
+	/* Get method */
+	method = ast_skip_blanks(buf);
+	uri = ast_skip_nonblanks(method);
 	if (*uri) {
 		*uri++ = '\0';
 	}
 
+	if (!strcasecmp(method,"GET")) {
+		http_method = AST_HTTP_GET;
+	} else if (!strcasecmp(method,"POST")) {
+		http_method = AST_HTTP_POST;
+	} else if (!strcasecmp(method,"HEAD")) {
+		http_method = AST_HTTP_HEAD;
+	} else if (!strcasecmp(method,"PUT")) {
+		http_method = AST_HTTP_PUT;
+	}
+
 	uri = ast_skip_blanks(uri);	/* Skip white space */
 
 	if (*uri) {			/* terminate at the first blank */
 		char *c = ast_skip_nonblanks(uri);
-
 		if (*c) {
 			*c = '\0';
 		}
 	}
 
-	/* process "Cookie: " lines */
-	while (fgets(cookie, sizeof(cookie), ser->f)) {
+	/* process "Request Headers" lines */
+	while (fgets(header_line, sizeof(header_line), ser->f)) {
+		char *name, *value;
+
 		/* Trim trailing characters */
-		ast_trim_blanks(cookie);
-		if (ast_strlen_zero(cookie)) {
+		ast_trim_blanks(header_line);
+		if (ast_strlen_zero(header_line)) {
 			break;
 		}
-		if (!strncasecmp(cookie, "Cookie: ", 8)) {
-			vars = parse_cookies(cookie);
-		} else {
-			char *name, *val;
+	
+		/* Are ast_strdupa is needed here ? */
+		/* ast_variable_new allocate memory himself */
+		/* value = ast_strdupa(header_line); */
+		value = header_line;
+		name = strsep(&value, ":");
+		if (!value) {
+			continue;
+		}
 
-			val = cookie;
-			name = strsep(&val, ":");
-			if (ast_strlen_zero(name) || ast_strlen_zero(val)) {
-				continue;
-			}
-			ast_trim_blanks(name);
-			val = ast_skip_blanks(val);
+		value = ast_skip_blanks(value);
+		if (ast_strlen_zero(value) || ast_strlen_zero(name)) {
+			continue;
+		}
 
-			if (!headers) {
-				headers = ast_variable_new(name, val, __FILE__);
-				tail = headers;
-			} else {
-				tail->next = ast_variable_new(name, val, __FILE__);
-				tail = tail->next;
-			}
+		ast_trim_blanks(name);
+
+		if (!headers) {
+			headers = ast_variable_new(name, value, __FILE__);
+			tail = headers;
+		} else {
+			tail->next = ast_variable_new(name, value, __FILE__);
+			tail = tail->next;
 		}
 	}
 
 	if (!*uri) {
-		out = ast_http_error(400, "Bad Request", NULL, "Invalid Request");
-	} else if (strcasecmp(buf, "post") && strcasecmp(buf, "get")) {
-		out = ast_http_error(501, "Not Implemented", NULL,
-				     "Attempt to use unimplemented / unsupported method");
-	} else {	/* try to serve it */
-		out = handle_uri(ser, uri, (!strcasecmp(buf, "get")) ? AST_HTTP_GET : AST_HTTP_POST,
-				 &status, &title, &contentlength, &vars, headers, &static_content);
+		ast_http_error(ser, 400, "Bad Request", "Invalid Request");
+		return NULL;
 	}
+	
+	handle_uri(ser, uri, http_method, headers);
 
-	/* If they aren't mopped up already, clean up the cookies */
-	if (vars) {
-		ast_variables_destroy(vars);
-	}
 	/* Clean up all the header information pulled as well */
 	if (headers) {
 		ast_variables_destroy(headers);
 	}
 
-	if (out) {
-		struct timeval now = ast_tvnow();
-		char timebuf[256];
-		struct ast_tm tm;
-
-		ast_strftime(timebuf, sizeof(timebuf), "%a, %d %b %Y %H:%M:%S %Z", ast_localtime(&now, &tm, "GMT"));
-		fprintf(ser->f,
-			"HTTP/1.1 %d %s\r\n"
-			"Server: Asterisk/%s\r\n"
-			"Date: %s\r\n"
-			"Connection: close\r\n"
-			"%s",
-			status, title ? title : "OK", ast_get_version(), timebuf,
-			static_content ? "" : "Cache-Control: no-cache, no-store\r\n");
-			/* We set the no-cache headers only for dynamic content.
-			* If you want to make sure the static file you requested is not from cache,
-			* append a random variable to your GET request.  Ex: 'something.html?r=109987734'
-			*/
-		if (!contentlength) {	/* opaque body ? just dump it hoping it is properly formatted */
-			fprintf(ser->f, "%s", out->str);
-		} else {
-			char *tmp = strstr(out->str, "\r\n\r\n");
-
-			if (tmp) {
-				fprintf(ser->f, "Content-length: %d\r\n", contentlength);
-				/* first write the header, then the body */
-				fwrite(out->str, 1, (tmp + 4 - out->str), ser->f);
-				fwrite(tmp + 4, 1, contentlength, ser->f);
-			}
-		}
-		ast_free(out);
+done:
+	if (ser->f) {
+		fclose(ser->f);
 	}
-
-	if (title) {
-		ast_free(title);
-	}
-
-done:
-	fclose(ser->f);
 	ao2_ref(ser, -1);
 	ser = NULL;
-
 	return NULL;
 }
 
@@ -807,7 +920,6 @@
 	if (!(redirect = ast_calloc(1, total_len))) {
 		return;
 	}
-
 	redirect->dest = redirect->target + target_len;
 	strcpy(redirect->target, target);
 	strcpy(redirect->dest, dest);
@@ -815,20 +927,18 @@
 	AST_RWLIST_WRLOCK(&uri_redirects);
 
 	target_len--; /* So we can compare directly with strlen() */
-	if (AST_RWLIST_EMPTY(&uri_redirects) 
-	    || strlen(AST_RWLIST_FIRST(&uri_redirects)->target) <= target_len) {
+	if ( AST_RWLIST_EMPTY(&uri_redirects) 
+		|| strlen(AST_RWLIST_FIRST(&uri_redirects)->target) <= target_len ) {
 		AST_RWLIST_INSERT_HEAD(&uri_redirects, redirect, entry);
 		AST_RWLIST_UNLOCK(&uri_redirects);
-
 		return;
 	}
 
 	AST_RWLIST_TRAVERSE(&uri_redirects, cur, entry) {
-		if (AST_RWLIST_NEXT(cur, entry) 
-		    && strlen(AST_RWLIST_NEXT(cur, entry)->target) <= target_len) {
+		if ( AST_RWLIST_NEXT(cur, entry) 
+			&& strlen(AST_RWLIST_NEXT(cur, entry)->target) <= target_len ) {
 			AST_RWLIST_INSERT_AFTER(&uri_redirects, cur, redirect, entry);
 			AST_RWLIST_UNLOCK(&uri_redirects); 
-
 			return;
 		}
 	}
@@ -927,7 +1037,6 @@
 
 		ast_config_destroy(cfg);
 	}
-
 	if (!have_sslbindaddr) {
 		https_desc.sin.sin_addr = http_desc.sin.sin_addr;
 	}
@@ -942,7 +1051,7 @@
 	if (ast_ssl_setup(https_desc.tls_cfg)) {
 		ast_tcptls_server_start(&https_desc);
 	}
-
+	
 	return 0;
 }
 
@@ -985,17 +1094,15 @@
 	if (AST_RWLIST_EMPTY(&uris)) {
 		ast_cli(a->fd, "None.\n");
 	} else {
-		AST_RWLIST_TRAVERSE(&uris, urih, entry) {
-			ast_cli(a->fd, "%s/%s%s => %s\n", prefix, urih->uri, (urih->has_subtree ? "/..." : ""), urih->description);
-		}
+		AST_RWLIST_TRAVERSE(&uris, urih, entry)
+			ast_cli(a->fd, "%s/%s%s => %s\n", prefix, urih->uri, (urih->has_subtree ? "/..." : "" ), urih->description);
 	}
 	AST_RWLIST_UNLOCK(&uris);
 
 	ast_cli(a->fd, "\nEnabled Redirects:\n");
 	AST_RWLIST_RDLOCK(&uri_redirects);
-	AST_RWLIST_TRAVERSE(&uri_redirects, redirect, entry) {
+	AST_RWLIST_TRAVERSE(&uri_redirects, redirect, entry)
 		ast_cli(a->fd, "  %s => %s\n", redirect->target, redirect->dest);
-	}
 	if (AST_RWLIST_EMPTY(&uri_redirects)) {
 		ast_cli(a->fd, "  None.\n");
 	}
Index: res/res_phoneprov.c
===================================================================
--- res/res_phoneprov.c	(revision 148468)
+++ res/res_phoneprov.c	(working copy)
@@ -154,19 +154,6 @@
 static struct ao2_container *http_routes;
 static struct ao2_container *users;
 
-/*! \brief Extensions whose mime types we think we know */
-static struct {
-	char *ext;
-	char *mtype;
-} mimetypes[] = {
-	{ "png", "image/png" },
-	{ "xml", "text/xml" },
-	{ "jpg", "image/jpeg" },
-	{ "js", "application/x-javascript" },
-	{ "wav", "audio/x-wav" },
-	{ "mp3", "audio/mpeg" },
-};
-
 static char global_server[80] = "";	/*!< Server to substitute into templates */
 static char global_serverport[6] = "";	/*!< Server port to substitute into templates */
 static char global_default_profile[80] = "";	/*!< Default profile to use if one isn't specified */	
@@ -175,22 +162,7 @@
 static struct varshead global_variables;
 static ast_mutex_t globals_lock;
 
-/*! \brief Return mime type based on extension */
-static char *ftype2mtype(const char *ftype)
-{
-	int x;
 
-	if (ast_strlen_zero(ftype))
-		return NULL;
-
-	for (x = 0;x < ARRAY_LEN(mimetypes);x++) {
-		if (!strcasecmp(ftype, mimetypes[x].ext))
-			return mimetypes[x].mtype;
-	}
-	
-	return NULL;
-}
-
 /* iface is the interface (e.g. eth0); address is the return value */
 static int lookup_iface(const char *iface, struct in_addr *address)
 {
@@ -404,21 +376,25 @@
 }
 
 /*! \brief Callback that is executed everytime an http request is received by this module */
-static struct ast_str *phoneprov_callback(struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *vars, struct ast_variable *headers, int *status, char **title, int *contentlength)
+static int phoneprov_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_vars, struct ast_variable *headers)
 {
 	struct http_route *route;
 	struct http_route search_route = {
 		.uri = uri,
 	};
-	struct ast_str *result = ast_str_create(512);
+	struct ast_str *result;
 	char path[PATH_MAX];
 	char *file = NULL;
 	int len;
 	int fd;
 	char buf[256];
-	struct timeval now = ast_tvnow();
-	struct ast_tm tm;
+	struct ast_str *http_header;
 
+	if (method != AST_HTTP_GET && method != AST_HTTP_HEAD) {
+		ast_http_error(ser, 501, "Not Implemented", "Attempt to use unimplemented / unsupported method");
+		return -1;
+	}
+
 	if (!(route = ao2_find(http_routes, &search_route, OBJ_POINTER))) {
 		goto out404;
 	}
@@ -440,23 +416,18 @@
 			goto out500;
 		}
 
-		ast_strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S %Z", ast_localtime(&now, &tm, "GMT"));
-		fprintf(ser->f, "HTTP/1.1 200 OK\r\n"
-			"Server: Asterisk/%s\r\n"
-			"Date: %s\r\n"
-			"Connection: close\r\n"
-			"Cache-Control: no-cache, no-store\r\n"
-			"Content-Length: %d\r\n"
-			"Content-Type: %s\r\n\r\n",
-			ast_get_version(), buf, len, route->file->mime_type);
-		
+		http_header = ast_str_create(80);
+		ast_str_set(&http_header, 0, "Content-type: %s\r\n",
+			route->file->mime_type);
+
 		while ((len = read(fd, buf, sizeof(buf))) > 0) {
 			fwrite(buf, 1, len, ser->f);
 		}
 
+		ast_http_send(ser, method, 200, NULL, http_header, NULL, fd, 0);
 		close(fd);
 		route = unref_route(route);
-		return NULL;
+		return 0;
 	} else { /* Dynamic file */
 		int bufsize;
 		char *tmp;
@@ -515,33 +486,30 @@
 			ast_free(file);
 		}
 
-		ast_str_append(&result, 0,
-			"Content-Type: %s\r\n"
-			"Content-length: %d\r\n"
-			"\r\n"
-			"%s", route->file->mime_type, (int) strlen(tmp), tmp);
+		ast_str_set(&http_header, 0, "Content-type: %s\r\n",
+			route->file->mime_type);
 
+		result = ast_str_create(512);
+		ast_str_append(&result, 0, "%s", tmp); 
+
+		ast_http_send(ser, method, 200, NULL, http_header, result, 0, 0);
 		if (tmp) {
 			ast_free(tmp);
 		}
 
 		route = unref_route(route);
 
-		return result;
+		return 0;
 	}
 
 out404:
-	*status = 404;
-	*title = strdup("Not Found");
-	*contentlength = 0;
-	return ast_http_error(404, "Not Found", NULL, "The requested URL was not found on this server.");
+	ast_http_error(ser, 404, "Not Found", "Nothing to see here.  Move along.");
+	return -1;
 
 out500:
 	route = unref_route(route);
-	*status = 500;
-	*title = strdup("Internal Server Error");
-	*contentlength = 0;
-	return ast_http_error(500, "Internal Error", NULL, "An internal error has occured.");
+	ast_http_error(ser, 500, "Internal Error", "An internal error has occured.");
+	return -1;
 }
 
 /*! \brief Build a route structure and add it to the list of available http routes
@@ -655,7 +623,7 @@
 			 * 3) Default mime type specified in profile
 			 * 4) text/plain
 			 */
-			ast_string_field_set(pp_file, mime_type, S_OR(args.mimetype, (S_OR(S_OR(ftype2mtype(file_extension), profile->default_mime_type), "text/plain"))));
+			ast_string_field_set(pp_file, mime_type, S_OR(args.mimetype, (S_OR(S_OR(ast_ftype2mtype(file_extension), profile->default_mime_type), "text/plain"))));
 
 			if (!strcasecmp(v->name, "static_file")) {
 				ast_string_field_set(pp_file, format, args.filename);
@@ -1234,7 +1202,6 @@
 	.description = "Asterisk HTTP Phone Provisioning Tool",
 	.uri = "phoneprov",
 	.has_subtree = 1,
-	.supports_get = 1,
 	.data = NULL,
 	.key = __FILE__,
 };
Index: res/res_http_post.c
===================================================================
--- res/res_http_post.c	(revision 148468)
+++ res/res_http_post.c	(working copy)
@@ -153,9 +153,9 @@
 	return cbinfo.count;
 }
 
-static struct ast_str *http_post_callback(struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *vars, struct ast_variable *headers, int *status, char **title, int *contentlength)
+static int http_post_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_vars, struct ast_variable *headers)
 {
-	struct ast_variable *var;
+	struct ast_variable *var, *cookies;
 	unsigned long ident = 0;
 	char buf[4096];
 	FILE *f;
@@ -165,41 +165,45 @@
 	GMimeMessage *message;
 	int message_count = 0;
 
-	if (!urih) {
-		return ast_http_error((*status = 400),
-			   (*title = ast_strdup("Missing URI handle")),
-			   NULL, "There was an error parsing the request");
+	if (method != AST_HTTP_POST) {
+		ast_http_error(ser, 501, "Not Implemented", "Attempt to use unimplemented / unsupported method");
+		return -1;
 	}
+	
+	if (!astman_is_authed(manid_from_vars(headers))) {
+		ast_http_error(ser, 403, "Access Denied", "Sorry, I cannot let you do that, Dave.");
+		return -1;
+	}
 
-	for (var = vars; var; var = var->next) {
-		if (strcasecmp(var->name, "mansession_id")) {
-			continue;
+	if (!urih) {
+		ast_http_error(ser, 400, "Missing URI handle", "There was an error parsing the request");
+	        return -1;
+	}
+	
+	cookies = ast_http_get_cookies(headers);
+	for (var = cookies; var; var = var->next) {
+		if (!strcasecmp(var->name, "mansession_id")) {
+			sscanf(var->value, "%lx", &ident);
+			break;
 		}
-
-		if (sscanf(var->value, "%lx", &ident) != 1) {
-			return ast_http_error((*status = 400),
-					      (*title = ast_strdup("Bad Request")),
-					      NULL, "The was an error parsing the request.");
-		}
-
-		if (!astman_verify_session_writepermissions(ident, EVENT_FLAG_CONFIG)) {
-			return ast_http_error((*status = 401),
-					      (*title = ast_strdup("Unauthorized")),
-					      NULL, "You are not authorized to make this request.");
-		}
-
-		break;
 	}
+	if (cookies) {
+		ast_variables_destroy(cookies);
+	}
 
-	if (!var) {
-		return ast_http_error((*status = 401),
-				      (*title = ast_strdup("Unauthorized")),
-				      NULL, "You are not authorized to make this request.");
+	if (ident == 0) {
+		ast_http_error(ser, 401, "Unauthorized", "You are not authorized to make this request.");
+		return -1;
 	}
+	if (!astman_verify_session_writepermissions(ident, EVENT_FLAG_CONFIG)) {
+		ast_http_error(ser, 401, "Unauthorized", "You are not authorized to make this request.");
+		return -1;
+	}
 
 	if (!(f = tmpfile())) {
 		ast_log(LOG_ERROR, "Could not create temp file.\n");
-		return NULL;
+		ast_http_error(ser, 500, "Internal server error", "Could not create temp file.");
+		return -1;
 	}
 
 	for (var = headers; var; var = var->next) {
@@ -209,8 +213,8 @@
 			if ((sscanf(var->value, "%u", &content_len)) != 1) {
 				ast_log(LOG_ERROR, "Invalid Content-Length in POST request!\n");
 				fclose(f);
-
-				return NULL;
+				ast_http_error(ser, 500, "Internal server error", "Invalid Content-Length in POST request!");
+				return -1;
 			}
 			ast_debug(1, "Got a Content-Length of %d\n", content_len);
 		}
@@ -229,8 +233,8 @@
 	if (fseek(f, SEEK_SET, 0)) {
 		ast_log(LOG_ERROR, "Failed to seek temp file back to beginning.\n");
 		fclose(f);
-
-		return NULL;
+		ast_http_error(ser, 500, "Internal server error", "Failed to seek temp file back to beginning.");
+		return -1;
 	}
 
 	post_dir = urih->data;
@@ -240,24 +244,20 @@
 	if (!message) {
 		ast_log(LOG_ERROR, "Error parsing MIME data\n");
 
-		return ast_http_error((*status = 400),
-				      (*title = ast_strdup("Bad Request")),
-				      NULL, "The was an error parsing the request.");
+		ast_http_error(ser, 400, "Bad Request", "The was an error parsing the request.");
+		return -1;
 	}
 
 	if (!(message_count = process_message(message, post_dir->str))) {
 		ast_log(LOG_ERROR, "Invalid MIME data, found no parts!\n");
 		g_object_unref(message);
-		return ast_http_error((*status = 400),
-				      (*title = ast_strdup("Bad Request")),
-				      NULL, "The was an error parsing the request.");
+		ast_http_error(ser, 400, "Bad Request", "The was an error parsing the request.");
+		return -1;
 	}
+	g_object_unref(message);	
 
-	g_object_unref(message);
-
-	return ast_http_error((*status = 200),
-			      (*title = ast_strdup("OK")),
-			      NULL, "File successfully uploaded.");
+	ast_http_error(ser, 200, "OK", "File successfully uploaded.");
+	return 0;
 }
 
 static int __ast_http_post_load(int reload)
@@ -305,8 +305,6 @@
 			ast_str_set(&ds, 0, "%s/%s", prefix, v->value);
 			urih->data = ds;
 			urih->has_subtree = 0;
-			urih->supports_get = 0;
-			urih->supports_post = 1;
 			urih->callback = http_post_callback;
 			urih->key = __FILE__;
 			urih->mallocd = urih->dmallocd = 1;
