[asterisk-commits] tilghman: branch tilghman/str_substitution r190420 - in /team/tilghman/str_su...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Thu Apr 23 17:29:40 CDT 2009


Author: tilghman
Date: Thu Apr 23 17:29:35 2009
New Revision: 190420

URL: http://svn.digium.com/svn-view/asterisk?view=rev&rev=190420
Log:
Merged revisions 190349,190352,190357 via svnmerge from 
https://origsvn.digium.com/svn/asterisk/trunk

................
  r190349 | tilghman | 2009-04-23 15:36:35 -0500 (Thu, 23 Apr 2009) | 10 lines
  
  Support HTTP digest authentication for the http manager interface.
  (closes issue #10961)
   Reported by: ys
   Patches: 
         digest_auth_r148468_v5.diff uploaded by ys (license 281)
         SVN branch http://svn.digium.com/svn/asterisk/team/group/manager_http_auth
   Tested by: ys, twilson, tilghman
   Review: http://reviewboard.digium.com/r/223/
   Reviewed by: tilghman,russellb,mmichelson
................
  r190352 | tilghman | 2009-04-23 15:42:11 -0500 (Thu, 23 Apr 2009) | 7 lines
  
  Labels are sometimes (most of the time?) NULL for extensions.
  (closes issue #14895)
   Reported by: chris-mac
   Patches: 
         20090423__bug14895__2.diff.txt uploaded by tilghman (license 14)
   Tested by: lmadsen
................
  r190357 | russell | 2009-04-23 16:13:07 -0500 (Thu, 23 Apr 2009) | 10 lines
  
  Merged revisions 190356 via svnmerge from 
  https://origsvn.digium.com/svn/asterisk/branches/1.4
  
  ........
  r190356 | russell | 2009-04-23 16:07:07 -0500 (Thu, 23 Apr 2009) | 2 lines
  
  Remove a bogus ast_channel_unlock().
  
  ........
................

Modified:
    team/tilghman/str_substitution/   (props changed)
    team/tilghman/str_substitution/channels/chan_sip.c
    team/tilghman/str_substitution/include/asterisk/http.h
    team/tilghman/str_substitution/include/asterisk/utils.h
    team/tilghman/str_substitution/main/astobj2.c
    team/tilghman/str_substitution/main/http.c
    team/tilghman/str_substitution/main/manager.c
    team/tilghman/str_substitution/main/pbx.c
    team/tilghman/str_substitution/main/utils.c
    team/tilghman/str_substitution/res/res_http_post.c
    team/tilghman/str_substitution/res/res_phoneprov.c

Propchange: team/tilghman/str_substitution/
------------------------------------------------------------------------------
Binary property 'branch-1.4-merged' - no diff available.

Propchange: team/tilghman/str_substitution/
------------------------------------------------------------------------------
    svnmerge = *

Propchange: team/tilghman/str_substitution/
------------------------------------------------------------------------------
--- svnmerge-integrated (original)
+++ svnmerge-integrated Thu Apr 23 17:29:35 2009
@@ -1,1 +1,1 @@
-/trunk:1-190343
+/trunk:1-190418

Modified: team/tilghman/str_substitution/channels/chan_sip.c
URL: http://svn.digium.com/svn-view/asterisk/team/tilghman/str_substitution/channels/chan_sip.c?view=diff&rev=190420&r1=190419&r2=190420
==============================================================================
--- team/tilghman/str_substitution/channels/chan_sip.c (original)
+++ team/tilghman/str_substitution/channels/chan_sip.c Thu Apr 23 17:29:35 2009
@@ -20027,7 +20027,6 @@
 		ast_clear_flag(&transferer->flags[0], SIP_GOTREFER);
 		transferer->refer->status = REFER_FAILED;
 		sip_pvt_unlock(targetcall_pvt);
-		ast_channel_unlock(current->chan1);
 		if (targetcall_pvt)
 			ao2_t_ref(targetcall_pvt, -1, "Drop targetcall_pvt pointer");
 		return -1;

Modified: team/tilghman/str_substitution/include/asterisk/http.h
URL: http://svn.digium.com/svn-view/asterisk/team/tilghman/str_substitution/include/asterisk/http.h?view=diff&rev=190420&r1=190419&r2=190420
==============================================================================
--- team/tilghman/str_substitution/include/asterisk/http.h (original)
+++ team/tilghman/str_substitution/include/asterisk/http.h Thu Apr 23 17:29:35 2009
@@ -52,27 +52,40 @@
  * be run earlier in the startup process so modules have it available.
  */
 
-
-/*! \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)
-\endverbatim
-*/
-
+/*! \brief HTTP Request methods known by Asterisk */
 enum ast_http_method {
+	AST_HTTP_UNKNOWN = -1,   /*!< Unknown response */
 	AST_HTTP_GET = 0,
 	AST_HTTP_POST,
+	AST_HTTP_HEAD,
+	AST_HTTP_PUT,            /*!< Not supported in Asterisk */
 };
+
 struct ast_http_uri;
 
-typedef struct ast_str *(*ast_http_callback)(struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *params, struct ast_variable *headers, int *status, char **title, int *contentlength);
+/*! \brief HTTP Callbacks
+ *
+ * \note The callback function receives server instance, uri, http method,
+ * get method (if present in URI), and http headers as arguments and should
+ * use the ast_http_send() function for sending content allocated with ast_str
+ * and/or content from an opened file descriptor.
+ *
+ * Status and status text should be sent as arguments to the ast_http_send()
+ * function to reflect the status of the request (200 or 304, for example).
+ * Content length is calculated by ast_http_send() automatically.
+ *
+ * Static content may be indicated to the ast_http_send() function, to indicate
+ * that it may be cached.
+ *
+ * \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)
+ * \endverbatim
+ *
+ * For an error response, the ast_http_error() function may be used.
+*/
+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 +94,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 +104,9 @@
 	const char *key;
 };
 
+/*! \brief 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,8 +116,58 @@
 /*! \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
+ * \since 1.6.3
+ */
+const char *ast_get_http_method(enum ast_http_method method) attribute_pure;
+
+/*!\brief Return mime type based on extension
+ * \param ftype filename extension
+ * \return String containing associated MIME type
+ * \since 1.6.3
+ */
+const char *ast_http_ftype2mtype(const char *ftype) attribute_pure;
+
+/*!\brief Return manager id, if exist, from request headers
+ * \param headers List of HTTP headers
+ * \return 32-bit associated manager session identifier
+ * \since 1.6.3
+ */
+uint32_t ast_http_manid_from_vars(struct ast_variable *headers) attribute_pure;
+
+/*! \brief Generic function for sending http/1.1 response.
+ * \param ser TCP/TLS session object
+ * \param method GET/POST/HEAD
+ * \param status_code HTTP response code (200/401/403/404/500)
+ * \param status_title English equivalent to the status_code parameter
+ * \param http_header An ast_str object containing all headers
+ * \param out An ast_str object containing the body of the response
+ * \param fd If out is NULL, a file descriptor where the body of the response is held (otherwise -1)
+ * \param static_content Zero if the content is dynamically generated and should not be cached; nonzero otherwise
+ *
+ * \note Function determines the HTTP response header from status_code,
+ * status_header, and http_header.
+ *
+ * Extra HTTP headers MUST be present only in the http_header argument.  The
+ * argument "out" should contain only content of the response (no headers!).
+ *
+ * HTTP content can be constructed from the argument "out", if it is not NULL;
+ * otherwise, the function will read content from FD.
+ *
+ * This function calculates the content-length http header itself.
+ *
+ * Both the http_header and out arguments will be freed by this function;
+ * however, if FD is open, it will remain open.
+ *
+ * \since 1.6.3
+ */
+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" response 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
@@ -117,4 +177,15 @@
  */
 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.
+ * \param ser TCP/TLS session object
+ * \param headers List of HTTP headers
+ * \return List of variables within the POST body
+ * \note Since returned list is malloc'd, list should be free'd by the calling function
+ * \since 1.6.3
+ */
+struct ast_variable *ast_http_get_post_vars(struct ast_tcptls_session_instance *ser, struct ast_variable *headers);
+
+
 #endif /* _ASTERISK_SRV_H */

Modified: team/tilghman/str_substitution/include/asterisk/utils.h
URL: http://svn.digium.com/svn-view/asterisk/team/tilghman/str_substitution/include/asterisk/utils.h?view=diff&rev=190420&r1=190419&r2=190420
==============================================================================
--- team/tilghman/str_substitution/include/asterisk/utils.h (original)
+++ team/tilghman/str_substitution/include/asterisk/utils.h Thu Apr 23 17:29:35 2009
@@ -32,8 +32,9 @@
 #include "asterisk/time.h"
 #include "asterisk/logger.h"
 #include "asterisk/localtime.h"
-
-/*! 
+#include "asterisk/stringfields.h"
+
+/*!
 \note \verbatim
    Note:
    It is very important to use only unsigned variables to hold
@@ -645,6 +646,33 @@
 int ast_mkdir(const char *path, int mode);
 
 #define ARRAY_LEN(a) (sizeof(a) / sizeof(0[a]))
+
+
+/* Definition for Digest authorization */
+struct ast_http_digest {
+	AST_DECLARE_STRING_FIELDS(
+		AST_STRING_FIELD(username);
+		AST_STRING_FIELD(nonce);
+		AST_STRING_FIELD(uri);
+		AST_STRING_FIELD(realm);
+		AST_STRING_FIELD(domain);
+		AST_STRING_FIELD(response);
+		AST_STRING_FIELD(cnonce);
+		AST_STRING_FIELD(opaque);
+		AST_STRING_FIELD(nc);
+	);
+	int qop;		/* Flag set to 1, if we send/recv qop="quth" */
+};
+
+/*!
+ *\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/tilghman/str_substitution/main/astobj2.c
URL: http://svn.digium.com/svn-view/asterisk/team/tilghman/str_substitution/main/astobj2.c?view=diff&rev=190420&r1=190419&r2=190420
==============================================================================
--- team/tilghman/str_substitution/main/astobj2.c (original)
+++ team/tilghman/str_substitution/main/astobj2.c Thu Apr 23 17:29:35 2009
@@ -236,7 +236,7 @@
 
 	if (delta != 0) {
 		FILE *refo = fopen(REF_FILE,"a");
-		fprintf(refo, "%p %s%d   %s:%d:%s (%s) [@%d]\n", user_data, (delta<0? "":"+"), delta, file, line, funcname, tag, obj->priv_data.ref_counter);
+		fprintf(refo, "%p %s%d   %s:%d:%s (%s) [@%d]\n", user_data, (delta<0? "":"+"), delta, file, line, funcname, tag, obj ? obj->priv_data.ref_counter : -1);
 		fclose(refo);
 	}
 	if (obj->priv_data.ref_counter + delta == 0 && obj->priv_data.destructor_fn != NULL) { /* this isn't protected with lock; just for o/p */
@@ -428,7 +428,7 @@
 		return NULL;
 	
 	c->version = 1;	/* 0 is a reserved value here */
-	c->n_buckets = n_buckets;
+	c->n_buckets = hash_fn ? n_buckets : 1;
 	c->hash_fn = hash_fn ? hash_fn : hash_zero;
 	c->cmp_fn = cmp_fn;
 
@@ -444,10 +444,11 @@
 {
 	/* XXX maybe consistency check on arguments ? */
 	/* compute the container size */
-	size_t container_size = sizeof(struct ao2_container) + n_buckets * sizeof(struct bucket);
+	const unsigned int num_buckets = hash_fn ? n_buckets : 1;
+	size_t container_size = sizeof(struct ao2_container) + num_buckets * sizeof(struct bucket);
 	struct ao2_container *c = __ao2_alloc_debug(container_size, container_destruct_debug, tag, file, line, funcname);
 
-	return internal_ao2_container_alloc(c, n_buckets, hash_fn, cmp_fn);
+	return internal_ao2_container_alloc(c, num_buckets, hash_fn, cmp_fn);
 }
 
 struct ao2_container *__ao2_container_alloc(const unsigned int n_buckets, ao2_hash_fn *hash_fn,
@@ -456,10 +457,11 @@
 	/* XXX maybe consistency check on arguments ? */
 	/* compute the container size */
 
-	size_t container_size = sizeof(struct ao2_container) + n_buckets * sizeof(struct bucket);
+	const unsigned int num_buckets = hash_fn ? n_buckets : 1;
+	size_t container_size = sizeof(struct ao2_container) + num_buckets * sizeof(struct bucket);
 	struct ao2_container *c = __ao2_alloc(container_size, container_destruct);
 
-	return internal_ao2_container_alloc(c, n_buckets, hash_fn, cmp_fn);
+	return internal_ao2_container_alloc(c, num_buckets, hash_fn, cmp_fn);
 }
 
 /*!

Modified: team/tilghman/str_substitution/main/http.c
URL: http://svn.digium.com/svn-view/asterisk/team/tilghman/str_substitution/main/http.c?view=diff&rev=190420&r1=190419&r2=190420
==============================================================================
--- team/tilghman/str_substitution/main/http.c (original)
+++ team/tilghman/str_substitution/main/http.c Thu Apr 23 17:29:35 2009
@@ -17,14 +17,14 @@
  */
 
 /*!
- * \file 
+ * \file
  * \brief http server for AMI access
  *
  * \author Mark Spencer <markster at digium.com>
  *
  * This program implements a tiny http server
- * and was inspired by micro-httpd by Jef Poskanzer 
- * 
+ * and was inspired by micro-httpd by Jef Poskanzer
+ *
  * \ref AstHTTP - AMI over the http protocol
  */
 
@@ -98,6 +98,7 @@
 	const char *mtype;
 } mimetypes[] = {
 	{ "png", "image/png" },
+	{ "xml", "text/xml" },
 	{ "jpg", "image/jpeg" },
 	{ "js", "application/x-javascript" },
 	{ "wav", "audio/x-wav" },
@@ -115,7 +116,23 @@
 
 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;
+	const char 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" },
+};
+
+const char *ast_get_http_method(enum ast_http_method method)
+{
+	return ast_http_methods_text[method].text;
+}
+
+const char *ast_http_ftype2mtype(const char *ftype)
 {
 	int x;
 
@@ -126,21 +143,24 @@
 			}
 		}
 	}
