mirror of
https://github.com/asterisk/asterisk.git
synced 2025-09-04 20:04:50 +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.
|
||||
*/
|
||||
|
||||
|
||||
/*! \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 @@ struct ast_http_uri {
|
||||
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 @@ struct ast_http_uri {
|
||||
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 @@ void ast_http_uri_unlink(struct ast_http_uri *urihandler);
|
||||
/*! \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 @@ struct ast_str *ast_http_error(int status, const char *title, const char *extra_
|
||||
*/
|
||||
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 */
|
||||
|
@@ -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
|
||||
@@ -646,6 +647,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__)
|
||||
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) {
|
||||
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 @@ static struct ao2_container *internal_ao2_container_alloc(struct ao2_container *
|
||||
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 @@ struct ao2_container *__ao2_container_alloc_debug(const unsigned int n_buckets,
|
||||
{
|
||||
/* 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 @@ struct ao2_container *__ao2_container_alloc(const unsigned int n_buckets, ao2_ha
|
||||
/* 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);
|
||||
}
|
||||
|
||||
/*!
|
||||
|
773
main/http.c
773
main/http.c
File diff suppressed because it is too large
Load Diff
1572
main/manager.c
1572
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;
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
*\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
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
/* Find a sequence of bytes within a binary array. */
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
static struct ast_str *http_post_callback(struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *vars, struct ast_variable *headers, int *status, char **title, int *contentlength)
|
||||
static int http_post_callback(struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *get_vars, struct ast_variable *headers)
|
||||
{
|
||||
struct ast_variable *var;
|
||||
struct ast_variable *var, *cookies;
|
||||
unsigned long ident = 0;
|
||||
FILE *f;
|
||||
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;
|
||||
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) {
|
||||
return ast_http_error((*status = 400),
|
||||
(*title = ast_strdup("Missing URI handle")),
|
||||
NULL, "There was an error parsing the request");
|
||||
ast_http_error(ser, 400, "Missing URI handle", "There was an error parsing the request");
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (var = vars; var; var = var->next) {
|
||||
if (strcasecmp(var->name, "mansession_id")) {
|
||||
continue;
|
||||
cookies = ast_http_get_cookies(headers);
|
||||
for (var = cookies; var; var = var->next) {
|
||||
if (!strcasecmp(var->name, "mansession_id")) {
|
||||
sscanf(var->value, "%lx", &ident);
|
||||
break;
|
||||
}
|
||||
|
||||
if (sscanf(var->value, "%lx", &ident) != 1) {
|
||||
return ast_http_error((*status = 400),
|
||||
(*title = ast_strdup("Bad Request")),
|
||||
NULL, "The was an error parsing the request.");
|
||||
}
|
||||
|
||||
if (!astman_verify_session_writepermissions(ident, EVENT_FLAG_CONFIG)) {
|
||||
return ast_http_error((*status = 401),
|
||||
(*title = ast_strdup("Unauthorized")),
|
||||
NULL, "You are not authorized to make this request.");
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
if (cookies) {
|
||||
ast_variables_destroy(cookies);
|
||||
}
|
||||
|
||||
if (!var) {
|
||||
return ast_http_error((*status = 401),
|
||||
(*title = ast_strdup("Unauthorized")),
|
||||
NULL, "You are not authorized to make this request.");
|
||||
if (ident == 0) {
|
||||
ast_http_error(ser, 401, "Unauthorized", "You are not authorized to make this request.");
|
||||
return -1;
|
||||
}
|
||||
if (!astman_verify_session_writepermissions(ident, EVENT_FLAG_CONFIG)) {
|
||||
ast_http_error(ser, 401, "Unauthorized", "You are not authorized to make this request.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!(f = tmpfile())) {
|
||||
ast_log(LOG_ERROR, "Could not create temp file.\n");
|
||||
return NULL;
|
||||
ast_http_error(ser, 500, "Internal server error", "Could not create temp file.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (var = headers; var; var = var->next) {
|
||||
@@ -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) {
|
||||
ast_log(LOG_ERROR, "Invalid Content-Length in POST request!\n");
|
||||
fclose(f);
|
||||
|
||||
return NULL;
|
||||
ast_http_error(ser, 500, "Internal server error", "Invalid Content-Length in POST request!");
|
||||
return -1;
|
||||
}
|
||||
ast_debug(1, "Got a Content-Length of %d\n", content_len);
|
||||
} else if (!strcasecmp(var->name, "Content-Type")) {
|
||||
@@ -367,15 +369,15 @@ static struct ast_str *http_post_callback(struct ast_tcptls_session_instance *se
|
||||
ast_log(LOG_DEBUG, "Cannot find boundary marker in POST request.\n");
|
||||
}
|
||||
fclose(f);
|
||||
|
||||
return NULL;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (fseek(f, SEEK_SET, 0)) {
|
||||
ast_log(LOG_ERROR, "Failed to seek temp file back to beginning.\n");
|
||||
fclose(f);
|
||||
|
||||
return NULL;
|
||||
ast_http_error(ser, 500, "Internal server error", "Failed to seek temp file back to beginning.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
post_dir = urih->data;
|
||||
@@ -385,24 +387,20 @@ static struct ast_str *http_post_callback(struct ast_tcptls_session_instance *se
|
||||
if (!message) {
|
||||
ast_log(LOG_ERROR, "Error parsing MIME data\n");
|
||||
|
||||
return ast_http_error((*status = 400),
|
||||
(*title = ast_strdup("Bad Request")),
|
||||
NULL, "The was an error parsing the request.");
|
||||
ast_http_error(ser, 400, "Bad Request", "The was an error parsing the request.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!(message_count = process_message(message, ast_str_buffer(post_dir)))) {
|
||||
ast_log(LOG_ERROR, "Invalid MIME data, found no parts!\n");
|
||||
g_object_unref(message);
|
||||
return ast_http_error((*status = 400),
|
||||
(*title = ast_strdup("Bad Request")),
|
||||
NULL, "The was an error parsing the request.");
|
||||
ast_http_error(ser, 400, "Bad Request", "The was an error parsing the request.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
g_object_unref(message);
|
||||
|
||||
return ast_http_error((*status = 200),
|
||||
(*title = ast_strdup("OK")),
|
||||
NULL, "File successfully uploaded.");
|
||||
ast_http_error(ser, 200, "OK", "File successfully uploaded.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __ast_http_post_load(int reload)
|
||||
@@ -450,8 +448,6 @@ static int __ast_http_post_load(int reload)
|
||||
ast_str_set(&ds, 0, "%s/%s", prefix, v->value);
|
||||
urih->data = ds;
|
||||
urih->has_subtree = 0;
|
||||
urih->supports_get = 0;
|
||||
urih->supports_post = 1;
|
||||
urih->callback = http_post_callback;
|
||||
urih->key = __FILE__;
|
||||
urih->mallocd = urih->dmallocd = 1;
|
||||
|
@@ -155,19 +155,6 @@ static struct ao2_container *profiles;
|
||||
static struct ao2_container *http_routes;
|
||||
static struct ao2_container *users;
|
||||
|
||||
/*! \brief Extensions whose mime types we think we know */
|
||||
static struct {
|
||||
char *ext;
|
||||
char *mtype;
|
||||
} mimetypes[] = {
|
||||
{ "png", "image/png" },
|
||||
{ "xml", "text/xml" },
|
||||
{ "jpg", "image/jpeg" },
|
||||
{ "js", "application/x-javascript" },
|
||||
{ "wav", "audio/x-wav" },
|
||||
{ "mp3", "audio/mpeg" },
|
||||
};
|
||||
|
||||
static char global_server[80] = ""; /*!< Server to substitute into templates */
|
||||
static char global_serverport[6] = ""; /*!< Server port to substitute into templates */
|
||||
static char global_default_profile[80] = ""; /*!< Default profile to use if one isn't specified */
|
||||
@@ -176,22 +163,6 @@ static char global_default_profile[80] = ""; /*!< Default profile to use if one
|
||||
static struct varshead global_variables;
|
||||
static ast_mutex_t globals_lock;
|
||||
|
||||
/*! \brief Return mime type based on extension */
|
||||
static char *ftype2mtype(const char *ftype)
|
||||
{
|
||||
int x;
|
||||
|
||||
if (ast_strlen_zero(ftype))
|
||||
return NULL;
|
||||
|
||||
for (x = 0;x < ARRAY_LEN(mimetypes);x++) {
|
||||
if (!strcasecmp(ftype, mimetypes[x].ext))
|
||||
return mimetypes[x].mtype;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* iface is the interface (e.g. eth0); address is the return value */
|
||||
static int lookup_iface(const char *iface, struct in_addr *address)
|
||||
{
|
||||
@@ -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 */
|
||||
static struct ast_str *phoneprov_callback(struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *vars, struct ast_variable *headers, int *status, char **title, int *contentlength)
|
||||
static int phoneprov_callback(struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *get_vars, struct ast_variable *headers)
|
||||
{
|
||||
struct http_route *route;
|
||||
struct http_route search_route = {
|
||||
.uri = uri,
|
||||
};
|
||||
struct ast_str *result = ast_str_create(512);
|
||||
struct ast_str *result;
|
||||
char path[PATH_MAX];
|
||||
char *file = NULL;
|
||||
int len;
|
||||
int fd;
|
||||
char buf[256];
|
||||
struct timeval now = ast_tvnow();
|
||||
struct ast_tm tm;
|
||||
struct ast_str *http_header;
|
||||
|
||||
if (method != AST_HTTP_GET && method != AST_HTTP_HEAD) {
|
||||
ast_http_error(ser, 501, "Not Implemented", "Attempt to use unimplemented / unsupported method");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!(route = ao2_find(http_routes, &search_route, OBJ_POINTER))) {
|
||||
goto out404;
|
||||
@@ -434,15 +409,9 @@ static struct ast_str *phoneprov_callback(struct ast_tcptls_session_instance *se
|
||||
goto out500;
|
||||
}
|
||||
|
||||
ast_strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S %Z", ast_localtime(&now, &tm, "GMT"));
|
||||
fprintf(ser->f, "HTTP/1.1 200 OK\r\n"
|
||||
"Server: Asterisk/%s\r\n"
|
||||
"Date: %s\r\n"
|
||||
"Connection: close\r\n"
|
||||
"Cache-Control: no-cache, no-store\r\n"
|
||||
"Content-Length: %d\r\n"
|
||||
"Content-Type: %s\r\n\r\n",
|
||||
ast_get_version(), buf, len, route->file->mime_type);
|
||||
http_header = ast_str_create(80);
|
||||
ast_str_set(&http_header, 0, "Content-type: %s\r\n",
|
||||
route->file->mime_type);
|
||||
|
||||
while ((len = read(fd, buf, sizeof(buf))) > 0) {
|
||||
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);
|
||||
route = unref_route(route);
|
||||
return NULL;
|
||||
return 0;
|
||||
} else { /* Dynamic file */
|
||||
int bufsize;
|
||||
char *tmp;
|
||||
@@ -516,33 +486,36 @@ static struct ast_str *phoneprov_callback(struct ast_tcptls_session_instance *se
|
||||
ast_free(file);
|
||||
}
|
||||
|
||||
ast_str_append(&result, 0,
|
||||
"Content-Type: %s\r\n"
|
||||
"Content-length: %d\r\n"
|
||||
"\r\n"
|
||||
"%s", route->file->mime_type, (int) strlen(tmp), tmp);
|
||||
ast_str_set(&http_header, 0, "Content-type: %s\r\n",
|
||||
route->file->mime_type);
|
||||
|
||||
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) {
|
||||
ast_free(tmp);
|
||||
}
|
||||
|
||||
route = unref_route(route);
|
||||
|
||||
return result;
|
||||
return 0;
|
||||
}
|
||||
|
||||
out404:
|
||||
*status = 404;
|
||||
*title = strdup("Not Found");
|
||||
*contentlength = 0;
|
||||
return ast_http_error(404, "Not Found", NULL, "The requested URL was not found on this server.");
|
||||
ast_http_error(ser, 404, "Not Found", "Nothing to see here. Move along.");
|
||||
return -1;
|
||||
|
||||
out500:
|
||||
route = unref_route(route);
|
||||
*status = 500;
|
||||
*title = strdup("Internal Server Error");
|
||||
*contentlength = 0;
|
||||
return ast_http_error(500, "Internal Error", NULL, "An internal error has occured.");
|
||||
ast_http_error(ser, 500, "Internal Error", "An internal error has occured.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*! \brief Build a route structure and add it to the list of available http routes
|
||||
@@ -656,7 +629,8 @@ static void build_profile(const char *name, struct ast_variable *v)
|
||||
* 3) Default mime type specified in profile
|
||||
* 4) text/plain
|
||||
*/
|
||||
ast_string_field_set(pp_file, mime_type, S_OR(args.mimetype, (S_OR(S_OR(ftype2mtype(file_extension), profile->default_mime_type), "text/plain"))));
|
||||
ast_string_field_set(pp_file, mime_type, S_OR(args.mimetype,
|
||||
(S_OR(S_OR(ast_http_ftype2mtype(file_extension), profile->default_mime_type), "text/plain"))));
|
||||
|
||||
if (!strcasecmp(v->name, "static_file")) {
|
||||
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",
|
||||
.uri = "phoneprov",
|
||||
.has_subtree = 1,
|
||||
.supports_get = 1,
|
||||
.data = NULL,
|
||||
.key = __FILE__,
|
||||
};
|
||||
|
Reference in New Issue
Block a user