Index: channels/chan_sip.c
===================================================================
--- channels/chan_sip.c	(revision 99859)
+++ channels/chan_sip.c	(working copy)
@@ -355,6 +355,7 @@
 	AUTH_UNKNOWN_DOMAIN = -5,
 	AUTH_PEER_NOT_DYNAMIC = -6,
 	AUTH_ACL_FAILED = -7,
+	AUTH_DIGEST_FORMAT_FAILED = -8
 };
 
 /*! \brief States for outbound registrations (with register= lines in sip.conf */
@@ -1157,14 +1158,14 @@
 	ast_mutex_t pvt_lock;			/*!< Dialog private lock */
 	enum invitestates invitestate;		/*!< Track state of SIP_INVITEs */
 	int method;				/*!< SIP method that opened this dialog */
+	unsigned long randdata;			/*!< Random data for nonce */
+	int qop;				/*!< Quality of Protection flag, if set qop="auth". */
 	AST_DECLARE_STRING_FIELDS(
 		AST_STRING_FIELD(callid);	/*!< Global CallID */
-		AST_STRING_FIELD(randdata);	/*!< Random data */
 		AST_STRING_FIELD(accountcode);	/*!< Account code */
 		AST_STRING_FIELD(realm);	/*!< Authorization realm */
 		AST_STRING_FIELD(nonce);	/*!< Authorization nonce */
 		AST_STRING_FIELD(opaque);	/*!< Opaque nonsense */
-		AST_STRING_FIELD(qop);		/*!< Quality of Protection, since SIP wasn't complicated enough yet. */
 		AST_STRING_FIELD(domain);	/*!< Authorization domain */
 		AST_STRING_FIELD(from);		/*!< The From: header */
 		AST_STRING_FIELD(useragent);	/*!< User agent in SIP request */
@@ -1257,7 +1258,7 @@
 	struct sip_route *route;		/*!< Head of linked list of routing steps (fm Record-Route) */
 	int route_persistant;			/*!< Is this the "real" route? */
 	struct sip_auth *peerauth;		/*!< Realm authentication */
-	int noncecount;				/*!< Nonce-count */
+	unsigned long noncecount;		/*!< Nonce-count */
 	char lastmsg[256];			/*!< Last Message sent/received */
 	int amaflags;				/*!< AMA Flags */
 	int pendinginvite;			/*!< Any pending invite ? (seqno of this) */
@@ -1517,7 +1518,6 @@
 		AST_STRING_FIELD(realm);	/*!< Authorization realm */
 		AST_STRING_FIELD(nonce);	/*!< Authorization nonce */
 		AST_STRING_FIELD(opaque);	/*!< Opaque nonsense */
-		AST_STRING_FIELD(qop);		/*!< Quality of Protection, since SIP wasn't complicated enough yet. */
 		AST_STRING_FIELD(domain);	/*!< Authorization domain */
 		AST_STRING_FIELD(username);	/*!< Who we are registering as */
 		AST_STRING_FIELD(authuser);	/*!< Who we *authenticate* as */
@@ -1527,6 +1527,7 @@
 		AST_STRING_FIELD(callback);	/*!< Contact extension */
 		AST_STRING_FIELD(random);
 	);
+	int qop;			/*!< Quality of Protection flag, if set qop="auth". */
 	enum sip_transport transport;
 	int portno;			/*!<  Optional port override */
 	int expire;			/*!< Sched ID of expiration */
@@ -1540,7 +1541,7 @@
 	int callid_valid;		/*!< 0 means we haven't chosen callid for this registry yet. */
 	unsigned int ocseq;		/*!< Sequence number we got to for REGISTERs for this registry */
 	struct sockaddr_in us;		/*!< Who the server thinks we are */
-	int noncecount;			/*!< Nonce-count */
+	unsigned long noncecount;	/*!< Nonce-count */
 	char lastmsg[256];		/*!< Last Message sent/received */
 };
 
@@ -1698,7 +1699,7 @@
 static int transmit_response_with_date(struct sip_pvt *p, const char *msg, const struct sip_request *req);
 static int transmit_response_with_sdp(struct sip_pvt *p, const char *msg, const struct sip_request *req, enum xmittype reliable, int oldsdp);
 static int transmit_response_with_unsupported(struct sip_pvt *p, const char *msg, const struct sip_request *req, const char *unsupported);
-static int transmit_response_with_auth(struct sip_pvt *p, const char *msg, const struct sip_request *req, const char *rand, enum xmittype reliable, const char *header, int stale);
+static int transmit_response_with_auth(struct sip_pvt *p, const char *msg, const struct sip_request *req, unsigned long rand, enum xmittype reliable, const char *header, int stale);
 static int transmit_response_with_allow(struct sip_pvt *p, const char *msg, const struct sip_request *req, enum xmittype reliable);
 static void transmit_fake_auth_response(struct sip_pvt *p, struct sip_request *req, int reliable);
 static int transmit_request(struct sip_pvt *p, int sipmethod, int inc, enum xmittype reliable, int newbranch);
@@ -5538,6 +5539,8 @@
 	p->socket.fd = -1;
 	p->socket.type = SIP_TRANSPORT_UDP;
 	p->method = intended_method;
+	p->randdata = 0;
+	p->noncecount = 0;
 	p->initid = -1;
 	p->waitid = -1;
 	p->autokillid = -1;
@@ -7415,7 +7418,7 @@
 }
 
 /*! \brief Respond with authorization request */
-static int transmit_response_with_auth(struct sip_pvt *p, const char *msg, const struct sip_request *req, const char *randdata, enum xmittype reliable, const char *header, int stale)
+static int transmit_response_with_auth(struct sip_pvt *p, const char *msg, const struct sip_request *req, unsigned long randdata, enum xmittype reliable, const char *header, int stale)
 {
 	struct sip_request resp;
 	char tmp[512];
@@ -7425,13 +7428,17 @@
 		ast_log(LOG_WARNING, "Unable to determine sequence number from '%s'\n", get_header(req, "CSeq"));
 		return -1;
 	}
-	/* Stale means that they sent us correct authentication, but 
-	   based it on an old challenge (nonce) */
-	snprintf(tmp, sizeof(tmp), "Digest algorithm=MD5, realm=\"%s\", nonce=\"%s\"%s", global_realm, randdata, stale ? ", stale=true" : "");
+	/* 
+	 * Create Digest authentication request, as difined in SIP RFC-3261.
+	 * 
+	 * Stale means that they sent us correct authentication, but 
+	 *  based it on an old challenge (nonce)
+	 */
+	snprintf(tmp, sizeof(tmp), "Digest algorithm=MD5, realm=\"%s\", nonce=\"%08lx\", qop=\"auth\", opaque=\"%08lx\"%s", global_realm, randdata, randdata, stale ? ", stale=true" : "");
 	respprep(&resp, p, msg, req);
 	add_header(&resp, header, tmp);
 	add_header_contentLength(&resp, 0);
-	append_history(p, "AuthChal", "Auth challenge sent for %s - nc %d", p->username, p->noncecount);
+	append_history(p, "AuthChal", "Auth challenge sent for %s - nc %lu", p->username, p->noncecount);
 	return send_response(p, &resp, reliable, seqno);
 }
 
@@ -9107,11 +9114,11 @@
 		ast_string_field_set(p, nonce, r->nonce);
 		ast_string_field_set(p, domain, r->domain);
 		ast_string_field_set(p, opaque, r->opaque);
-		ast_string_field_set(p, qop, r->qop);
+		p->qop = r->qop;
 		p->noncecount = ++r->noncecount;
 
 		memset(digest,0,sizeof(digest));
-		if(!build_reply_digest(p, sipmethod, digest, sizeof(digest)))
+		if (!build_reply_digest(p, sipmethod, digest, sizeof(digest)))
 			add_header(&req, "Authorization", digest);
 		else
 			ast_log(LOG_NOTICE, "No authorization available for authentication of registration to %s@%s\n", r->username, r->hostname);