-
-	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 ast_http_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;
 }
 
@@ -151,20 +171,31 @@
 	}
 }
 
-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 *ftype;
 	const 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;
-
-	/* Yuck.  I'm not really sold on this, but if you don't deliver static content it makes your configuration 
+	char timebuf[80], etag[23];
+	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;
@@ -178,18 +209,20 @@
 	if (strstr(uri, "/..")) {
 		goto out403;
 	}
-		
+
 	if ((ftype = strrchr(uri, '.'))) {
 		ftype++;
 	}
 
-	mtype = ftype2mtype(ftype, wkspace, sizeof(wkspace));
-	
+	if (!(mtype = ast_http_ftype2mtype(ftype))) {
+		snprintf(wkspace, sizeof(wkspace), "text/%s", S_OR(ftype, "plain"));
+	}
+
 	/* Cap maximum length */
 	if ((len = strlen(uri) + strlen(ast_config_AST_DATA_DIR) + strlen("/static-http/") + 5) > 1024) {
 		goto out403;
 	}
-		
+
 	path = alloca(len);
 	sprintf(path, "%s/static-http/%s", ast_config_AST_DATA_DIR, uri);
 	if (stat(path, &st)) {
@@ -198,142 +231,275 @@
 
 	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(ast_http_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: private\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) {
-		if (fwrite(buf, 1, len, ser->f) != len) {
-			ast_log(LOG_WARNING, "fwrite() failed: %s\n", strerror(errno));
-		}
-	}
-
+	/* make "Etag:" http header value */
+	snprintf(etag, sizeof(etag), "\"%ld\"", (long)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);
+
+	/* ast_http_send() frees http_header, so we don't need to do it before returning */
+	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.old_address.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.old_address.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.old_address.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__,
 };
