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
certified/1.8.6
Tilghman Lesher 16 years ago
parent 80b8d34377
commit ce6ebaef97

@ -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);
}
/*!

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -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 (!urih) {
return ast_http_error((*status = 400),
(*title = ast_strdup("Missing URI handle")),
NULL, "There was an error parsing the request");
if (method != AST_HTTP_POST) {
ast_http_error(ser, 501, "Not Implemented", "Attempt to use unimplemented / unsupported method");
return -1;
}
for (var = vars; var; var = var->next) {
if (strcasecmp(var->name, "mansession_id")) {
continue;
}
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 (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 (!urih) {
ast_http_error(ser, 400, "Missing URI handle", "There was an error parsing the request");
return -1;
}
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.");
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;
}
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__,
};

Loading…
Cancel
Save