[asterisk-commits] twilson: branch group/manager_http_auth r148565 - in /team/group/manager_http...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Mon Oct 13 14:01:01 CDT 2008


Author: twilson
Date: Mon Oct 13 14:01:00 2008
New Revision: 148565

URL: http://svn.digium.com/view/asterisk?view=rev&rev=148565
Log:
Initial patch commit

Modified:
    team/group/manager_http_auth/include/asterisk/http.h
    team/group/manager_http_auth/include/asterisk/utils.h
    team/group/manager_http_auth/main/http.c
    team/group/manager_http_auth/main/manager.c
    team/group/manager_http_auth/main/utils.c
    team/group/manager_http_auth/res/res_http_post.c
    team/group/manager_http_auth/res/res_phoneprov.c

Modified: team/group/manager_http_auth/include/asterisk/http.h
URL: http://svn.digium.com/view/asterisk/team/group/manager_http_auth/include/asterisk/http.h?view=diff&rev=148565&r1=148564&r2=148565
==============================================================================
--- team/group/manager_http_auth/include/asterisk/http.h (original)
+++ team/group/manager_http_auth/include/asterisk/http.h Mon Oct 13 14:01:00 2008
@@ -52,27 +52,39 @@
  * 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.
 */
-
-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);
+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);
 
 /*! \brief Definition of a URI handler */
 struct ast_http_uri {
@@ -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 */

Modified: team/group/manager_http_auth/include/asterisk/utils.h
URL: http://svn.digium.com/view/asterisk/team/group/manager_http_auth/include/asterisk/utils.h?view=diff&rev=148565&r1=148564&r2=148565
==============================================================================
--- team/group/manager_http_auth/include/asterisk/utils.h (original)
+++ team/group/manager_http_auth/include/asterisk/utils.h Mon Oct 13 14:01:00 2008
@@ -641,6 +641,31 @@
 int ast_mkdir(const char *path, int mode);
 
 #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__)

Modified: team/group/manager_http_auth/main/http.c
URL: http://svn.digium.com/view/asterisk/team/group/manager_http_auth/main/http.c?view=diff&rev=148565&r1=148564&r2=148565
==============================================================================
--- team/group/manager_http_auth/main/http.c (original)
+++ team/group/manager_http_auth/main/http.c Mon Oct 13 14:01:00 2008
@@ -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;
-}
-
-static uint32_t manid_from_vars(struct ast_variable *sid) {
-	uint32_t mngid;
-
-	while (sid && strcmp(sid->name, "mansession_id"))
-		sid = sid->next;
-	
-	if (!sid || sscanf(sid->value, "%x", &mngid) != 1)
-		return 0;
-	
+	return NULL;
+}
+
+uint32_t manid_from_vars(struct ast_variable *headers) {
+	uint32_t mngid = 0;
+	struct ast_variable *v, *cookies;
+	
+	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);
-
-	while ((len = read(fd, buf, sizeof(buf))) > 0) {
-		fwrite(buf, 1, len, ser->f);
-	}
-
+	/* make "Etag:" http header value */
+	sprintf(etag, "\"%d\"", st.st_mtime);
+
+	/* 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 NULL;
+	return 0;
 
 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.");
-}
-
-
-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)
-{
-	struct ast_str *out = ast_str_create(512);
-	struct ast_variable *v;
-
-	if (out == NULL) {
-		return out;
+	ast_http_error(ser, 403, "Access Denied", "You do not have permission to access the requested URL.");
+	return -1;
+}
+
+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;
+	struct ast_variable *v, *cookies = NULL;
+
+	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__,
 };
-	
-struct ast_str *ast_http_error(int status, const char *title, const char *extra_header, const char *text)
-{
+
+
+/* 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);
+	}
+	
+	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 (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>%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>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,
+		"<!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];
-
-			snprintf(buf, sizeof(buf), "Location: %s\r\n", redirect->dest);
-			out = ast_http_error((*status = 302),
-					     (*title = ast_strdup("Moved Temporarily")),
-					     buf, "Redirecting...");
+			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;
 		}
 	}
 	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);
-		}
-	}
-
-	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);
+	
+		/* 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;
+		}
+
+		value = ast_skip_blanks(value);
+		if (ast_strlen_zero(value) || ast_strlen_zero(name)) {
+			continue;
+		}
+
+		ast_trim_blanks(name);
+
+		if (!headers) {
+			headers = ast_variable_new(name, value, __FILE__);
+			tail = headers;
 		} else {
-			char *name, *val;
-
-			val = cookie;
-			name = strsep(&val, ":");
-			if (ast_strlen_zero(name) || ast_strlen_zero(val)) {
-				continue;
-			}
-			ast_trim_blanks(name);
-			val = ast_skip_blanks(val);
-
-			if (!headers) {
-				headers = ast_variable_new(name, val, __FILE__);
-				tail = headers;
-			} else {
-				tail->next = ast_variable_new(name, val, __FILE__);
-				tail = tail->next;
-			}
+			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);
-	}
-
-	/* If they aren't mopped up already, clean up the cookies */
-	if (vars) {
-		ast_variables_destroy(vars);
-	}
+		ast_http_error(ser, 400, "Bad Request", "Invalid Request");
+		return NULL;
+	}
+	
+	handle_uri(ser, uri, http_method, headers);
+
 	/* 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);
-	}
-
-	if (title) {
-		ast_free(title);
-	}
-
 done:
-	fclose(ser->f);
+	if (ser->f) {
+		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");
 	}

Modified: team/group/manager_http_auth/main/manager.c
URL: http://svn.digium.com/view/asterisk/team/group/manager_http_auth/main/manager.c?view=diff&rev=148565&r1=148564&r2=148565
==============================================================================
--- team/group/manager_http_auth/main/manager.c (original)
+++ team/group/manager_http_auth/main/manager.c Mon Oct 13 14:01:00 2008
@@ -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,13 +176,17 @@
 	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.
  *

[... 3278 lines stripped ...]



More information about the asterisk-commits mailing list