@@ -9783,26 +9790,12 @@
 	const char *authtoken;
 	char a1_hash[256];
 	char resp_hash[256]="";
-	char *c;
-	int  wrongnonce = FALSE;
-	int  good_response;
-	const char *usednonce = p->randdata;
+	int  good_response, good_nonce;
+	unsigned long recvnonce;	/* received nonce value in digest */
+	struct ast_http_digest digest;
 	struct ast_str *buf;
 	int res;
 
-	/* table of recognised keywords, and their value in the digest */
-	enum keys { K_RESP, K_URI, K_USER, K_NONCE, K_LAST };
-	struct x {
-		const char *key;
-		const char *s;
-	} *i, keys[] = {
-		[K_RESP] = { "response=", "" },
-		[K_URI] = { "uri=", "" },
-		[K_USER] = { "username=", "" },
-		[K_NONCE] = { "nonce=", "" },
-		[K_LAST] = { NULL, NULL}
-	};
-
 	/* Always OK if no secret */
 	if (ast_strlen_zero(secret) && ast_strlen_zero(md5secret))
 		return AUTH_SUCCESSFUL;
@@ -9817,8 +9810,8 @@
 	 */
 	auth_headers(WWW_AUTH, &respheader, &reqheader);
 
-	authtoken =  get_header(req, reqheader);	
-	if (ignore && !ast_strlen_zero(p->randdata) && ast_strlen_zero(authtoken)) {
+	authtoken = get_header(req, reqheader);	
+	if (ignore && p->randdata && ast_strlen_zero(authtoken)) {
 		/* This is a retransmitted invite/register/etc, don't reconstruct authentication
 		   information */
 		if (!reliable) {
@@ -9829,9 +9822,10 @@
 			sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
 		}
 		return AUTH_CHALLENGE_SENT;
-	} else if (ast_strlen_zero(p->randdata) || ast_strlen_zero(authtoken)) {
+	} else if (!p->randdata || ast_strlen_zero(authtoken)) {
 		/* We have no auth, so issue challenge and request authentication */
-		ast_string_field_build(p, randdata, "%08lx", ast_random());	/* Create nonce for challenge */
+		p->randdata = ast_random();	/* Create nonce for challenge */
+		p->noncecount = 0;
 		transmit_response_with_auth(p, response, req, p->randdata, reliable, respheader, 0);
 		/* Schedule auto destroy in 32 seconds */
 		sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
@@ -9843,51 +9837,37 @@
 	/* Whoever came up with the authentication section of SIP can suck my %&#$&* for not putting
 	   an example in the spec of just what it is you're doing a hash on. */
 
-	if (!(buf = ast_str_thread_get(&check_auth_buf, CHECK_AUTH_BUF_INITLEN)))
-		return AUTH_SECRET_FAILED; /*! XXX \todo need a better return code here */
-
-	/* Make a copy of the response and parse it */
-	res = ast_str_set(&buf, 0, "%s", authtoken);
-
-	if (res == AST_DYNSTR_BUILD_FAILED)
-		return AUTH_SECRET_FAILED; /*! XXX \todo need a better return code here */
-
-	c = buf->str;
-
-	while(c && *(c = ast_skip_blanks(c)) ) { /* lookup for keys */
-		for (i = keys; i->key != NULL; i++) {
-			const char *separator = ",";	/* default */
-
-			if (strncasecmp(c, i->key, strlen(i->key)) != 0)
-				continue;
-			/* Found. Skip keyword, take text in quotes or up to the separator. */
-			c += strlen(i->key);
-			if (*c == '"') { /* in quotes. Skip first and look for last */
-				c++;
-				separator = "\"";
-			}
-			i->s = c;
-			strsep(&c, separator);
-			break;
-		}
-		if (i->key == NULL) /* not found, jump after space or comma */
-			strsep(&c, " ,");
-	}
-
-	/* Verify that digest username matches  the username we auth as */
-	if (strcmp(username, keys[K_USER].s)) {
-		ast_log(LOG_WARNING, "username mismatch, have <%s>, digest has <%s>\n",
-			username, keys[K_USER].s);
+	/* Parse digest */
+	if (ast_parse_digest(authtoken, &digest, 0, pedanticsipchecking))
+		return AUTH_DIGEST_FORMAT_FAILED; /* Bad digest */
+	/* 
+	 * First: Verify that digest username matches the username we auth as
+	 */
+	if (strcmp(username, digest.username)) {
+		ast_log(LOG_WARNING, "Username mismatch, have <%s>, digest has <%s>\n",
+			username, digest.username);
 		/* Oops, we're trying something here */
 		return AUTH_USERNAME_MISMATCH;
 	}
 
-	/* Verify nonce from request matches our nonce.  If not, send 401 with new nonce */
-	if (strcasecmp(p->randdata, keys[K_NONCE].s)) { /* XXX it was 'n'casecmp ? */
-		wrongnonce = TRUE;
-		usednonce = keys[K_NONCE].s;
+	/*
+	 * This nonce value generated by asterisk
+	 * and must be returned as unsigned long in hexdecimal format
+	 */
+	if (sscanf(digest.nonce, "%lx", &recvnonce) != 1) {
+		ast_log(LOG_WARNING, "Received incorrect nonce in Digest <%s>\n", digest.nonce);
+		/* XXX Resent digest request with new nonce ? XXX */
+		p->randdata = ast_random();
+		p->noncecount = 0;
+		transmit_response_with_auth(p, response, req, p->randdata, reliable, respheader, TRUE);
+
+		/* Schedule auto destroy in 32 seconds */
+		sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
+		return AUTH_CHALLENGE_SENT;
 	}
 
+
+	/* Generate A1 and A1 MD5 hash */
 	if (!ast_strlen_zero(md5secret))
 		ast_copy_string(a1_hash, md5secret, sizeof(a1_hash));
 	else {
@@ -9895,51 +9875,85 @@
 		snprintf(a1, sizeof(a1), "%s:%s:%s", username, global_realm, secret);
 		ast_md5_hash(a1_hash, a1);
 	}
-
-	/* compute the expected response to compare with what we received */
+	
+	/*
+	 * Compute the expected response to compare with what we received
+	 * We use received nonce (and cnonce value, if qop and cnonce exist)
+	 */
 	{
 		char a2[256];
 		char a2_hash[256];
 		char resp[256];
 
-		snprintf(a2, sizeof(a2), "%s:%s", sip_methods[sipmethod].text,
-				S_OR(keys[K_URI].s, uri));
+		snprintf(a2, sizeof(a2), "%s:%s", sip_methods[sipmethod].text, digest.uri); /* "uri" in Digest must exist */
 		ast_md5_hash(a2_hash, a2);
-		snprintf(resp, sizeof(resp), "%s:%s:%s", a1_hash, usednonce, a2_hash);
+
+		if (digest.qop && *digest.cnonce) {
+			/* 
+			 * Generate RFC-2617 responce.
+			 * Note: cnonce MUST always exist, if qop="auth"
+			 * for http protocol, but I don't know about SIP
+			 */
+			snprintf(resp, sizeof(resp), "%s:%08lx:%s:%s:auth:%s", a1_hash, recvnonce, digest.nc, digest.cnonce, a2_hash);
+		} else {
+			/* Generate RFC 2069 responce */
+			snprintf(resp, sizeof(resp), "%s:%08lx:%s", a1_hash, recvnonce, a2_hash);
+		}
 		ast_md5_hash(resp_hash, resp);
 	}
 
-	good_response = keys[K_RESP].s &&
-			!strncasecmp(keys[K_RESP].s, resp_hash, strlen(resp_hash));
-	if (wrongnonce) {
-		ast_string_field_build(p, randdata, "%08lx", ast_random());
-		if (good_response) {
+	 /* Verify response and nonce. */
+	good_response = !strncmp(digest.response, resp_hash, strlen(resp_hash));
+	good_nonce = (p->randdata == recvnonce);
+
+	if (pedanticsipchecking && digest.qop) {
+		/* In pedantic mode we check nonce count for RFC-2617 response */
+		unsigned long nc;
+		sscanf(digest.nc, "%lx", &nc);
+		
+		if (p->noncecount <= nc)
+			p->noncecount = nc;
+		else {
 			if (sipdebug)
-				ast_log(LOG_NOTICE, "Correct auth, but based on stale nonce received from '%s'\n", get_header(req, "To"));
-			/* We got working auth token, based on stale nonce . */
+				ast_log(LOG_NOTICE, "Correct auth, but received nc '%lu', less '%lu'\n", nc, p->noncecount);
+			/* Resent auth request with new nonce */
+			p->randdata = ast_random();
+			p->noncecount = 0;
 			transmit_response_with_auth(p, response, req, p->randdata, reliable, respheader, TRUE);
-		} else {
-			/* Everything was wrong, so give the device one more try with a new challenge */
-			if (sipdebug)
-				ast_log(LOG_NOTICE, "Bad authentication received from '%s'\n", get_header(req, "To"));
-			transmit_response_with_auth(p, response, req, p->randdata, reliable, respheader, FALSE);
+
+			/* Schedule auto destroy in 32 seconds */
+			sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
+			return AUTH_CHALLENGE_SENT;
 		}
+	}
 
-		/* Schedule auto destroy in 32 seconds */
-		sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
-		return AUTH_CHALLENGE_SENT;
-	} 
-	if (good_response) {
+	if (good_response && good_nonce) {
+		 /* Digest verification complete */
 		append_history(p, "AuthOK", "Auth challenge succesful for %s", username);
 		return AUTH_SUCCESSFUL;
 	}
 
-	/* Ok, we have a bad username/secret pair */
-	/* Tell the UAS not to re-send this authentication data, because
-	   it will continue to fail
-	*/
+	if (!good_response) {
+		/*
+		 * If response not match, we have a bad username/secret pair
+		 * Tell the UAS not to re-send this authentication data,
+		 * because   it will continue to fail
+		 */
+		if (sipdebug)
+			ast_log(LOG_NOTICE, "Bad authentication received from '%s'\n", get_header(req, "To"));
+		return AUTH_SECRET_FAILED;
+	}
+	
+	/* Responce is good, but nonce not match */
+	if (sipdebug)
+		ast_log(LOG_NOTICE, "Correct auth, but based on stale nonce received from '%s'\n", get_header(req, "To"));
+	/* Resent auth request with actual nonce */
+	p->noncecount = 0;
+	transmit_response_with_auth(p, response, req, p->randdata, reliable, respheader, TRUE);
 
-	return AUTH_SECRET_FAILED;
+	/* Schedule auto destroy in 32 seconds */
+	sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
+	return AUTH_CHALLENGE_SENT;
 }
 
 /*! \brief Change onhold state of a peer using a pvt structure */
@@ -10008,7 +10022,7 @@
  */
 static void transmit_fake_auth_response(struct sip_pvt *p, struct sip_request *req, int reliable)
 {
-	ast_string_field_build(p, randdata, "%08lx", ast_random());	/* Create nonce for challenge */
+	p->randdata = ast_random();	/* Create nonce for challenge */
 	transmit_response_with_auth(p, "401 Unauthorized", req, p->randdata, reliable, "WWW-Authenticate", 0);
 }
 
@@ -10173,7 +10187,11 @@
 	if (res < 0) {
 		switch (res) {
 		case AUTH_SECRET_FAILED:
-			/* Wrong password in authentication. Go away, don't try again until you fixed it */
+		case AUTH_DIGEST_FORMAT_FAILED:
+			/* 
+			 * Wrong Digest format or password in authentication.
+			 * Go away, don't try again until you fixed it
+			 */
 			transmit_response(p, "403 Forbidden (Bad auth)", &p->initreq);
 			break;
 		case AUTH_USERNAME_MISMATCH:
@@ -13726,57 +13744,26 @@
 	with  for receiving calls from.  */
 static int reply_digest(struct sip_pvt *p, struct sip_request *req, char *header, int sipmethod,  char *digest, int digest_len)
 {
-	char tmp[512];
-	char *c;
-	char oldnonce[256];
+	struct ast_http_digest d; /* Digest fields */
+	const char *authreq;
 
-	/* table of recognised keywords, and places where they should be copied */
-	const struct x {
-		const char *key;
-		const ast_string_field *field;
-	} *i, keys[] = {
-		{ "realm=", &p->realm },
-		{ "nonce=", &p->nonce },
-		{ "opaque=", &p->opaque },
-		{ "qop=", &p->qop },
-		{ "domain=", &p->domain },
-		{ NULL, 0 },
-	};
-
-	ast_copy_string(tmp, get_header(req, header), sizeof(tmp));
-	if (ast_strlen_zero(tmp)) 
+	authreq = get_header(req, header);
+	if (ast_parse_digest(authreq, &d, 1, pedanticsipchecking)) {
+		ast_log(LOG_WARNING, "Wrong Digest request.\n");
 		return -1;
-	if (strncasecmp(tmp, "Digest ", strlen("Digest "))) {
-		ast_log(LOG_WARNING, "missing Digest.\n");
-		return -1;
 	}
-	c = tmp + strlen("Digest ");
-	ast_copy_string(oldnonce, p->nonce, sizeof(oldnonce));
-	while (c && *(c = ast_skip_blanks(c))) {	/* lookup for keys */
-		for (i = keys; i->key != NULL; i++) {
-			char *src, *separator;
-			if (strncasecmp(c, i->key, strlen(i->key)) != 0)
-				continue;
-			/* Found. Skip keyword, take text in quotes or up to the separator. */
-			c += strlen(i->key);
-			if (*c == '"') {
-				src = ++c;
-				separator = "\"";
-			} else {
-				src = c;
-				separator = ",";
-			}
-			strsep(&c, separator); /* clear separator and move ptr */
-			ast_string_field_ptr_set(p, i->field, src);
-			break;
-		}
-		if (i->key == NULL) /* not found, try ',' */
-			strsep(&c, ",");
-	}
-	/* Reset nonce count */
-	if (strcmp(p->nonce, oldnonce)) 
+
+	/* New nonce value received - reset nonce count */
+	if (strcmp(d.nonce, p->nonce)) 
 		p->noncecount = 0;
 
+	/* save auth data */
+	ast_string_field_set(p, realm, d.realm);
+	ast_string_field_set(p, nonce, d.nonce);
+	ast_string_field_set(p, domain, d.domain);
+	ast_string_field_set(p, opaque, d.opaque);
+	p->qop = d.qop;
+
 	/* Save auth data for following registrations */
 	if (p->registry) {
 		struct sip_registry *r = p->registry;
@@ -13786,7 +13773,7 @@
 			ast_string_field_set(r, nonce, p->nonce);
 			ast_string_field_set(r, domain, p->domain);
 			ast_string_field_set(r, opaque, p->opaque);
-			ast_string_field_set(r, qop, p->qop);
+			r->qop = p->qop;
 			r->noncecount = 0;
 		}
 	}
@@ -13807,7 +13794,7 @@
 	char resp[256];
 	char resp_hash[256];
 	char uri[256];
-	char cnonce[80];
+	unsigned long cnonce;
 	const char *username;
 	const char *secret;
 	const char *md5secret;
@@ -13820,7 +13807,7 @@
 	else
 		snprintf(uri, sizeof(uri), "sip:%s@%s",p->username, ast_inet_ntoa(p->sa.sin_addr));
 
-	snprintf(cnonce, sizeof(cnonce), "%08lx", ast_random());
+	cnonce = ast_random();
 
  	/* Check if we have separate auth credentials */
  	if ((auth = find_realm_authentication(authl, p->realm))) {
@@ -13850,18 +13837,18 @@
 	ast_md5_hash(a2_hash,a2);
 
 	p->noncecount++;
-	if (!ast_strlen_zero(p->qop))
-		snprintf(resp,sizeof(resp),"%s:%s:%08x:%s:%s:%s", a1_hash, p->nonce, p->noncecount, cnonce, "auth", a2_hash);
+	if (p->qop)
+		snprintf(resp,sizeof(resp),"%s:%s:%08lx:%08lx:auth:%s", a1_hash, p->nonce, p->noncecount, cnonce, a2_hash);
 	else
 		snprintf(resp,sizeof(resp),"%s:%s:%s", a1_hash, p->nonce, a2_hash);
 	ast_md5_hash(resp_hash, resp);
 	/* XXX We hard code our qop to "auth" for now.  XXX */
-	if (!ast_strlen_zero(p->qop))
-		snprintf(digest, digest_len, "Digest username=\"%s\", realm=\"%s\", algorithm=MD5, uri=\"%s\", nonce=\"%s\", response=\"%s\", opaque=\"%s\", qop=auth, cnonce=\"%s\", nc=%08x", username, p->realm, uri, p->nonce, resp_hash, p->opaque, cnonce, p->noncecount);
+	if (p->qop)
+		snprintf(digest, digest_len, "Digest username=\"%s\", realm=\"%s\", algorithm=MD5, uri=\"%s\", nonce=\"%s\", response=\"%s\", qop=\"auth\", opaque=\"%s\", cnonce=\"%08lx\", nc=%08lx", username, p->realm, uri, p->nonce, resp_hash, p->opaque, cnonce, p->noncecount);
 	else
-		snprintf(digest, digest_len, "Digest username=\"%s\", realm=\"%s\", algorithm=MD5, uri=\"%s\", nonce=\"%s\", response=\"%s\", opaque=\"%s\"", username, p->realm, uri, p->nonce, resp_hash, p->opaque);
+		snprintf(digest, digest_len, "Digest username=\"%s\", realm=\"%s\", algorithm=MD5, uri=\"%s\", nonce=\"%s\", response=\"%s\"", username, p->realm, uri, p->nonce, resp_hash);
 
-	append_history(p, "AuthResp", "Auth response sent for %s in realm %s - nc %d", username, p->realm, p->noncecount);
+	append_history(p, "AuthResp", "Auth response sent for %s in realm %s - nc %lu", username, p->realm, p->noncecount);
 
 	return 0;
 }
@@ -17516,6 +17503,9 @@
 		case AUTH_ACL_FAILED:
 			reason = "Device does not match ACL";
 			break;
+		case AUTH_DIGEST_FORMAT_FAILED:
+			reason = "Wrong Digest format";
+			break;
 		default:
 			reason = "Unknown failure";
 			break;
@@ -17735,7 +17725,7 @@
 			check_pendings(p);
 		}
 		/* Got an ACK that we did not match. Ignore silently */
-		if (!p->lastinvite && ast_strlen_zero(p->randdata))
+		if (!p->lastinvite && !p->randdata)
 			p->needdestroy = 1;
 		break;
 	default:
Index: include/asterisk/utils.h
===================================================================
--- include/asterisk/utils.h	(revision 99859)
+++ include/asterisk/utils.h	(working copy)
@@ -652,6 +652,33 @@
 
 #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);
+
+
+
 #include "asterisk/strings.h"
 
 #endif /* _ASTERISK_UTILS_H */
Index: include/asterisk/http.h
===================================================================
--- include/asterisk/http.h	(revision 99859)
+++ include/asterisk/http.h	(working copy)
@@ -52,20 +52,37 @@
  * be run earlier in the startup process so modules have it available.
  */
 
+/*! \brief HTTP Request methods known by Asterisk */
+enum httpmethod {
+	HTTP_UNKNOWN,            /*!< Unknown response */
+	HTTP_GET,
+	HTTP_HEAD,
+	HTTP_POST,
+	HTTP_PUT		/*!< Not supported in Asterisk */
+};
 
 /*! \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 struct ast_str *(*ast_http_callback)(struct server_instance *ser, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength);
+typedef int (*ast_http_callback)(struct server_instance *ser, const char *uri, enum httpmethod method, struct ast_variable *get_params, struct ast_variable *headers);
 
 /*! \brief Definition of a URI reachable in the embedded HTTP server */
 struct ast_http_uri {
@@ -73,17 +90,44 @@
 	const char *description;
 	const char *uri;
 	unsigned int has_subtree:1;
-	/*! This URI mapping serves static content */
-	unsigned int static_content:1;
 	ast_http_callback callback;
 };
 
 /*! \brief Link into the Asterisk HTTP server */
 int ast_http_uri_link(struct ast_http_uri *urihandler);
 
-/*! \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);
+/* Get cookie from Request headers */
+struct ast_variable *ast_http_get_cookies(struct ast_variable *headers);
 
+/*! \brief Return mime type based on extension */
+char *ast_ftype2mtype(const char *ftype);
+
+/*! \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 server_instance *ser, enum httpmethod 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 server_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 server_instance *ser, int status, const char *title, const char *text);
+
 /*! \brief Destroy an HTTP server */
 void ast_http_uri_unlink(struct ast_http_uri *urihandler);
 
Index: main/utils.c
===================================================================
--- main/utils.c	(revision 99859)
+++ main/utils.c	(working copy)
@@ -1555,3 +1555,113 @@
 #endif
 	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;
+}
+
Index: main/manager.c
===================================================================
--- main/manager.c	(revision 99859)
+++ main/manager.c	(working copy)
@@ -110,6 +110,9 @@
 static int manager_enabled = 0;
 static int webmanager_enabled = 0;
 
+#define DEFAULT_REALM		"asterisk"
+static char global_realm[MAXHOSTNAMELEN];	/*!< Default realm */
+
 static int block_sockets;
 static int num_sessions;
 
@@ -122,7 +125,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).
  */
 static const char *command_blacklist[] = {
 	"module load",
@@ -153,6 +157,9 @@
 	int send_events;	/*!<  XXX what ? */
 	struct eventqent *last_ev;	/*!< last event processed. */
 	int writetimeout;	/*!< Timeout for ast_carefulwrite() */
+	time_t noncetime;	/*!< Timer for nonce value expiration */
+	unsigned long oldnonce;	/*!< Stale nonce value */
+	unsigned long nc;	/*!< incremental  nonce counter */
 	AST_LIST_ENTRY(mansession) list;
 };
 
@@ -160,6 +167,7 @@
 
 static AST_LIST_HEAD_STATIC(sessions, mansession);
 
+
 /*! \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
@@ -173,8 +181,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;
 };
 
@@ -616,8 +625,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;
 }
@@ -892,8 +901,10 @@
 
 	if (s->f != NULL)
 		send_string(s, buf->str);
-	else
+	else {
 		ast_verbose("fd == -1 in astman_append, should not happen\n");
+ast_verbose("str: %s\n", buf->str);
+	}
 }
 
 /*! \note NOTE: XXX this comment is unclear and possibly wrong.
@@ -1047,7 +1058,8 @@
 
 static int action_ping(struct mansession *s, const struct message *m)
 {
-	astman_send_response(s, m, "Success", "Ping: Pong\r\n");
+	astman_append(s, "Response: Success\r\n"
+			 "Ping: Pong\r\n");
 	return 0;
 }
 
@@ -1416,10 +1428,11 @@
 
 	res = set_eventmask(s, mask);
 	if (res > 0)
-		astman_send_response(s, m, "Success", "Events: On\r\n");
+		astman_append(s, "Response: Success\r\n"
+				 "Events: On\r\n");
 	else if (res == 0)
-		astman_send_response(s, m, "Success", "Events: Off\r\n");
-
+		astman_append(s, "Response: Success\r\n"
+				 "Events: Off\r\n");
 	return 0;
 }
 
@@ -1435,6 +1448,13 @@
 
 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");
@@ -2477,6 +2497,7 @@
 	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"), sizeof(action));
 	ast_debug(1, "Manager received command '%s'\n", action);
@@ -2511,14 +2532,17 @@
 		if (strcasecmp(action, tmp->action))
 			continue;
 		if (s->writeperm & tmp->authority || tmp->authority == 0)
-			ret = tmp->func(s, m);
-		else
+			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[BUFSIZ];
 		snprintf(buf, sizeof(buf), "Invalid/unknown command: %s. Use Action: ListCommands to show available commands.", action);
 		ast_mutex_lock(&s->__lock);
@@ -2946,7 +2970,6 @@
 
 	if (ident == 0)
 		return NULL;
-
 	AST_LIST_LOCK(&sessions);
 	AST_LIST_TRAVERSE(&sessions, s, list) {
 		ast_mutex_lock(&s->__lock);
@@ -2961,6 +2984,39 @@
 	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;
+
+	if (nonce == 0 || username == NULL || stale == NULL)
+		return NULL;
+	
+	AST_LIST_LOCK(&sessions);
+	AST_LIST_TRAVERSE(&sessions, s, list) {
+		ast_mutex_lock(&s->__lock);
+		if (!strcasecmp(s->username, username) && s->managerid == nonce) {
+			*stale = 0;
+			break;
+		} else if (!strcasecmp(s->username, username) && s->oldnonce == nonce) {
+			*stale = 1;
+			break;
+		}
+		ast_mutex_unlock(&s->__lock);
+	}
+	AST_LIST_UNLOCK(&sessions);
+
+	return s;
+}
+
 int astman_verify_session_readpermissions(unsigned long ident, int perm)
 {
 	int result = 0;
@@ -3129,7 +3185,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;
@@ -3141,7 +3197,7 @@
 	struct variable_count *vc = NULL;
 	struct ao2_container *vco = NULL;
 
-	for (v = vars; v; v = v->next) {
+	for (v = get_vars; v; v = v->next) {
 		if (!dest && !strcasecmp(v->name, "ajaxdest"))
 			dest = v->value;
 		else if (!objtype && !strcasecmp(v->name, "ajaxobjtype"))
@@ -3164,11 +3220,13 @@
 				ast_str_append(out, 0, xml ? "'" : "</td></tr>\n");
 				in_data = 0;
 			}
-			ast_str_append(out, 0, xml ? " /></response>\n" :
-				"<tr><td colspan=\"2\"><hr></td></tr>\r\n");
-			inobj = 0;
-			ao2_ref(vco, -1);
-			vco = NULL;
+			if (inobj) {
+				ast_str_append(out, 0, xml ? " /></response>\n" :
+					"<tr><td colspan=\"2\"><hr></td></tr>\r\n");
+				inobj = 0;
+				ao2_ref(vco, -1);
+				vco = NULL;
+			}
 			continue;
 		}
 
@@ -3227,35 +3285,40 @@
 	}
 }
 
-static struct ast_str *generic_http_callback(enum output_format format,
+static int generic_http_callback(struct server_instance *ser,
+					     enum httpmethod method,
+					     enum output_format format,
 					     struct sockaddr_in *requestor, const char *uri,
-					     struct ast_variable *params, int *status,
-					     char **title, int *contentlength)
+					     struct ast_variable *get_params,
+					     struct ast_variable *headers)
 {
 	struct mansession *s = NULL;
 	unsigned long ident = 0; /* invalid, so find_session will fail if not set through the cookie */
 	int blastaway = 0;
-	struct ast_variable *v;
+	struct ast_variable *v, *cookies;
 	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) {
+	cookies = ast_http_get_cookies(headers);
+	for (v = cookies; v; v = v->next) {
 		if (!strcasecmp(v->name, "mansession_id")) {
 			sscanf(v->value, "%lx", &ident);
 			break;
 		}
 	}
+	if (cookies)
+		ast_variables_destroy(cookies);
 
 	if (!(s = find_session(ident))) {
 		/* 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;
+			ast_http_error(ser, 500, "Server Error", "Internal Server Error (out of memory)\n");
+			return -1;
 		}
 		s->sin = *requestor;
 		s->fd = -1;
@@ -3274,16 +3337,29 @@
 
 	ast_mutex_unlock(&s->__lock);
 
-	if (!(out = ast_str_create(1024))) {
-		*status = 500;
+	http_header = ast_str_create(128);
+	out = ast_str_create(2048);
+
+	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;
+	}
 
-	for (x = 0, v = params; v && (x < AST_MAX_MANHEADERS); x++, v = v->next) {
+
+	for (x = 0, v = get_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);
@@ -3292,29 +3368,27 @@
 
 	if (process_message(s, &m)) {
 		if (s->authenticated) {
-				if (manager_displayconnects(s))
+			if (manager_displayconnects(s))
 				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));
 		} else {
-				if (displayconnects)
+			if (displayconnects)
 				ast_verb(2, "HTTP Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(s->sin.sin_addr));
 			ast_log(LOG_EVENT, "HTTP Failed attempt from %s\n", ast_inet_ntoa(s->sin.sin_addr));
 		}
 		s->needdestroy = 1;
 	}
 
-	ast_str_append(&out, 0,
-		       "Content-type: text/%s\r\n"
-		       "Cache-Control: no-cache;\r\n"
-		       "Set-Cookie: mansession_id=\"%08lx\"; 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=\"%08lx\"; Version=\"1\"; Max-Age=%d",
+		contenttype[format],
+		s->managerid, httptimeout);
 
 	if (format == FORMAT_XML) {
 		ast_str_append(&out, 0, "<ajax-response>\n");
 	} else if (format == FORMAT_HTML) {
-
 #define ROW_FMT	"<tr><td colspan=\"2\" bgcolor=\"#f1f1ff\">%s</td></tr>\r\n"
 #define TEST_STRING \
 	"<form action=\"manager\">action: <input name=\"action\"> cmd <input name=\"command\"><br> \
@@ -3334,13 +3408,13 @@
 		if (l) {
 			if ((buf = mmap(NULL, l, PROT_READ | PROT_WRITE, MAP_SHARED, s->fd, 0))) {
 				if (format == FORMAT_XML || format == FORMAT_HTML)
-					xml_translate(&out, buf, params, format);
+					xml_translate(&out, buf, get_params, format);
 				else
 					ast_str_append(&out, 0, buf);
 				munmap(buf, l);
 			}
 		} else if (format == FORMAT_XML || format == FORMAT_HTML) {
-			xml_translate(&out, "", params, format);
+			xml_translate(&out, "", get_params, format);
 		}
 		fclose(s->f);
 		s->f = NULL;
@@ -3370,29 +3444,335 @@
 		s->inuse--;
 	ast_mutex_unlock(&s->__lock);
 
-	if (blastaway)
+	ast_http_send(ser, method, 200, NULL, http_header, out, 0, 0);
+	http_header = out = NULL;
+
+generic_callback_out:
+	/* Clear resource */
+	if (http_header)
+		ast_free(http_header);
+	if (out)
+		ast_free(out);
+	if(s->f)
+		fclose(s->f);
+
+	if (s != NULL && blastaway)
 		destroy_session(s);
-generic_callback_out:
-	if (*status != 200)
-		return ast_http_error(500, "Server Error", NULL, "Internal Server Error (out of memory)\n");
-	return out;
+	return 0;
 }
 
-static struct ast_str *manager_http_callback(struct server_instance *ser, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
+static int auth_http_callback(struct server_instance *ser,
+					     enum httpmethod 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, params, status, title, contentlength);
+	struct mansession *s = NULL;
+	struct ast_variable *v;
+	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;
+
+	
+	/* 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), "GET:%s", 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))) {
+		
+		/*
+		 * Create new session.
+		 * While it is not in the list we don't need any locking
+		 */
+		if (!(s = ast_calloc(1, sizeof(*s)))) {
+			ast_http_error(ser, 500, "Server Error", "Internal Server Error (out of memory)\n");
+			return -1;
+		}
+		memset(s, 0, sizeof(struct mansession));
+
+		s->sin = *requestor;
+		s->fd = -1;
+		ast_copy_string(s->username, u_username, sizeof(s->username));
+		s->waiting_thread = AST_PTHREADT_NULL;
+		ast_mutex_init(&s->__lock);
+		s->managerid = nonce;
+		s->last_ev = grab_last();
+		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_mutex_lock(&s->__lock);
+		AST_LIST_LOCK(&sessions);
+		AST_LIST_INSERT_HEAD(&sessions, s, list);
+		ast_atomic_fetchadd_int(&num_sessions, 1);
+		AST_LIST_UNLOCK(&sessions);
+
+		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;
+		ast_mutex_unlock(&s->__lock);
+		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;
+			ast_mutex_unlock(&s->__lock);
+			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);
+	ast_mutex_unlock(&s->__lock);
+
+	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;
+	}
+
+	for (x=0, v = get_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);
+		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=\"GET\">\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, get_params, format);
+			else
+				ast_str_append(&out, 0, buf);
+			munmap(buf, result_size);
+		}
+	} else if (format == FORMAT_XML || format == FORMAT_HTML) {
+		xml_translate(&out, "", get_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 (http_header)
+		ast_free(http_header);
+	if (out)
+		ast_free(out);
+	ast_mutex_lock(&s->__lock);
+	if(s->f)
+		fclose(s->f);
+	s->f = NULL;
+	s->fd = -1;
+	ast_mutex_unlock(&s->__lock);
+
+	if (s->needdestroy) {
+		ast_debug(1, "Need destroy, doing it now!\n");
+		destroy_session(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 server_instance *ser, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
+static int manager_http_callback(struct server_instance *ser, const char *uri, enum httpmethod method, struct ast_variable *get_params,  struct ast_variable *headers)
 {
-	return generic_http_callback(FORMAT_XML, &ser->requestor, uri, 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 server_instance *ser, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
+static int mxml_http_callback(struct server_instance *ser, const char *uri, enum httpmethod method, struct ast_variable *get_params, struct ast_variable *headers)
 {
-	return generic_http_callback(FORMAT_RAW, &ser->requestor, uri, params, status, title, contentlength);
+	return generic_http_callback(ser, method, FORMAT_XML, &ser->requestor, uri, get_params, headers);
 }
 
+static int rawman_http_callback(struct server_instance *ser, const char *uri, enum httpmethod 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",
@@ -3414,6 +3794,44 @@
 	.callback = mxml_http_callback,
 };
 
+
+/* Callback with Digest authentication */
+static int auth_manager_http_callback(struct server_instance *ser, const char *uri, enum httpmethod 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 server_instance *ser, const char *uri, enum httpmethod 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 server_instance *ser, const char *uri, enum httpmethod 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,
+};
+
+struct ast_http_uri amanageruri = {
+	.description = "HTML Manager Event Interface w/Digest authentication",
+	.uri = "amanager",
+	.has_subtree = 0,
+	.callback = auth_manager_http_callback,
+};
+
+struct ast_http_uri amanagerxmluri = {
+	.description = "XML Manager Event Interface w/Digest authentication",
+	.uri = "amxml",
+	.has_subtree = 0,
+	.callback = auth_mxml_http_callback,
+};
+
 static int registered = 0;
 static int webregged = 0;
 
@@ -3460,6 +3878,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];
 
 	if (!registered) {
 		/* Register default actions */
@@ -3509,6 +3929,7 @@
 	}
 
 	/* 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);
@@ -3716,11 +4137,20 @@
 	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->a1_hash)
+			ast_free(user->a1_hash);
 		if (user->secret)
 			ast_free(user->secret);
 		ast_free_ha(user->ha);
@@ -3732,9 +4162,14 @@
 
 	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 {
@@ -3742,6 +4177,10 @@
 			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;
 		}
 	}
Index: main/http.c
===================================================================
--- main/http.c	(revision 99859)
+++ main/http.c	(working copy)
@@ -103,10 +103,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" },
@@ -124,31 +125,34 @@
 
 static AST_RWLIST_HEAD_STATIC(uri_redirects, http_uri_redirect);
 
-static const char *ftype2mtype(const char *ftype, char *wkspace, int wkspacelen)
+/*! \brief Return mime type based on extension */
+char *ast_ftype2mtype(const char *ftype)
 {
 	int x;
 	if (ftype) {
-		for (x=0;x<sizeof(mimetypes) / sizeof(mimetypes[0]); x++) {
+		for (x = 0;x < ARRAY_LEN(mimetypes); x++) {
 			if (!strcasecmp(ftype, mimetypes[x].ext))
 				return mimetypes[x].mtype;
 		}
 	}
-	snprintf(wkspace, wkspacelen, "text/%s", ftype ? ftype : "plain");
-	return wkspace;
+	return NULL;
 }
 
-static struct ast_str *static_callback(struct server_instance *ser, const char *uri, struct ast_variable *vars, int *status, char **title, int *contentlength)
+static int static_callback(struct server_instance *ser, const char *uri, enum httpmethod 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 tv = ast_tvnow();
-	char buf[256];
+	struct ast_str *http_header = ast_str_create(80);
+	struct timeval tv;
 	struct ast_tm tm;
+	char timebuf[80], etag[20];
+	struct ast_variable *v;
+	int not_modified = 0;
 
 	/* 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. */
@@ -162,7 +166,9 @@
 		
 	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 */
 	len = strlen(uri) + strlen(ast_config_AST_DATA_DIR) + strlen("/static-http/") + 5;
@@ -179,44 +185,55 @@
 	if (fd < 0)
 		goto out403;
 
-	ast_strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S %Z", ast_localtime(&tv, &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;
+		}
+	}
 
+	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 NULL;
+	return 0;
 
 out404:
-	*status = 404;
-	*title = ast_strdup("Not Found");
-	return ast_http_error(404, "Not Found", NULL, "Nothing to see here.  Move along.");
+	ast_http_error(ser, 404, "Not Found", "Nothing to see here.  Move along.");
+	return -1;
 
 out403:
-	*status = 403;
-	*title = ast_strdup("Access Denied");
-	return ast_http_error(403, "Access Denied", NULL, "Sorry, I cannot let you do that, Dave.");
+	ast_http_error(ser, 403, "Access Denied", "Sorry, I cannot let you do that, Dave.");
+	return -1;
 }
 
-
-static struct ast_str *httpstatus_callback(struct server_instance *ser, const char *uri, struct ast_variable *vars, int *status, char **title, int *contentlength)
+static int httpstatus_callback(struct server_instance *ser, const char *uri, enum httpmethod method, struct ast_variable *get_vars, struct ast_variable *headers)
 {
 	struct ast_str *out = ast_str_create(512);
-	struct ast_variable *v;
+	struct ast_variable *v, *cookies = NULL;
 
 	if (out == NULL)
-		return out;
+		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"
@@ -231,17 +248,20 @@
 		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));
 	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 = {
@@ -256,20 +276,175 @@
 	.description = "Asterisk HTTP Static Delivery",
 	.uri = "static",
 	.has_subtree = 1,
-	.static_content = 1,
 };
+
+
+/* get cookie from Request headers */
+struct ast_variable *ast_http_get_cookies(struct ast_variable *headers)
+{
+	struct ast_variable *v, *var, *prev=NULL, *cookies=NULL;
 	
-struct ast_str *ast_http_error(int status, const char *title, const char *extra_header, const char *text)
+	for (v = headers; v; v = v->next) {
+		char *vname, *vval;
+		int l;
+
+                if (!strncasecmp(v->name, "Cookie", 6)) {
+			/* process "Cookie: " lines is moved to upper level,
+			   if these are required. */
+
+			/* TODO - The cookie parsing code below seems to work   
+			   in IE6 and FireFox 1.5.  However, it is not entirely 
+			   correct, and therefore may not work in all           
+			   circumstances.		                        
+			     For more details see RFC 2109 and RFC 2965    */
+	
+			/* FireFox cookie strings look like:                    
+			     Cookie: mansession_id="********"                   
+			   InternetExplorer's look like:                        
+			     Cookie: $Version="1"; mansession_id="********"     */
+
+			/* If we got a FireFox cookie string, the name's right  
+			    after "Cookie: "                                    */
+			vname = ast_skip_blanks(v->value);
+			
+			/* If we got an IE cookie string, we need to skip to    
+			    past the version to get to the name                 */
+			if (*vname == '$') {
+				strsep(&vname, ";");
+				if (!vname)	/* no name ? */
+					continue;
+				vname = ast_skip_blanks(vname);
+			}
+			vval = strchr(vname, '=');
+			if (!vval)
+				continue;
+			/* Ditch the = and the quotes */
+			*vval++ = '\0';
+			if (*vval)
+				vval++;
+			if ( (l = strlen(vval)) )
+				vval[l - 1] = '\0';	/* trim trailing quote */
+			var = ast_variable_new(vname, vval, "");
+			if (var) {
+				if (prev)
+					prev->next = var;
+				else
+					cookies = var;
+				prev = var;
+			}
+		}
+	}
+	return cookies;
+}
+
+/* send http/1.1 responce */
+/* free content variable and close socket*/
+void ast_http_send(struct server_instance *ser, enum httpmethod 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 tv = 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(&tv, &tm, "GMT"));
+
+	/* calc conetnt length */
+	if (out)
+		content_length += strlen(out->str);
+	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 != 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 server_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 (out == NULL)
-		return out;
+	
+	if (http_headers == NULL || out == NULL)
+		return;
+
+	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,
-		"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>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, HTTP_UNKNOWN, 401, "Unauthorized", http_headers, out, 0, 0);
+	return;
+}
+
+/* send http error responce and close socket*/
+void ast_http_error(struct server_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,
+		"<!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"
@@ -277,8 +452,10 @@
 		"<hr />\r\n"
 		"<address>Asterisk Server</address>\r\n"
 		"</body></html>\r\n",
-			(extra_header ? extra_header : ""), status, title, title, text);
-	return out;
+			status_code, status_title, status_title, text);
+	
+	ast_http_send(ser, HTTP_UNKNOWN, status_code, status_title, http_headers, out, 0, 0);
+	return;
 }
 
 /*! \brief 
@@ -392,14 +569,13 @@
 	fclose(f);
 }
 
-static struct ast_str *handle_post(struct server_instance *ser, char *uri, 
-	int *status, char **title, int *contentlength, struct ast_variable *headers,
-	struct ast_variable *cookies)
+static void handle_post(struct server_instance *ser, char *uri, 
+	struct ast_variable *headers)
 {
 	char buf;
 	FILE *f;
 	size_t res;
-	struct ast_variable *var;
+	struct ast_variable *var, *cookies=NULL;
 	int content_len = 0;
 	MM_CTX *ctx;
 	int mm_res, i;
@@ -407,39 +583,40 @@
 	const char *post_dir;
 	unsigned long ident = 0;
 
+	/* Get Cookies from headers and find "mansession_id" */
+	cookies = ast_http_get_cookies(headers);
 	for (var = cookies; var; var = var->next) {
 		if (strcasecmp(var->name, "mansession_id"))
 			continue;
 
 		if (sscanf(var->value, "%lx", &ident) != 1) {
-			*status = 400;
-			*title = ast_strdup("Bad Request");
-			return ast_http_error(400, "Bad Request", NULL, "The was an error parsing the request.");
+			ast_http_error(ser, 400, "Bad Request", "The was an error parsing the request.");
+			goto cleanup;
 		}
 
 		if (!astman_verify_session_writepermissions(ident, EVENT_FLAG_CONFIG)) {
-			*status = 401;
-			*title = ast_strdup("Unauthorized");
-			return ast_http_error(401, "Unauthorized", NULL, "You are not authorized to make this request.");
+//			ast_http_auth(ser, "Asterisk", "You are not authorized to make this request.");
+			ast_http_error(ser, 401, "Unauthorized", "You are not authorized to make this request.");
+			goto cleanup;
 		}
 
 		break;
 	}
+
 	if (!var) {
-		*status = 401;
-		*title = ast_strdup("Unauthorized");
-		return ast_http_error(401, "Unauthorized", NULL, "You are not authorized to make this request.");
+		ast_http_error(ser, 401, "Unauthorized", "You are not authorized to make this request.");
+		goto cleanup;
 	}
 
 	if (!(f = tmpfile()))
-		return NULL;
+		goto cleanup;
 
 	for (var = headers; var; var = var->next) {
 		if (!strcasecmp(var->name, "Content-Length")) {
 			if ((sscanf(var->value, "%u", &content_len)) != 1) {
 				ast_log(LOG_ERROR, "Invalid Content-Length in POST request!\n");
 				fclose(f);
-				return NULL;
+				goto cleanup;
 			}
 			ast_debug(1, "Got a Content-Length of %d\n", content_len);
 		} else if (!strcasecmp(var->name, "Content-Type"))
@@ -456,7 +633,7 @@
 	if (fseek(f, SEEK_SET, 0)) {
 		ast_debug(1, "Failed to seek temp file back to beginning.\n");
 		fclose(f);
-		return NULL;
+		goto cleanup;
 	}
 
 	AST_RWLIST_RDLOCK(&post_mappings);
@@ -464,9 +641,8 @@
 		ast_debug(1, "%s is not a valid URI for POST\n", uri);
 		AST_RWLIST_UNLOCK(&post_mappings);
 		fclose(f);
-		*status = 404;
-		*title = ast_strdup("Not Found");
-		return ast_http_error(404, "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.");
+		goto cleanup;
 	}
 	post_dir = ast_strdupa(post_map->to);
 	post_map = NULL;
@@ -476,7 +652,7 @@
 
 	if (!(ctx = mm_context_new())) {
 		fclose(f);
-		return NULL;
+		goto cleanup;
 	}
 
 	mm_res = mm_parse_fileptr(ctx, f, MM_PARSE_LOOSE, 0);
@@ -484,18 +660,16 @@
 	if (mm_res == -1) {
 		ast_log(LOG_ERROR, "Error parsing MIME data\n");
 		mm_context_free(ctx);
-		*status = 400;
-		*title = ast_strdup("Bad Request");
-		return ast_http_error(400, "Bad Request", NULL, "The was an error parsing the request.");
+		ast_http_error(ser, 400, "Bad Request", "The was an error parsing the request.");
+		goto cleanup;
 	}
 
 	mm_res = mm_context_countparts(ctx);
 	if (!mm_res) {
 		ast_log(LOG_ERROR, "Invalid MIME data, found no parts!\n");
 		mm_context_free(ctx);
-		*status = 400;
-		*title = ast_strdup("Bad Request");
-		return ast_http_error(400, "Bad Request", NULL, "The was an error parsing the request.");
+		ast_http_error(ser, 400, "Bad Request", "The was an error parsing the request.");
+		goto cleanup;
 	}
 
 	if (option_debug) {
@@ -530,21 +704,23 @@
 
 	mm_context_free(ctx);
 
-	*status = 200;
-	*title = ast_strdup("OK");
-	return ast_http_error(200, "OK", NULL, "File successfully uploaded.");
+	ast_http_error(ser, 200, "OK", "File successfully uploaded.");
+
+cleanup:
+	if (cookies)
+		ast_variables_destroy(cookies);
+	return;
 }
 
-static struct ast_str *handle_uri(struct server_instance *ser, char *uri, int *status, 
-	char **title, int *contentlength, struct ast_variable **cookies, 
-	unsigned int *static_content)
+static int handle_uri(struct server_instance *ser, char *uri, enum httpmethod method, 
+	struct ast_variable *headers)
 {
 	char *c;
-	struct ast_str *out = NULL;
+	int res = -1;
 	char *params = uri;
 	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;
 
 	strsep(&params, "?");
@@ -560,35 +736,23 @@
 				val = "";
 			ast_uri_decode(var);
 			if ((v = ast_variable_new(var, val, ""))) {
-				if (vars)
+				if (get_vars)
 					prev->next = v;
 				else
-					vars = v;
+					get_vars = v;
 				prev = v;
 			}
 		}
 	}
-	/*
-	 * Append the cookies to the variables (the only reason to have them
-	 * at the end is to avoid another pass of the cookies list to find
-	 * the tail).
-	 */
-	if (prev)
-		prev->next = *cookies;
-	else
-		vars = *cookies;
-	*cookies = NULL;
 	ast_uri_decode(uri);
 
 	AST_RWLIST_RDLOCK(&uri_redirects);
 	AST_RWLIST_TRAVERSE(&uri_redirects, redirect, entry) {
 		if (!strcasecmp(uri, redirect->target)) {
-			char buf[512];
-			snprintf(buf, sizeof(buf), "Location: %s\r\n", redirect->dest);
-			out = ast_http_error(302, "Moved Temporarily", buf,
-				"There is no spoon...");
-			*status = 302;
-			*title = ast_strdup("Moved Temporarily");
+			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);
+
 			break;
 		}
 	}
