mirror of
https://github.com/asterisk/asterisk.git
synced 2025-09-05 20:20:07 +00:00
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 git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@190349 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
@@ -52,27 +52,40 @@
|
|||||||
* be run earlier in the startup process so modules have it available.
|
* be run earlier in the startup process so modules have it available.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/*! \brief HTTP Request methods known by 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)
|
|
||||||
\endverbatim
|
|
||||||
*/
|
|
||||||
|
|
||||||
enum ast_http_method {
|
enum ast_http_method {
|
||||||
|
AST_HTTP_UNKNOWN = -1, /*!< Unknown response */
|
||||||
AST_HTTP_GET = 0,
|
AST_HTTP_GET = 0,
|
||||||
AST_HTTP_POST,
|
AST_HTTP_POST,
|
||||||
|
AST_HTTP_HEAD,
|
||||||
|
AST_HTTP_PUT, /*!< Not supported in Asterisk */
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ast_http_uri;
|
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 */
|
/*! \brief Definition of a URI handler */
|
||||||
struct ast_http_uri {
|
struct ast_http_uri {
|
||||||
@@ -81,12 +94,6 @@ struct ast_http_uri {
|
|||||||
const char *uri;
|
const char *uri;
|
||||||
ast_http_callback callback;
|
ast_http_callback callback;
|
||||||
unsigned int has_subtree:1;
|
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 */
|
/*! Structure is malloc'd */
|
||||||
unsigned int mallocd:1;
|
unsigned int mallocd:1;
|
||||||
/*! Data structure is malloc'd */
|
/*! Data structure is malloc'd */
|
||||||
@@ -97,6 +104,9 @@ struct ast_http_uri {
|
|||||||
const char *key;
|
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 */
|
/*! \brief Register a URI handler */
|
||||||
int ast_http_uri_link(struct ast_http_uri *urihandler);
|
int ast_http_uri_link(struct ast_http_uri *urihandler);
|
||||||
|
|
||||||
@@ -106,8 +116,58 @@ void ast_http_uri_unlink(struct ast_http_uri *urihandler);
|
|||||||
/*! \brief Unregister all handlers with matching key */
|
/*! \brief Unregister all handlers with matching key */
|
||||||
void ast_http_uri_unlink_all_with_key(const char *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 */
|
/*!\brief Return http method name string
|
||||||
struct ast_str *ast_http_error(int status, const char *title, const char *extra_header, const char *text);
|
* \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
|
* \brief Return the current prefix
|
||||||
@@ -117,4 +177,15 @@ struct ast_str *ast_http_error(int status, const char *title, const char *extra_
|
|||||||
*/
|
*/
|
||||||
void ast_http_prefix(char *buf, int len);
|
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 */
|
#endif /* _ASTERISK_SRV_H */
|
||||||
|
@@ -32,6 +32,7 @@
|
|||||||
#include "asterisk/time.h"
|
#include "asterisk/time.h"
|
||||||
#include "asterisk/logger.h"
|
#include "asterisk/logger.h"
|
||||||
#include "asterisk/localtime.h"
|
#include "asterisk/localtime.h"
|
||||||
|
#include "asterisk/stringfields.h"
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\note \verbatim
|
\note \verbatim
|
||||||
@@ -646,6 +647,33 @@ int ast_mkdir(const char *path, int mode);
|
|||||||
|
|
||||||
#define ARRAY_LEN(a) (sizeof(a) / sizeof(0[a]))
|
#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
|
#ifdef AST_DEVMODE
|
||||||
#define ast_assert(a) _ast_assert(a, # a, __FILE__, __LINE__, __PRETTY_FUNCTION__)
|
#define ast_assert(a) _ast_assert(a, # a, __FILE__, __LINE__, __PRETTY_FUNCTION__)
|
||||||
static void force_inline _ast_assert(int condition, const char *condition_str,
|
static void force_inline _ast_assert(int condition, const char *condition_str,
|
||||||
|
@@ -236,7 +236,7 @@ int __ao2_ref_debug(void *user_data, const int delta, char *tag, char *file, int
|
|||||||
|
|
||||||
if (delta != 0) {
|
if (delta != 0) {
|
||||||
FILE *refo = fopen(REF_FILE,"a");
|
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);
|
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 */
|
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 @@ static struct ao2_container *internal_ao2_container_alloc(struct ao2_container *
|
|||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
c->version = 1; /* 0 is a reserved value here */
|
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->hash_fn = hash_fn ? hash_fn : hash_zero;
|
||||||
c->cmp_fn = cmp_fn;
|
c->cmp_fn = cmp_fn;
|
||||||
|
|
||||||
@@ -444,10 +444,11 @@ struct ao2_container *__ao2_container_alloc_debug(const unsigned int n_buckets,
|
|||||||
{
|
{
|
||||||
/* XXX maybe consistency check on arguments ? */
|
/* XXX maybe consistency check on arguments ? */
|
||||||
/* compute the container size */
|
/* 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);
|
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,
|
struct ao2_container *__ao2_container_alloc(const unsigned int n_buckets, ao2_hash_fn *hash_fn,
|
||||||
@@ -456,10 +457,11 @@ struct ao2_container *__ao2_container_alloc(const unsigned int n_buckets, ao2_ha
|
|||||||
/* XXX maybe consistency check on arguments ? */
|
/* XXX maybe consistency check on arguments ? */
|
||||||
/* compute the container size */
|
/* 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);
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
729
main/http.c
729
main/http.c
@@ -98,6 +98,7 @@ static struct {
|
|||||||
const char *mtype;
|
const char *mtype;
|
||||||
} mimetypes[] = {
|
} mimetypes[] = {
|
||||||
{ "png", "image/png" },
|
{ "png", "image/png" },
|
||||||
|
{ "xml", "text/xml" },
|
||||||
{ "jpg", "image/jpeg" },
|
{ "jpg", "image/jpeg" },
|
||||||
{ "js", "application/x-javascript" },
|
{ "js", "application/x-javascript" },
|
||||||
{ "wav", "audio/x-wav" },
|
{ "wav", "audio/x-wav" },
|
||||||
@@ -115,7 +116,23 @@ struct http_uri_redirect {
|
|||||||
|
|
||||||
static AST_RWLIST_HEAD_STATIC(uri_redirects, http_uri_redirect);
|
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;
|
int x;
|
||||||
|
|
||||||
@@ -126,21 +143,24 @@ static const char *ftype2mtype(const char *ftype, char *wkspace, int wkspacelen)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return NULL;
|
||||||
snprintf(wkspace, wkspacelen, "text/%s", S_OR(ftype, "plain"));
|
|
||||||
|
|
||||||
return wkspace;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint32_t manid_from_vars(struct ast_variable *sid) {
|
uint32_t ast_http_manid_from_vars(struct ast_variable *headers)
|
||||||
uint32_t mngid;
|
{
|
||||||
|
uint32_t mngid = 0;
|
||||||
while (sid && strcmp(sid->name, "mansession_id"))
|
struct ast_variable *v, *cookies;
|
||||||
sid = sid->next;
|
|
||||||
|
|
||||||
if (!sid || sscanf(sid->value, "%x", &mngid) != 1)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
|
cookies = ast_http_get_cookies(headers);
|
||||||
|
for (v = cookies; v; v = v->next) {
|
||||||
|
if (!strcasecmp(v->name, "mansession_id")) {
|
||||||
|
sscanf(v->value, "%x", &mngid);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (cookies) {
|
||||||
|
ast_variables_destroy(cookies);
|
||||||
|
}
|
||||||
return mngid;
|
return mngid;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,18 +171,29 @@ void ast_http_prefix(char *buf, int len)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 *path;
|
||||||
char *ftype;
|
const char *ftype;
|
||||||
const char *mtype;
|
const char *mtype;
|
||||||
char wkspace[80];
|
char wkspace[80];
|
||||||
struct stat st;
|
struct stat st;
|
||||||
int len;
|
int len;
|
||||||
int fd;
|
int fd;
|
||||||
struct timeval now = ast_tvnow();
|
struct ast_str *http_header;
|
||||||
char buf[256];
|
struct timeval tv;
|
||||||
struct ast_tm tm;
|
struct ast_tm tm;
|
||||||
|
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
|
/* 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. */
|
substantially more challenging, but this seems like a rather irritating feature creep on Asterisk. */
|
||||||
@@ -183,7 +214,9 @@ static struct ast_str *static_callback(struct ast_tcptls_session_instance *ser,
|
|||||||
ftype++;
|
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 */
|
/* Cap maximum length */
|
||||||
if ((len = strlen(uri) + strlen(ast_config_AST_DATA_DIR) + strlen("/static-http/") + 5) > 1024) {
|
if ((len = strlen(uri) + strlen(ast_config_AST_DATA_DIR) + strlen("/static-http/") + 5) > 1024) {
|
||||||
@@ -200,97 +233,116 @@ static struct ast_str *static_callback(struct ast_tcptls_session_instance *ser,
|
|||||||
goto out404;
|
goto out404;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((fd = open(path, O_RDONLY)) < 0) {
|
fd = open(path, O_RDONLY);
|
||||||
|
if (fd < 0) {
|
||||||
goto out403;
|
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;
|
goto out403;
|
||||||
}
|
}
|
||||||
|
|
||||||
ast_strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S %Z", ast_localtime(&now, &tm, "GMT"));
|
/* make "Etag:" http header value */
|
||||||
fprintf(ser->f, "HTTP/1.1 200 OK\r\n"
|
snprintf(etag, sizeof(etag), "\"%ld\"", (long)st.st_mtime);
|
||||||
"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) {
|
/* make "Last-Modified:" http header value */
|
||||||
if (fwrite(buf, 1, len, ser->f) != len) {
|
tv.tv_sec = st.st_mtime;
|
||||||
ast_log(LOG_WARNING, "fwrite() failed: %s\n", strerror(errno));
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
close(fd);
|
if ( (http_header = ast_str_create(255)) == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
return NULL;
|
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 0;
|
||||||
|
|
||||||
out404:
|
out404:
|
||||||
return ast_http_error((*status = 404),
|
ast_http_error(ser, 404, "Not Found", "The requested URL was not found on this server.");
|
||||||
(*title = ast_strdup("Not Found")),
|
return -1;
|
||||||
NULL, "The requested URL was not found on this server.");
|
|
||||||
|
|
||||||
out403:
|
out403:
|
||||||
return ast_http_error((*status = 403),
|
ast_http_error(ser, 403, "Access Denied", "You do not have permission to access the requested URL.");
|
||||||
(*title = ast_strdup("Access Denied")),
|
return -1;
|
||||||
NULL, "You do not have permission to access the requested URL.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int httpstatus_callback(struct ast_tcptls_session_instance *ser,
|
||||||
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)
|
const struct ast_http_uri *urih, const char *uri,
|
||||||
|
enum ast_http_method method, struct ast_variable *get_vars,
|
||||||
|
struct ast_variable *headers)
|
||||||
{
|
{
|
||||||
struct ast_str *out = ast_str_create(512);
|
struct ast_str *out;
|
||||||
struct ast_variable *v;
|
struct ast_variable *v, *cookies = NULL;
|
||||||
|
|
||||||
if (out == NULL) {
|
if (method != AST_HTTP_GET && method != AST_HTTP_HEAD) {
|
||||||
return out;
|
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,
|
ast_str_append(&out, 0,
|
||||||
"\r\n"
|
"<title>Asterisk HTTP Status</title>\r\n"
|
||||||
"<title>Asterisk HTTP Status</title>\r\n"
|
"<body bgcolor=\"#ffffff\">\r\n"
|
||||||
"<body bgcolor=\"#ffffff\">\r\n"
|
"<table bgcolor=\"#f1f1f1\" align=\"center\"><tr><td bgcolor=\"#e0e0ff\" colspan=\"2\" width=\"500\">\r\n"
|
||||||
"<table bgcolor=\"#f1f1f1\" align=\"center\"><tr><td bgcolor=\"#e0e0ff\" colspan=\"2\" width=\"500\">\r\n"
|
"<h2> Asterisk™ HTTP Status</h2></td></tr>\r\n");
|
||||||
"<h2> Asterisk™ 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>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_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_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",
|
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));
|
ntohs(http_desc.old_address.sin_port));
|
||||||
|
|
||||||
if (http_tls_cfg.enabled) {
|
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",
|
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));
|
ntohs(https_desc.old_address.sin_port));
|
||||||
}
|
}
|
||||||
|
ast_str_append(&out, 0, "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
|
||||||
|
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");
|
ast_str_append(&out, 0, "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
|
||||||
|
|
||||||
for (v = vars; v; v = v->next) {
|
cookies = ast_http_get_cookies(headers);
|
||||||
if (strncasecmp(v->name, "cookie_", 7)) {
|
for (v = cookies; v; v = v->next) {
|
||||||
ast_str_append(&out, 0, "<tr><td><i>Submitted Variable '%s'</i></td><td>%s</td></tr>\r\n", v->name, v->value);
|
ast_str_append(&out, 0, "<tr><td><i>Cookie '%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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
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");
|
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 = {
|
static struct ast_http_uri statusuri = {
|
||||||
.callback = httpstatus_callback,
|
.callback = httpstatus_callback,
|
||||||
.description = "Asterisk HTTP General Status",
|
.description = "Asterisk HTTP General Status",
|
||||||
.uri = "httpstatus",
|
.uri = "httpstatus",
|
||||||
.supports_get = 1,
|
.has_subtree = 0,
|
||||||
.data = NULL,
|
.data = NULL,
|
||||||
.key = __FILE__,
|
.key = __FILE__,
|
||||||
};
|
};
|
||||||
@@ -300,36 +352,150 @@ static struct ast_http_uri staticuri = {
|
|||||||
.description = "Asterisk HTTP Static Delivery",
|
.description = "Asterisk HTTP Static Delivery",
|
||||||
.uri = "static",
|
.uri = "static",
|
||||||
.has_subtree = 1,
|
.has_subtree = 1,
|
||||||
.static_content = 1,
|
|
||||||
.supports_get = 1,
|
|
||||||
.data = NULL,
|
.data = NULL,
|
||||||
.key= __FILE__,
|
.key= __FILE__,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ast_str *ast_http_error(int status, const char *title, const char *extra_header, const char *text)
|
|
||||||
{
|
|
||||||
struct ast_str *out = ast_str_create(512);
|
|
||||||
|
|
||||||
if (out == NULL) {
|
/* send http/1.1 responce */
|
||||||
return out;
|
/* 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_str_set(&out, 0,
|
ast_strftime(timebuf, sizeof(timebuf), "%a, %d %b %Y %H:%M:%S GMT", ast_localtime(&now, &tm, "GMT"));
|
||||||
"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;
|
/* 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 (!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,
|
||||||
|
"<!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
|
/*! \brief
|
||||||
@@ -346,23 +512,17 @@ int ast_http_uri_link(struct ast_http_uri *urih)
|
|||||||
struct ast_http_uri *uri;
|
struct ast_http_uri *uri;
|
||||||
int len = strlen(urih->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);
|
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_INSERT_HEAD(&uris, urih, entry);
|
||||||
AST_RWLIST_UNLOCK(&uris);
|
AST_RWLIST_UNLOCK(&uris);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
AST_RWLIST_TRAVERSE(&uris, uri, entry) {
|
AST_RWLIST_TRAVERSE(&uris, uri, entry) {
|
||||||
if (AST_RWLIST_NEXT(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_INSERT_AFTER(&uris, uri, urih, entry);
|
||||||
AST_RWLIST_UNLOCK(&uris);
|
AST_RWLIST_UNLOCK(&uris);
|
||||||
|
|
||||||
@@ -413,89 +573,119 @@ static void http_decode(char *s)
|
|||||||
char *t;
|
char *t;
|
||||||
|
|
||||||
for (t = s; *t; t++) {
|
for (t = s; *t; t++) {
|
||||||
if (*t == '+')
|
if (*t == '+') {
|
||||||
*t = ' ';
|
*t = ' ';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ast_uri_decode(s);
|
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,
|
* get post variables from client Request Entity-Body, if content type is
|
||||||
unsigned int *static_content)
|
* application/x-www-form-urlencoded
|
||||||
|
*/
|
||||||
|
struct ast_variable *ast_http_get_post_vars(
|
||||||
|
struct ast_tcptls_session_instance *ser, struct ast_variable *headers)
|
||||||
{
|
{
|
||||||
char *c;
|
int content_length = 0;
|
||||||
struct ast_str *out = NULL;
|
struct ast_variable *v, *post_vars=NULL, *prev = NULL;
|
||||||
char *params = uri;
|
char *buf, *var, *val;
|
||||||
struct ast_http_uri *urih = NULL;
|
|
||||||
int l;
|
|
||||||
struct ast_variable *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 */
|
for (v = headers; v; v = v->next) {
|
||||||
if (method == AST_HTTP_GET) {
|
if (!strcasecmp(v->name, "Content-Type")) {
|
||||||
strsep(¶ms, "?");
|
if (strcasecmp(v->value, "application/x-www-form-urlencoded")) {
|
||||||
|
return NULL;
|
||||||
/* 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(¶ms, "&"))) {
|
|
||||||
var = strsep(&val, "=");
|
|
||||||
if (val) {
|
|
||||||
http_decode(val);
|
|
||||||
} else {
|
|
||||||
val = "";
|
|
||||||
}
|
|
||||||
http_decode(var);
|
|
||||||
if ((v = ast_variable_new(var, val, ""))) {
|
|
||||||
if (vars) {
|
|
||||||
prev->next = v;
|
|
||||||
} else {
|
|
||||||
vars = v;
|
|
||||||
}
|
|
||||||
prev = v;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
for (v = headers; v; v = v->next) {
|
||||||
* Append the cookies to the list of variables.
|
if (!strcasecmp(v->name, "Content-Length")) {
|
||||||
* This saves a pass in the cookies list, but has the side effect
|
content_length = atoi(v->value) + 1;
|
||||||
* that a variable might mask a cookie with the same name if the
|
break;
|
||||||
* 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;
|
|
||||||
|
|
||||||
|
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;
|
||||||
|
int res = -1;
|
||||||
|
char *params = uri;
|
||||||
|
struct ast_http_uri *urih = NULL;
|
||||||
|
int l;
|
||||||
|
struct ast_variable *get_vars = NULL, *v, *prev = NULL;
|
||||||
|
struct http_uri_redirect *redirect;
|
||||||
|
|
||||||
|
strsep(¶ms, "?");
|
||||||
|
/* Extract arguments from the request and store them in variables. */
|
||||||
|
if (params) {
|
||||||
|
char *var, *val;
|
||||||
|
|
||||||
|
while ((val = strsep(¶ms, "&"))) {
|
||||||
|
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 {
|
||||||
|
get_vars = v;
|
||||||
|
}
|
||||||
|
prev = v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
http_decode(uri);
|
http_decode(uri);
|
||||||
|
|
||||||
AST_RWLIST_RDLOCK(&uri_redirects);
|
AST_RWLIST_RDLOCK(&uri_redirects);
|
||||||
AST_RWLIST_TRAVERSE(&uri_redirects, redirect, entry) {
|
AST_RWLIST_TRAVERSE(&uri_redirects, redirect, entry) {
|
||||||
if (!strcasecmp(uri, redirect->target)) {
|
if (!strcasecmp(uri, redirect->target)) {
|
||||||
char buf[512];
|
struct ast_str *http_header = ast_str_create(128);
|
||||||
|
ast_str_set(&http_header, 0, "Location: %s\r\n", redirect->dest);
|
||||||
snprintf(buf, sizeof(buf), "Location: %s\r\n", redirect->dest);
|
ast_http_send(ser, method, 302, "Moved Temporarily", http_header, NULL, 0, 0);
|
||||||
out = ast_http_error((*status = 302),
|
|
||||||
(*title = ast_strdup("Moved Temporarily")),
|
|
||||||
buf, "Redirecting...");
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
AST_RWLIST_UNLOCK(&uri_redirects);
|
AST_RWLIST_UNLOCK(&uri_redirects);
|
||||||
|
|
||||||
if (redirect) {
|
if (redirect) {
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
@@ -508,70 +698,31 @@ static struct ast_str *handle_uri(struct ast_tcptls_session_instance *ser, char
|
|||||||
AST_RWLIST_RDLOCK(&uris);
|
AST_RWLIST_RDLOCK(&uris);
|
||||||
AST_RWLIST_TRAVERSE(&uris, urih, entry) {
|
AST_RWLIST_TRAVERSE(&uris, urih, entry) {
|
||||||
ast_debug(2, "match request [%s] with handler [%s] len %d\n", uri, urih->uri, l);
|
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);
|
l = strlen(urih->uri);
|
||||||
c = uri + l; /* candidate */
|
c = uri + l; /* candidate */
|
||||||
|
if (strncasecmp(urih->uri, uri, l) /* no match */
|
||||||
if (strncasecmp(urih->uri, uri, l) || /* no match */
|
|| (*c && *c != '/')) { /* substring */
|
||||||
(*c && *c != '/')) { /* substring */
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (*c == '/') {
|
if (*c == '/') {
|
||||||
c++;
|
c++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!*c || urih->has_subtree) {
|
if (!*c || urih->has_subtree) {
|
||||||
if (((method == AST_HTTP_GET) && urih->supports_get) ||
|
uri = c;
|
||||||
((method == AST_HTTP_POST) && urih->supports_post)) {
|
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);
|
AST_RWLIST_UNLOCK(&uris);
|
||||||
} else if (saw_method) {
|
}
|
||||||
out = ast_http_error((*status = 404),
|
if (urih) {
|
||||||
(*title = ast_strdup("Not Found")), NULL,
|
res = urih->callback(ser, urih, uri, method, get_vars, headers);
|
||||||
"The requested URL was not found on this server.");
|
|
||||||
} else {
|
} else {
|
||||||
out = ast_http_error((*status = 501),
|
ast_http_error(ser, 404, "Not Found", "The requested URL was not found on this server.");
|
||||||
(*title = ast_strdup("Not Implemented")), NULL,
|
|
||||||
"Attempt to use unimplemented / unsupported method");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cleanup:
|
cleanup:
|
||||||
ast_variables_destroy(vars);
|
ast_variables_destroy(get_vars);
|
||||||
|
return res;
|
||||||
return out;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DO_SSL
|
#ifdef DO_SSL
|
||||||
@@ -624,9 +775,6 @@ static struct ast_variable *parse_cookies(char *cookies)
|
|||||||
char *cur;
|
char *cur;
|
||||||
struct ast_variable *vars = NULL, *var;
|
struct ast_variable *vars = NULL, *var;
|
||||||
|
|
||||||
/* Skip Cookie: */
|
|
||||||
cookies += 8;
|
|
||||||
|
|
||||||
while ((cur = strsep(&cookies, ";"))) {
|
while ((cur = strsep(&cookies, ";"))) {
|
||||||
char *name, *val;
|
char *name, *val;
|
||||||
|
|
||||||
@@ -656,27 +804,55 @@ static struct ast_variable *parse_cookies(char *cookies)
|
|||||||
return vars;
|
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)
|
static void *httpd_helper_thread(void *data)
|
||||||
{
|
{
|
||||||
char buf[4096];
|
char buf[4096];
|
||||||
char cookie[4096];
|
char header_line[4096];
|
||||||
struct ast_tcptls_session_instance *ser = data;
|
struct ast_tcptls_session_instance *ser = data;
|
||||||
struct ast_variable *vars=NULL, *headers = NULL;
|
struct ast_variable *headers = NULL;
|
||||||
char *uri, *title=NULL;
|
|
||||||
int status = 200, contentlength = 0;
|
|
||||||
struct ast_str *out = NULL;
|
|
||||||
unsigned int static_content = 0;
|
|
||||||
struct ast_variable *tail = headers;
|
struct ast_variable *tail = headers;
|
||||||
|
char *uri, *method;
|
||||||
|
enum ast_http_method http_method = AST_HTTP_UNKNOWN;
|
||||||
|
|
||||||
if (!fgets(buf, sizeof(buf), ser->f)) {
|
if (!fgets(buf, sizeof(buf), ser->f)) {
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
uri = ast_skip_nonblanks(buf); /* Skip method */
|
/* Get method */
|
||||||
|
method = ast_skip_blanks(buf);
|
||||||
|
uri = ast_skip_nonblanks(method);
|
||||||
if (*uri) {
|
if (*uri) {
|
||||||
*uri++ = '\0';
|
*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 */
|
uri = ast_skip_blanks(uri); /* Skip white space */
|
||||||
|
|
||||||
if (*uri) { /* terminate at the first blank */
|
if (*uri) { /* terminate at the first blank */
|
||||||
@@ -687,101 +863,56 @@ static void *httpd_helper_thread(void *data)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* process "Cookie: " lines */
|
/* process "Request Headers" lines */
|
||||||
while (fgets(cookie, sizeof(cookie), ser->f)) {
|
while (fgets(header_line, sizeof(header_line), ser->f)) {
|
||||||
|
char *name, *value;
|
||||||
|
|
||||||
/* Trim trailing characters */
|
/* Trim trailing characters */
|
||||||
ast_trim_blanks(cookie);
|
ast_trim_blanks(header_line);
|
||||||
if (ast_strlen_zero(cookie)) {
|
if (ast_strlen_zero(header_line)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (!strncasecmp(cookie, "Cookie: ", 8)) {
|
|
||||||
vars = parse_cookies(cookie);
|
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 {
|
} else {
|
||||||
char *name, *val;
|
tail->next = ast_variable_new(name, value, __FILE__);
|
||||||
|
tail = tail->next;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!*uri) {
|
if (!*uri) {
|
||||||
out = ast_http_error(400, "Bad Request", NULL, "Invalid Request");
|
ast_http_error(ser, 400, "Bad Request", "Invalid Request");
|
||||||
} else if (strcasecmp(buf, "post") && strcasecmp(buf, "get")) {
|
return NULL;
|
||||||
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 */
|
handle_uri(ser, uri, http_method, headers);
|
||||||
if (vars) {
|
|
||||||
ast_variables_destroy(vars);
|
|
||||||
}
|
|
||||||
/* Clean up all the header information pulled as well */
|
/* Clean up all the header information pulled as well */
|
||||||
if (headers) {
|
if (headers) {
|
||||||
ast_variables_destroy(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", ast_str_buffer(out));
|
|
||||||
} else {
|
|
||||||
char *tmp = strstr(ast_str_buffer(out), "\r\n\r\n");
|
|
||||||
|
|
||||||
if (tmp) {
|
|
||||||
fprintf(ser->f, "Content-length: %d\r\n", contentlength);
|
|
||||||
/* first write the header, then the body */
|
|
||||||
if (fwrite(ast_str_buffer(out), 1, (tmp + 4 - ast_str_buffer(out)), ser->f) != tmp + 4 - ast_str_buffer(out)) {
|
|
||||||
ast_log(LOG_WARNING, "fwrite() failed: %s\n", strerror(errno));
|
|
||||||
}
|
|
||||||
if (fwrite(tmp + 4, 1, contentlength, ser->f) != contentlength ) {
|
|
||||||
ast_log(LOG_WARNING, "fwrite() failed: %s\n", strerror(errno));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ast_free(out);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (title) {
|
|
||||||
ast_free(title);
|
|
||||||
}
|
|
||||||
|
|
||||||
done:
|
done:
|
||||||
fclose(ser->f);
|
if (ser->f) {
|
||||||
|
fclose(ser->f);
|
||||||
|
}
|
||||||
ao2_ref(ser, -1);
|
ao2_ref(ser, -1);
|
||||||
ser = NULL;
|
ser = NULL;
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -814,7 +945,6 @@ static void add_redirect(const char *value)
|
|||||||
if (!(redirect = ast_calloc(1, total_len))) {
|
if (!(redirect = ast_calloc(1, total_len))) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
redirect->dest = redirect->target + target_len;
|
redirect->dest = redirect->target + target_len;
|
||||||
strcpy(redirect->target, target);
|
strcpy(redirect->target, target);
|
||||||
strcpy(redirect->dest, dest);
|
strcpy(redirect->dest, dest);
|
||||||
@@ -823,7 +953,7 @@ static void add_redirect(const char *value)
|
|||||||
|
|
||||||
target_len--; /* So we can compare directly with strlen() */
|
target_len--; /* So we can compare directly with strlen() */
|
||||||
if (AST_RWLIST_EMPTY(&uri_redirects)
|
if (AST_RWLIST_EMPTY(&uri_redirects)
|
||||||
|| strlen(AST_RWLIST_FIRST(&uri_redirects)->target) <= target_len) {
|
|| strlen(AST_RWLIST_FIRST(&uri_redirects)->target) <= target_len ) {
|
||||||
AST_RWLIST_INSERT_HEAD(&uri_redirects, redirect, entry);
|
AST_RWLIST_INSERT_HEAD(&uri_redirects, redirect, entry);
|
||||||
AST_RWLIST_UNLOCK(&uri_redirects);
|
AST_RWLIST_UNLOCK(&uri_redirects);
|
||||||
|
|
||||||
@@ -832,10 +962,9 @@ static void add_redirect(const char *value)
|
|||||||
|
|
||||||
AST_RWLIST_TRAVERSE(&uri_redirects, cur, entry) {
|
AST_RWLIST_TRAVERSE(&uri_redirects, cur, entry) {
|
||||||
if (AST_RWLIST_NEXT(cur, entry)
|
if (AST_RWLIST_NEXT(cur, entry)
|
||||||
&& strlen(AST_RWLIST_NEXT(cur, entry)->target) <= target_len) {
|
&& strlen(AST_RWLIST_NEXT(cur, entry)->target) <= target_len ) {
|
||||||
AST_RWLIST_INSERT_AFTER(&uri_redirects, cur, redirect, entry);
|
AST_RWLIST_INSERT_AFTER(&uri_redirects, cur, redirect, entry);
|
||||||
AST_RWLIST_UNLOCK(&uri_redirects);
|
AST_RWLIST_UNLOCK(&uri_redirects);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -992,17 +1121,15 @@ static char *handle_show_http(struct ast_cli_entry *e, int cmd, struct ast_cli_a
|
|||||||
if (AST_RWLIST_EMPTY(&uris)) {
|
if (AST_RWLIST_EMPTY(&uris)) {
|
||||||
ast_cli(a->fd, "None.\n");
|
ast_cli(a->fd, "None.\n");
|
||||||
} else {
|
} else {
|
||||||
AST_RWLIST_TRAVERSE(&uris, urih, entry) {
|
AST_RWLIST_TRAVERSE(&uris, urih, entry)
|
||||||
ast_cli(a->fd, "%s/%s%s => %s\n", prefix, urih->uri, (urih->has_subtree ? "/..." : ""), urih->description);
|
ast_cli(a->fd, "%s/%s%s => %s\n", prefix, urih->uri, (urih->has_subtree ? "/..." : "" ), urih->description);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
AST_RWLIST_UNLOCK(&uris);
|
AST_RWLIST_UNLOCK(&uris);
|
||||||
|
|
||||||
ast_cli(a->fd, "\nEnabled Redirects:\n");
|
ast_cli(a->fd, "\nEnabled Redirects:\n");
|
||||||
AST_RWLIST_RDLOCK(&uri_redirects);
|
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);
|
ast_cli(a->fd, " %s => %s\n", redirect->target, redirect->dest);
|
||||||
}
|
|
||||||
if (AST_RWLIST_EMPTY(&uri_redirects)) {
|
if (AST_RWLIST_EMPTY(&uri_redirects)) {
|
||||||
ast_cli(a->fd, " None.\n");
|
ast_cli(a->fd, " None.\n");
|
||||||
}
|
}
|
||||||
|
1482
main/manager.c
1482
main/manager.c
File diff suppressed because it is too large
Load Diff
120
main/utils.c
120
main/utils.c
@@ -1839,6 +1839,126 @@ int ast_utils_init(void)
|
|||||||
return 0;
|
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];
|
||||||
|
struct ast_str *str = ast_str_create(16);
|
||||||
|
|
||||||
|
if (ast_strlen_zero(digest) || !d || !str) {
|
||||||
|
ast_free(str);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_str_set(&str, 0, "%s", digest);
|
||||||
|
|
||||||
|
c = ast_skip_blanks(ast_str_buffer(str));
|
||||||
|
|
||||||
|
if (strncasecmp(tmp, "Digest ", strlen("Digest "))) {
|
||||||
|
ast_log(LOG_WARNING, "Missing Digest.\n");
|
||||||
|
ast_free(str);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
c += strlen("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")) {
|
||||||
|
ast_string_field_set(d, username, val);
|
||||||
|
} else if (!strcasecmp(key, "realm")) {
|
||||||
|
ast_string_field_set(d, realm, val);
|
||||||
|
} else if (!strcasecmp(key, "nonce")) {
|
||||||
|
ast_string_field_set(d, nonce, val);
|
||||||
|
} else if (!strcasecmp(key, "uri")) {
|
||||||
|
ast_string_field_set(d, uri, val);
|
||||||
|
} else if (!strcasecmp(key, "domain")) {
|
||||||
|
ast_string_field_set(d, domain, val);
|
||||||
|
} else if (!strcasecmp(key, "response")) {
|
||||||
|
ast_string_field_set(d, response, val);
|
||||||
|
} 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")) {
|
||||||
|
ast_string_field_set(d, cnonce, val);
|
||||||
|
} else if (!strcasecmp(key, "opaque")) {
|
||||||
|
ast_string_field_set(d, opaque, val);
|
||||||
|
} 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;
|
||||||
|
}
|
||||||
|
ast_string_field_set(d, nc, val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ast_free(str);
|
||||||
|
|
||||||
|
/* Digest checkout */
|
||||||
|
if (ast_strlen_zero(d->realm) || ast_strlen_zero(d->nonce)) {
|
||||||
|
/* "realm" and "nonce" MUST be always exist */
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!request) {
|
||||||
|
/* Additional check for Digest response */
|
||||||
|
if (ast_strlen_zero(d->username) || ast_strlen_zero(d->uri) || ast_strlen_zero(d->response)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pedantic && d->qop && (ast_strlen_zero(d->cnonce) || ast_strlen_zero(d->nc))) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
#ifndef __AST_DEBUG_MALLOC
|
#ifndef __AST_DEBUG_MALLOC
|
||||||
int _ast_asprintf(char **ret, const char *file, int lineno, const char *func, const char *fmt, ...)
|
int _ast_asprintf(char **ret, const char *file, int lineno, const char *func, const char *fmt, ...)
|
||||||
{
|
{
|
||||||
|
@@ -156,7 +156,6 @@ static int process_message(GMimeMessage *message, const char *post_dir)
|
|||||||
return cbinfo.count;
|
return cbinfo.count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Find a sequence of bytes within a binary array. */
|
/* Find a sequence of bytes within a binary array. */
|
||||||
static int find_sequence(char * inbuf, int inlen, char * matchbuf, int matchlen)
|
static int find_sequence(char * inbuf, int inlen, char * matchbuf, int matchlen)
|
||||||
{
|
{
|
||||||
@@ -292,10 +291,9 @@ static int readmimefile(FILE * fin, FILE * fout, char * boundary, int contentlen
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int http_post_callback(struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *get_vars, struct ast_variable *headers)
|
||||||
static struct ast_str *http_post_callback(struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *vars, struct ast_variable *headers, int *status, char **title, int *contentlength)
|
|
||||||
{
|
{
|
||||||
struct ast_variable *var;
|
struct ast_variable *var, *cookies;
|
||||||
unsigned long ident = 0;
|
unsigned long ident = 0;
|
||||||
FILE *f;
|
FILE *f;
|
||||||
int content_len = 0;
|
int content_len = 0;
|
||||||
@@ -304,41 +302,45 @@ static struct ast_str *http_post_callback(struct ast_tcptls_session_instance *se
|
|||||||
int message_count = 0;
|
int message_count = 0;
|
||||||
char * boundary_marker = NULL;
|
char * boundary_marker = NULL;
|
||||||
|
|
||||||
|
if (method != AST_HTTP_POST) {
|
||||||
|
ast_http_error(ser, 501, "Not Implemented", "Attempt to use unimplemented / unsupported method");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!astman_is_authed(ast_http_manid_from_vars(headers))) {
|
||||||
|
ast_http_error(ser, 403, "Access Denied", "Sorry, I cannot let you do that, Dave.");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
if (!urih) {
|
if (!urih) {
|
||||||
return ast_http_error((*status = 400),
|
ast_http_error(ser, 400, "Missing URI handle", "There was an error parsing the request");
|
||||||
(*title = ast_strdup("Missing URI handle")),
|
return -1;
|
||||||
NULL, "There was an error parsing the request");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var = vars; var; var = var->next) {
|
cookies = ast_http_get_cookies(headers);
|
||||||
if (strcasecmp(var->name, "mansession_id")) {
|
for (var = cookies; var; var = var->next) {
|
||||||
continue;
|
if (!strcasecmp(var->name, "mansession_id")) {
|
||||||
|
sscanf(var->value, "%lx", &ident);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (sscanf(var->value, "%lx", &ident) != 1) {
|
if (cookies) {
|
||||||
return ast_http_error((*status = 400),
|
ast_variables_destroy(cookies);
|
||||||
(*title = ast_strdup("Bad Request")),
|
|
||||||
NULL, "The was an error parsing the request.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!astman_verify_session_writepermissions(ident, EVENT_FLAG_CONFIG)) {
|
|
||||||
return ast_http_error((*status = 401),
|
|
||||||
(*title = ast_strdup("Unauthorized")),
|
|
||||||
NULL, "You are not authorized to make this request.");
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!var) {
|
if (ident == 0) {
|
||||||
return ast_http_error((*status = 401),
|
ast_http_error(ser, 401, "Unauthorized", "You are not authorized to make this request.");
|
||||||
(*title = ast_strdup("Unauthorized")),
|
return -1;
|
||||||
NULL, "You are not authorized to make this request.");
|
}
|
||||||
|
if (!astman_verify_session_writepermissions(ident, EVENT_FLAG_CONFIG)) {
|
||||||
|
ast_http_error(ser, 401, "Unauthorized", "You are not authorized to make this request.");
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(f = tmpfile())) {
|
if (!(f = tmpfile())) {
|
||||||
ast_log(LOG_ERROR, "Could not create temp file.\n");
|
ast_log(LOG_ERROR, "Could not create temp file.\n");
|
||||||
return NULL;
|
ast_http_error(ser, 500, "Internal server error", "Could not create temp file.");
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var = headers; var; var = var->next) {
|
for (var = headers; var; var = var->next) {
|
||||||
@@ -348,8 +350,8 @@ static struct ast_str *http_post_callback(struct ast_tcptls_session_instance *se
|
|||||||
if ((sscanf(var->value, "%u", &content_len)) != 1) {
|
if ((sscanf(var->value, "%u", &content_len)) != 1) {
|
||||||
ast_log(LOG_ERROR, "Invalid Content-Length in POST request!\n");
|
ast_log(LOG_ERROR, "Invalid Content-Length in POST request!\n");
|
||||||
fclose(f);
|
fclose(f);
|
||||||
|
ast_http_error(ser, 500, "Internal server error", "Invalid Content-Length in POST request!");
|
||||||
return NULL;
|
return -1;
|
||||||
}
|
}
|
||||||
ast_debug(1, "Got a Content-Length of %d\n", content_len);
|
ast_debug(1, "Got a Content-Length of %d\n", content_len);
|
||||||
} else if (!strcasecmp(var->name, "Content-Type")) {
|
} else if (!strcasecmp(var->name, "Content-Type")) {
|
||||||
@@ -368,14 +370,14 @@ static struct ast_str *http_post_callback(struct ast_tcptls_session_instance *se
|
|||||||
}
|
}
|
||||||
fclose(f);
|
fclose(f);
|
||||||
|
|
||||||
return NULL;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fseek(f, SEEK_SET, 0)) {
|
if (fseek(f, SEEK_SET, 0)) {
|
||||||
ast_log(LOG_ERROR, "Failed to seek temp file back to beginning.\n");
|
ast_log(LOG_ERROR, "Failed to seek temp file back to beginning.\n");
|
||||||
fclose(f);
|
fclose(f);
|
||||||
|
ast_http_error(ser, 500, "Internal server error", "Failed to seek temp file back to beginning.");
|
||||||
return NULL;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
post_dir = urih->data;
|
post_dir = urih->data;
|
||||||
@@ -385,24 +387,20 @@ static struct ast_str *http_post_callback(struct ast_tcptls_session_instance *se
|
|||||||
if (!message) {
|
if (!message) {
|
||||||
ast_log(LOG_ERROR, "Error parsing MIME data\n");
|
ast_log(LOG_ERROR, "Error parsing MIME data\n");
|
||||||
|
|
||||||
return ast_http_error((*status = 400),
|
ast_http_error(ser, 400, "Bad Request", "The was an error parsing the request.");
|
||||||
(*title = ast_strdup("Bad Request")),
|
return -1;
|
||||||
NULL, "The was an error parsing the request.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(message_count = process_message(message, ast_str_buffer(post_dir)))) {
|
if (!(message_count = process_message(message, ast_str_buffer(post_dir)))) {
|
||||||
ast_log(LOG_ERROR, "Invalid MIME data, found no parts!\n");
|
ast_log(LOG_ERROR, "Invalid MIME data, found no parts!\n");
|
||||||
g_object_unref(message);
|
g_object_unref(message);
|
||||||
return ast_http_error((*status = 400),
|
ast_http_error(ser, 400, "Bad Request", "The was an error parsing the request.");
|
||||||
(*title = ast_strdup("Bad Request")),
|
return -1;
|
||||||
NULL, "The was an error parsing the request.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
g_object_unref(message);
|
g_object_unref(message);
|
||||||
|
|
||||||
return ast_http_error((*status = 200),
|
ast_http_error(ser, 200, "OK", "File successfully uploaded.");
|
||||||
(*title = ast_strdup("OK")),
|
return 0;
|
||||||
NULL, "File successfully uploaded.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __ast_http_post_load(int reload)
|
static int __ast_http_post_load(int reload)
|
||||||
@@ -450,8 +448,6 @@ static int __ast_http_post_load(int reload)
|
|||||||
ast_str_set(&ds, 0, "%s/%s", prefix, v->value);
|
ast_str_set(&ds, 0, "%s/%s", prefix, v->value);
|
||||||
urih->data = ds;
|
urih->data = ds;
|
||||||
urih->has_subtree = 0;
|
urih->has_subtree = 0;
|
||||||
urih->supports_get = 0;
|
|
||||||
urih->supports_post = 1;
|
|
||||||
urih->callback = http_post_callback;
|
urih->callback = http_post_callback;
|
||||||
urih->key = __FILE__;
|
urih->key = __FILE__;
|
||||||
urih->mallocd = urih->dmallocd = 1;
|
urih->mallocd = urih->dmallocd = 1;
|
||||||
|
@@ -155,19 +155,6 @@ static struct ao2_container *profiles;
|
|||||||
static struct ao2_container *http_routes;
|
static struct ao2_container *http_routes;
|
||||||
static struct ao2_container *users;
|
static struct ao2_container *users;
|
||||||
|
|
||||||
/*! \brief Extensions whose mime types we think we know */
|
|
||||||
static struct {
|
|
||||||
char *ext;
|
|
||||||
char *mtype;
|
|
||||||
} mimetypes[] = {
|
|
||||||
{ "png", "image/png" },
|
|
||||||
{ "xml", "text/xml" },
|
|
||||||
{ "jpg", "image/jpeg" },
|
|
||||||
{ "js", "application/x-javascript" },
|
|
||||||
{ "wav", "audio/x-wav" },
|
|
||||||
{ "mp3", "audio/mpeg" },
|
|
||||||
};
|
|
||||||
|
|
||||||
static char global_server[80] = ""; /*!< Server to substitute into templates */
|
static char global_server[80] = ""; /*!< Server to substitute into templates */
|
||||||
static char global_serverport[6] = ""; /*!< Server port to substitute into templates */
|
static char global_serverport[6] = ""; /*!< Server port to substitute into templates */
|
||||||
static char global_default_profile[80] = ""; /*!< Default profile to use if one isn't specified */
|
static char global_default_profile[80] = ""; /*!< Default profile to use if one isn't specified */
|
||||||
@@ -176,22 +163,6 @@ static char global_default_profile[80] = ""; /*!< Default profile to use if one
|
|||||||
static struct varshead global_variables;
|
static struct varshead global_variables;
|
||||||
static ast_mutex_t globals_lock;
|
static ast_mutex_t globals_lock;
|
||||||
|
|
||||||
/*! \brief Return mime type based on extension */
|
|
||||||
static char *ftype2mtype(const char *ftype)
|
|
||||||
{
|
|
||||||
int x;
|
|
||||||
|
|
||||||
if (ast_strlen_zero(ftype))
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
for (x = 0;x < ARRAY_LEN(mimetypes);x++) {
|
|
||||||
if (!strcasecmp(ftype, mimetypes[x].ext))
|
|
||||||
return mimetypes[x].mtype;
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* iface is the interface (e.g. eth0); address is the return value */
|
/* iface is the interface (e.g. eth0); address is the return value */
|
||||||
static int lookup_iface(const char *iface, struct in_addr *address)
|
static int lookup_iface(const char *iface, struct in_addr *address)
|
||||||
{
|
{
|
||||||
@@ -398,20 +369,24 @@ static void set_timezone_variables(struct varshead *headp, const char *zone)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*! \brief Callback that is executed everytime an http request is received by this module */
|
/*! \brief Callback that is executed everytime an http request is received by this module */
|
||||||
static struct ast_str *phoneprov_callback(struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *vars, struct ast_variable *headers, int *status, char **title, int *contentlength)
|
static int phoneprov_callback(struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *get_vars, struct ast_variable *headers)
|
||||||
{
|
{
|
||||||
struct http_route *route;
|
struct http_route *route;
|
||||||
struct http_route search_route = {
|
struct http_route search_route = {
|
||||||
.uri = uri,
|
.uri = uri,
|
||||||
};
|
};
|
||||||
struct ast_str *result = ast_str_create(512);
|
struct ast_str *result;
|
||||||
char path[PATH_MAX];
|
char path[PATH_MAX];
|
||||||
char *file = NULL;
|
char *file = NULL;
|
||||||
int len;
|
int len;
|
||||||
int fd;
|
int fd;
|
||||||
char buf[256];
|
char buf[256];
|
||||||
struct timeval now = ast_tvnow();
|
struct ast_str *http_header;
|
||||||
struct ast_tm tm;
|
|
||||||
|
if (method != AST_HTTP_GET && method != AST_HTTP_HEAD) {
|
||||||
|
ast_http_error(ser, 501, "Not Implemented", "Attempt to use unimplemented / unsupported method");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
if (!(route = ao2_find(http_routes, &search_route, OBJ_POINTER))) {
|
if (!(route = ao2_find(http_routes, &search_route, OBJ_POINTER))) {
|
||||||
goto out404;
|
goto out404;
|
||||||
@@ -434,15 +409,9 @@ static struct ast_str *phoneprov_callback(struct ast_tcptls_session_instance *se
|
|||||||
goto out500;
|
goto out500;
|
||||||
}
|
}
|
||||||
|
|
||||||
ast_strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S %Z", ast_localtime(&now, &tm, "GMT"));
|
http_header = ast_str_create(80);
|
||||||
fprintf(ser->f, "HTTP/1.1 200 OK\r\n"
|
ast_str_set(&http_header, 0, "Content-type: %s\r\n",
|
||||||
"Server: Asterisk/%s\r\n"
|
route->file->mime_type);
|
||||||
"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);
|
|
||||||
|
|
||||||
while ((len = read(fd, buf, sizeof(buf))) > 0) {
|
while ((len = read(fd, buf, sizeof(buf))) > 0) {
|
||||||
if (fwrite(buf, 1, len, ser->f) != len) {
|
if (fwrite(buf, 1, len, ser->f) != len) {
|
||||||
@@ -455,9 +424,10 @@ static struct ast_str *phoneprov_callback(struct ast_tcptls_session_instance *se
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ast_http_send(ser, method, 200, NULL, http_header, NULL, fd, 0);
|
||||||
close(fd);
|
close(fd);
|
||||||
route = unref_route(route);
|
route = unref_route(route);
|
||||||
return NULL;
|
return 0;
|
||||||
} else { /* Dynamic file */
|
} else { /* Dynamic file */
|
||||||
int bufsize;
|
int bufsize;
|
||||||
char *tmp;
|
char *tmp;
|
||||||
@@ -516,33 +486,36 @@ static struct ast_str *phoneprov_callback(struct ast_tcptls_session_instance *se
|
|||||||
ast_free(file);
|
ast_free(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
ast_str_append(&result, 0,
|
ast_str_set(&http_header, 0, "Content-type: %s\r\n",
|
||||||
"Content-Type: %s\r\n"
|
route->file->mime_type);
|
||||||
"Content-length: %d\r\n"
|
|
||||||
"\r\n"
|
|
||||||
"%s", route->file->mime_type, (int) strlen(tmp), tmp);
|
|
||||||
|
|
||||||
|
if (!(result = ast_str_create(512))) {
|
||||||
|
ast_log(LOG_ERROR, "Could not create result string!\n");
|
||||||
|
if (tmp) {
|
||||||
|
ast_free(tmp);
|
||||||
|
}
|
||||||
|
goto out500;
|
||||||
|
}
|
||||||
|
ast_str_append(&result, 0, "%s", tmp);
|
||||||
|
|
||||||
|
ast_http_send(ser, method, 200, NULL, http_header, result, 0, 0);
|
||||||
if (tmp) {
|
if (tmp) {
|
||||||
ast_free(tmp);
|
ast_free(tmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
route = unref_route(route);
|
route = unref_route(route);
|
||||||
|
|
||||||
return result;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
out404:
|
out404:
|
||||||
*status = 404;
|
ast_http_error(ser, 404, "Not Found", "Nothing to see here. Move along.");
|
||||||
*title = strdup("Not Found");
|
return -1;
|
||||||
*contentlength = 0;
|
|
||||||
return ast_http_error(404, "Not Found", NULL, "The requested URL was not found on this server.");
|
|
||||||
|
|
||||||
out500:
|
out500:
|
||||||
route = unref_route(route);
|
route = unref_route(route);
|
||||||
*status = 500;
|
ast_http_error(ser, 500, "Internal Error", "An internal error has occured.");
|
||||||
*title = strdup("Internal Server Error");
|
return -1;
|
||||||
*contentlength = 0;
|
|
||||||
return ast_http_error(500, "Internal Error", NULL, "An internal error has occured.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! \brief Build a route structure and add it to the list of available http routes
|
/*! \brief Build a route structure and add it to the list of available http routes
|
||||||
@@ -656,7 +629,8 @@ static void build_profile(const char *name, struct ast_variable *v)
|
|||||||
* 3) Default mime type specified in profile
|
* 3) Default mime type specified in profile
|
||||||
* 4) text/plain
|
* 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_http_ftype2mtype(file_extension), profile->default_mime_type), "text/plain"))));
|
||||||
|
|
||||||
if (!strcasecmp(v->name, "static_file")) {
|
if (!strcasecmp(v->name, "static_file")) {
|
||||||
ast_string_field_set(pp_file, format, args.filename);
|
ast_string_field_set(pp_file, format, args.filename);
|
||||||
@@ -1233,7 +1207,6 @@ static struct ast_http_uri phoneprovuri = {
|
|||||||
.description = "Asterisk HTTP Phone Provisioning Tool",
|
.description = "Asterisk HTTP Phone Provisioning Tool",
|
||||||
.uri = "phoneprov",
|
.uri = "phoneprov",
|
||||||
.has_subtree = 1,
|
.has_subtree = 1,
|
||||||
.supports_get = 1,
|
|
||||||
.data = NULL,
|
.data = NULL,
|
||||||
.key = __FILE__,
|
.key = __FILE__,
|
||||||
};
|
};
|
||||||
|
Reference in New Issue
Block a user