-	
+
 static struct ast_http_uri staticuri = {
 	.callback = static_callback,
 	.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(ast_str_buffer(out));
+	}
+
+	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 ? ast_str_buffer(http_header) : ""
+		);
+
+	/* send content */
+	if (method != AST_HTTP_HEAD || status_code >= 400) {
+		if (out) {
+			fprintf(ser->f, "%s", ast_str_buffer(out));
+		}
+
+		if (fd) {
+			char buf[256];
+			int len;
+			while ((len = read(fd, buf, sizeof(buf))) > 0) {
+				if (fwrite(buf, len, 1, ser->f) != len) {
+					ast_log(LOG_WARNING, "fwrite() failed: %s\n", strerror(errno));
+				}
+			}
+		}
+	}
+
+	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 || !out) {
+		ast_free(http_headers);
+		ast_free(out);
+		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;
-}
-
-/*! \brief 
- * Link the new uri into the list. 
+		"<!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 || !out) {
+		ast_free(http_headers);
+		ast_free(out);
+		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
+ * Link the new uri into the list.
  *
  * They are sorted by length of
  * the string, not alphabetically. Duplicate entries are not replaced,
@@ -346,25 +512,19 @@
 	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) {
+			strlen(AST_RWLIST_NEXT(uri, entry)->uri) <= len) {
 			AST_RWLIST_INSERT_AFTER(&uris, uri, urih, entry);
-			AST_RWLIST_UNLOCK(&uris); 
+			AST_RWLIST_UNLOCK(&uris);
 
 			return 0;
 		}
@@ -373,9 +533,9 @@
 	AST_RWLIST_INSERT_TAIL(&uris, urih, entry);
 
 	AST_RWLIST_UNLOCK(&uris);
-	
+
 	return 0;
-}	
+}
 
 void ast_http_uri_unlink(struct ast_http_uri *urih)
 {
@@ -411,91 +571,121 @@
 static void http_decode(char *s)
 {
 	char *t;
-	
+
 	for (t = s; *t; t++) {
-		if (*t == '+')
+		if (*t == '+') {
 			*t = ' ';
+		}
 	}
 
 	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 *ast_http_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;
 	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;
 	}
@@ -508,70 +698,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
@@ -624,12 +775,9 @@
 	char *cur;
 	struct ast_variable *vars = NULL, *var;
 
-	/* Skip Cookie: */
-	cookies += 8;
-
 	while ((cur = strsep(&cookies, ";"))) {
 		char *name, *val;
-		
+
 		name = val = cur;
 		strsep(&val, "=");
 
@@ -656,27 +804,55 @@
 	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 */
@@ -687,101 +863,56 @@
 		}
 	}
 
-	/* 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);
+
+		value = header_line;
+		name = strsep(&value, ":");
+		if (!value) {
+			continue;
+		}

[... 3774 lines stripped ...]



More information about the asterisk-commits mailing list