@@ -615,24 +779,17 @@
 				break;
 			}
 		}
-		if (!urih)
-			AST_RWLIST_UNLOCK(&uris);
+		AST_RWLIST_UNLOCK(&uris);
 	}
 	if (urih) {
-		if (urih->static_content)
-			*static_content = 1;
-		out = urih->callback(ser, uri, vars, status, title, contentlength);
-		AST_RWLIST_UNLOCK(&uris);
+		res = urih->callback(ser, uri, method, get_vars, headers);
 	} else {
-		out = ast_http_error(404, "Not Found", NULL,
-			"The requested URL was not found on this server.");
-		*status = 404;
-		*title = ast_strdup("Not Found");
+		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
@@ -682,21 +839,30 @@
 static void *httpd_helper_thread(void *data)
 {
 	char buf[4096];
-	char cookie[4096];
+	char header_line[4096];
 	struct server_instance *ser = data;
-	struct ast_variable *var, *prev=NULL, *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 *var, *headers = NULL;
+	char *uri, *method;
+	enum httpmethod http_method = 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 = HTTP_GET;
+	else if(!strcasecmp(method,"POST"))
+		http_method = HTTP_POST;
+	else if(!strcasecmp(method,"HEAD"))
+		http_method = HTTP_HEAD;
+	else if(!strcasecmp(method,"PUT"))
+		http_method = HTTP_PUT;
+
 	uri = ast_skip_blanks(uri);	/* Skip white space */
 
 	if (*uri) {			/* terminate at the first blank */
@@ -705,128 +871,58 @@
 			*c = '\0';
 	}
 
-	/* process "Cookie: " lines */
-	while (fgets(cookie, sizeof(cookie), ser->f)) {
-		char *vname, *vval;
-		int l;
+	/* 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)) {
-			char *name, *value;
-
-			value = ast_strdupa(cookie);
-			name = strsep(&value, ":");
-			if (!value)
-				continue;
-			value = ast_skip_blanks(value);
-			if (ast_strlen_zero(value))
-				continue;
-			var = ast_variable_new(name, value, "");
-			if (!var)
-				continue;
-			var->next = headers;
-			headers = var;
+	
+		/* Are ast_strdupa is needed here ? */
+		/* ast_variable_new sllocate memory himself */
+		/* value = ast_strdupa(header_line); */
+		value = header_line;
+		name = strsep(&value, ":");
+		if (!value)
 			continue;
-		}
+		value = ast_skip_blanks(value);
+		if (ast_strlen_zero(value))
+			continue;
+		var = ast_variable_new(name, value, "");
+		if (!var)
+			continue;
+		var->next = headers;
+		headers = var;
+		
+	}
 
-		/* TODO - The cookie parsing code below seems to work   
-		   in IE6 and FireFox 1.5.  However, it is not entirely 
-		   correct, and therefore may not work in all           
-		   circumstances.		                        
-		      For more details see RFC 2109 and RFC 2965        */
+	if (!*uri) {
+		ast_http_error(ser, 400, "Bad Request", "Invalid Request");
+		return NULL;
+	}
 	
-		/* FireFox cookie strings look like:                    
-		     Cookie: mansession_id="********"                   
-		   InternetExplorer's look like:                        
-		     Cookie: $Version="1"; mansession_id="********"     */
-		
-		/* If we got a FireFox cookie string, the name's right  
-		    after "Cookie: "                                    */
-		vname = ast_skip_blanks(cookie + 8);
-			
-		/* If we got an IE cookie string, we need to skip to    
-		    past the version to get to the name                 */
-		if (*vname == '$') {
-			strsep(&vname, ";");
-			if (!vname)	/* no name ? */
-				continue;
-			vname = ast_skip_blanks(vname);
-		}
-		vval = strchr(vname, '=');
-		if (!vval)
-			continue;
-		/* Ditch the = and the quotes */
-		*vval++ = '\0';
-		if (*vval)
-			vval++;
-		if ( (l = strlen(vval)) )
-			vval[l - 1] = '\0';	/* trim trailing quote */
-		var = ast_variable_new(vname, vval, "");
-		if (var) {
-			if (prev)
-				prev->next = var;
-			else
-				vars = var;
-			prev = var;
-		}
+	switch(http_method) {
+
+	case HTTP_POST:
+		handle_post(ser, uri, headers);
+		break;
+	case HTTP_GET:
+	case HTTP_HEAD:
+		handle_uri(ser, uri, http_method, headers);
+		break;
+	default:
+		ast_http_error(ser, 501, "Not Implemented", "Attempt to use unimplemented / unsupported method");
 	}
 
-	if (!*uri)
-		out = ast_http_error(400, "Bad Request", NULL, "Invalid Request");
-	else if (!strcasecmp(buf, "post")) 
-		out = handle_post(ser, uri, &status, &title, &contentlength, headers, vars);
-	else if (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, &status, &title, &contentlength, &vars, &static_content);
-
-	/* 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 tv = ast_tvnow();
-		char timebuf[256];
-		struct ast_tm tm;
-
-		ast_strftime(timebuf, sizeof(timebuf), "%a, %d %b %Y %H:%M:%S %Z", ast_localtime(&tv, &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);
-	}
-	if (title)
-		ast_free(title);
-
 done:
-	fclose(ser->f);
+	if (ser->f)
+		fclose(ser->f);
 	ast_free(ser);
 	return NULL;
 }
Index: res/res_phoneprov.c
===================================================================
--- res/res_phoneprov.c	(revision 99859)
+++ res/res_phoneprov.c	(working copy)
@@ -139,19 +139,6 @@
 static struct ao2_container *http_routes;
 static AST_RWLIST_HEAD_STATIC(users, user);
 
-/*! \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" },
-};
-
 char global_server[80] = "";	/*!< Server to substitute into templates */
 char global_serverport[6] = "";	/*!< Server port to substitute into templates */
 char global_default_profile[80] = "";	/*!< Default profile to use if one isn't specified */	
@@ -159,22 +146,7 @@
 /*! \brief List of global variables currently available: VOICEMAIL_EXTEN, EXTENSION_LENGTH */
 struct varshead global_variables;
 
-/*! \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)
 {
@@ -321,7 +293,7 @@
 }
 
 /*! \brief Callback that is executed everytime an http request is received by this module */
-static struct ast_str *phoneprov_callback(struct server_instance *ser, const char *uri, struct ast_variable *vars, int *status, char **title, int *contentlength)
+static int phoneprov_callback(struct server_instance *ser, const char *uri, enum httpmethod method, struct ast_variable *get_vars, struct ast_variable *headers)
 {
 	struct http_route *route;
 	struct http_route search_route = {
@@ -333,8 +305,7 @@
 	int len;
 	int fd;
 	char buf[256];
-	struct timeval tv = ast_tvnow();
-	struct ast_tm tm;
+	struct ast_str *http_header = ast_str_create(80);
 
 	if (!(route = ao2_find(http_routes, &search_route, OBJ_POINTER)))
 		goto out404;
@@ -355,22 +326,16 @@
 			goto out500;
 		}
 
-		ast_strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S %Z", ast_localtime(&tv, &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);
-		
+		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;
@@ -419,32 +384,27 @@
 		if (file)
 			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);
+		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, "Nothing to see here.  Move along.");
+	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
@@ -556,7 +516,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);
