2006-09-19 19:58:09 +00:00
/*
2006-03-25 23:50:09 +00:00
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 1999 - 2006, Digium, Inc.
*
* Mark Spencer <markster@digium.com>
*
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
2006-03-26 16:48:47 +00:00
/*!
2006-09-19 19:58:09 +00:00
* \file
2006-04-03 18:38:28 +00:00
* \brief http server for AMI access
2006-03-26 16:48:47 +00:00
*
2006-04-03 18:38:28 +00:00
* \author Mark Spencer <markster@digium.com>
2007-04-06 20:58:43 +00:00
*
* This program implements a tiny http server
* and was inspired by micro-httpd by Jef Poskanzer
2006-09-19 19:58:09 +00:00
*
2006-04-03 18:38:28 +00:00
* \ref AstHTTP - AMI over the http protocol
2006-03-26 16:48:47 +00:00
*/
2006-06-07 18:54:56 +00:00
#include "asterisk.h"
ASTERISK_FILE_VERSION ( __FILE__ , "$Revision$" )
2006-03-25 23:50:09 +00:00
#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <netinet/in.h>
#include <sys/time.h>
#include <sys/socket.h>
2006-04-01 08:49:54 +00:00
#include <sys/stat.h>
2006-03-25 23:50:09 +00:00
#include <sys/signal.h>
#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
2006-03-26 16:48:47 +00:00
#include "asterisk/cli.h"
#include "asterisk/http.h"
#include "asterisk/utils.h"
#include "asterisk/strings.h"
2006-04-01 08:49:54 +00:00
#include "asterisk/options.h"
#include "asterisk/config.h"
2007-02-20 20:26:06 +00:00
#include "asterisk/version.h"
2007-04-06 20:58:43 +00:00
#include "asterisk/manager.h"
2006-03-25 23:50:09 +00:00
#define MAX_PREFIX 80
2006-04-02 23:55:15 +00:00
#define DEFAULT_PREFIX "/asterisk"
2006-03-25 23:50:09 +00:00
struct ast_http_server_instance {
FILE * f ;
int fd ;
struct sockaddr_in requestor ;
ast_http_callback callback ;
};
2006-12-28 19:52:46 +00:00
AST_RWLOCK_DEFINE_STATIC ( uris_lock );
2006-12-27 22:06:56 +00:00
static struct ast_http_uri * uris ;
2006-03-25 23:50:09 +00:00
static int httpfd = - 1 ;
static pthread_t master = AST_PTHREADT_NULL ;
static char prefix [ MAX_PREFIX ];
2006-12-27 22:06:56 +00:00
static int prefix_len ;
2006-03-25 23:50:09 +00:00
static struct sockaddr_in oldsin ;
2006-12-27 22:06:56 +00:00
static int enablestatic ;
2006-04-01 08:49:54 +00:00
2006-04-03 18:38:28 +00:00
/*! \brief Limit the kinds of files we're willing to serve up */
2006-04-01 08:49:54 +00:00
static struct {
2007-04-11 14:48:01 +00:00
const char * ext ;
const char * mtype ;
2006-04-01 08:49:54 +00:00
} mimetypes [] = {
{ "png" , "image/png" },
{ "jpg" , "image/jpeg" },
{ "js" , "application/x-javascript" },
{ "wav" , "audio/x-wav" },
{ "mp3" , "audio/mpeg" },
2007-03-21 18:08:57 +00:00
{ "svg" , "image/svg+xml" },
2007-04-11 14:48:01 +00:00
{ "svgz" , "image/svg+xml" },
2007-04-05 15:47:17 +00:00
{ "gif" , "image/gif" },
2006-04-01 08:49:54 +00:00
};
2007-04-11 14:48:01 +00:00
static const char * ftype2mtype ( const char * ftype , char * wkspace , int wkspacelen )
2006-04-01 08:49:54 +00:00
{
int x ;
if ( ftype ) {
for ( x = 0 ; x < sizeof ( mimetypes ) / sizeof ( mimetypes [ 0 ]); x ++ ) {
if ( ! strcasecmp ( ftype , mimetypes [ x ]. ext ))
return mimetypes [ x ]. mtype ;
}
}
snprintf ( wkspace , wkspacelen , "text/%s" , ftype ? ftype : "plain" );
return wkspace ;
}
static char * static_callback ( struct sockaddr_in * req , const char * uri , struct ast_variable * vars , int * status , char ** title , int * contentlength )
{
char result [ 4096 ];
char * c = result ;
char * path ;
2007-04-11 14:48:01 +00:00
char * ftype ;
const char * mtype ;
2006-04-01 08:49:54 +00:00
char wkspace [ 80 ];
struct stat st ;
int len ;
int fd ;
void * blob ;
2006-09-19 19:58:09 +00:00
/* Yuck. I'm not really sold on this, but if you don't deliver static content it makes your configuration
2006-04-01 08:49:54 +00:00
substantially more challenging, but this seems like a rather irritating feature creep on Asterisk. */
if ( ! enablestatic || ast_strlen_zero ( uri ))
goto out403 ;
/* Disallow any funny filenames at all */
if (( uri [ 0 ] < 33 ) || strchr ( "./|~@#$%^&*() \t " , uri [ 0 ]))
goto out403 ;
if ( strstr ( uri , "/.." ))
goto out403 ;
2006-09-19 19:58:09 +00:00
2006-04-01 08:49:54 +00:00
if (( ftype = strrchr ( uri , '.' )))
ftype ++ ;
2007-04-11 14:48:01 +00:00
mtype = ftype2mtype ( ftype , wkspace , sizeof ( wkspace ));
2006-09-19 19:58:09 +00:00
2006-04-01 08:49:54 +00:00
/* Cap maximum length */
2006-04-15 22:53:53 +00:00
len = strlen ( uri ) + strlen ( ast_config_AST_DATA_DIR ) + strlen ( "/static-http/" ) + 5 ;
2006-04-01 08:49:54 +00:00
if ( len > 1024 )
goto out403 ;
2006-09-19 19:58:09 +00:00
2006-04-01 08:49:54 +00:00
path = alloca ( len );
2006-04-15 22:53:53 +00:00
sprintf ( path , "%s/static-http/%s" , ast_config_AST_DATA_DIR , uri );
2006-04-01 08:49:54 +00:00
if ( stat ( path , & st ))
goto out404 ;
if ( S_ISDIR ( st . st_mode ))
goto out404 ;
fd = open ( path , O_RDONLY );
if ( fd < 0 )
goto out403 ;
2006-09-19 19:58:09 +00:00
2006-04-01 08:49:54 +00:00
len = st . st_size + strlen ( mtype ) + 40 ;
2006-09-19 19:58:09 +00:00
2006-04-01 08:49:54 +00:00
blob = malloc ( len );
if ( blob ) {
c = blob ;
sprintf ( c , "Content-type: %s \r\n\r\n " , mtype );
c += strlen ( c );
* contentlength = read ( fd , c , st . st_size );
if ( * contentlength < 0 ) {
close ( fd );
free ( blob );
goto out403 ;
}
}
2006-07-18 20:26:38 +00:00
close ( fd );
2006-04-01 08:49:54 +00:00
return blob ;
out404 :
* status = 404 ;
* title = strdup ( "Not Found" );
return ast_http_error ( 404 , "Not Found" , NULL , "Nothing to see here. Move along." );
out403 :
* status = 403 ;
* title = strdup ( "Access Denied" );
return ast_http_error ( 403 , "Access Denied" , NULL , "Sorry, I cannot let you do that, Dave." );
}
2006-03-25 23:50:09 +00:00
static char * httpstatus_callback ( struct sockaddr_in * req , const char * uri , struct ast_variable * vars , int * status , char ** title , int * contentlength )
{
char result [ 4096 ];
2006-03-26 06:59:10 +00:00
size_t reslen = sizeof ( result );
2006-03-25 23:50:09 +00:00
char * c = result ;
struct ast_variable * v ;
ast_build_string ( & c , & reslen ,
" \r\n "
"<title>Asterisk HTTP Status</title> \r\n "
"<body bgcolor= \" #ffffff \" > \r\n "
"<table bgcolor= \" #f1f1f1 \" align= \" center \" ><tr><td bgcolor= \" #e0e0ff \" colspan= \" 2 \" width= \" 500 \" > \r\n "
"<h2> Asterisk™ HTTP Status</h2></td></tr> \r\n " );
ast_build_string ( & c , & reslen , "<tr><td><i>Prefix</i></td><td><b>%s</b></td></tr> \r\n " , prefix );
ast_build_string ( & c , & reslen , "<tr><td><i>Bind Address</i></td><td><b>%s</b></td></tr> \r\n " ,
2006-07-21 17:31:28 +00:00
ast_inet_ntoa ( oldsin . sin_addr ));
2006-03-25 23:50:09 +00:00
ast_build_string ( & c , & reslen , "<tr><td><i>Bind Port</i></td><td><b>%d</b></td></tr> \r\n " ,
ntohs ( oldsin . sin_port ));
ast_build_string ( & c , & reslen , "<tr><td colspan= \" 2 \" ><hr></td></tr> \r\n " );
v = vars ;
while ( v ) {
2006-04-01 08:49:54 +00:00
if ( strncasecmp ( v -> name , "cookie_" , 7 ))
ast_build_string ( & c , & reslen , "<tr><td><i>Submitted Variable '%s'</i></td><td>%s</td></tr> \r\n " , v -> name , v -> value );
v = v -> next ;
}
ast_build_string ( & c , & reslen , "<tr><td colspan= \" 2 \" ><hr></td></tr> \r\n " );
v = vars ;
while ( v ) {
if ( ! strncasecmp ( v -> name , "cookie_" , 7 ))
ast_build_string ( & c , & reslen , "<tr><td><i>Cookie '%s'</i></td><td>%s</td></tr> \r\n " , v -> name , v -> value );
2006-03-25 23:50:09 +00:00
v = v -> next ;
}
ast_build_string ( & c , & reslen , "</table><center><font size= \" -1 \" ><i>Asterisk and Digium are registered trademarks of Digium, Inc.</i></font></center></body> \r\n " );
return strdup ( result );
}
static struct ast_http_uri statusuri = {
. callback = httpstatus_callback ,
. description = "Asterisk HTTP General Status" ,
. uri = "httpstatus" ,
. has_subtree = 0 ,
};
2006-09-19 19:58:09 +00:00
2006-04-01 08:49:54 +00:00
static struct ast_http_uri staticuri = {
. callback = static_callback ,
. description = "Asterisk HTTP Static Delivery" ,
. uri = "static" ,
. has_subtree = 1 ,
};
2006-09-19 19:58:09 +00:00
2006-03-25 23:50:09 +00:00
char * ast_http_error ( int status , const char * title , const char * extra_header , const char * text )
{
char * c = NULL ;
asprintf ( & c ,
"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 c ;
}
int ast_http_uri_link ( struct ast_http_uri * urih )
{
2006-12-23 20:22:52 +00:00
struct ast_http_uri * prev ;
2006-12-28 19:52:46 +00:00
ast_rwlock_wrlock ( & uris_lock );
2006-12-23 20:22:52 +00:00
prev = uris ;
2006-03-25 23:50:09 +00:00
if ( ! uris || strlen ( uris -> uri ) <= strlen ( urih -> uri )) {
urih -> next = uris ;
uris = urih ;
} else {
while ( prev -> next && ( strlen ( prev -> next -> uri ) > strlen ( urih -> uri )))
prev = prev -> next ;
/* Insert it here */
urih -> next = prev -> next ;
prev -> next = urih ;
}
2006-12-28 19:52:46 +00:00
ast_rwlock_unlock ( & uris_lock );
2006-12-23 20:22:52 +00:00
2006-03-25 23:50:09 +00:00
return 0 ;
2006-09-19 19:58:09 +00:00
}
2006-03-25 23:50:09 +00:00
void ast_http_uri_unlink ( struct ast_http_uri * urih )
{
2006-12-23 20:22:52 +00:00
struct ast_http_uri * prev ;
2006-12-28 19:52:46 +00:00
ast_rwlock_wrlock ( & uris_lock );
2006-12-23 20:22:52 +00:00
if ( ! uris ) {
2006-12-28 19:52:46 +00:00
ast_rwlock_unlock ( & uris_lock );
2006-03-25 23:50:09 +00:00
return ;
2006-12-23 20:22:52 +00:00
}
prev = uris ;
2006-03-25 23:50:09 +00:00
if ( uris == urih ) {
uris = uris -> next ;
}
while ( prev -> next ) {
if ( prev -> next == urih ) {
prev -> next = urih -> next ;
break ;
}
prev = prev -> next ;
}
2006-12-28 19:52:46 +00:00
ast_rwlock_unlock ( & uris_lock );
2006-03-25 23:50:09 +00:00
}
2006-04-01 08:49:54 +00:00
static char * handle_uri ( struct sockaddr_in * sin , char * uri , int * status , char ** title , int * contentlength , struct ast_variable ** cookies )
2006-03-25 23:50:09 +00:00
{
char * c ;
char * turi ;
char * params ;
char * var ;
char * val ;
struct ast_http_uri * urih = NULL ;
int len ;
struct ast_variable * vars = NULL , * v , * prev = NULL ;
2006-09-19 19:58:09 +00:00
2006-03-25 23:50:09 +00:00
params = strchr ( uri , '?' );
if ( params ) {
* params = '\0' ;
params ++ ;
while (( var = strsep ( & params , "&" ))) {
val = strchr ( var , '=' );
if ( val ) {
* val = '\0' ;
val ++ ;
2006-04-01 08:49:54 +00:00
ast_uri_decode ( val );
2006-09-19 19:58:09 +00:00
} else
2006-03-25 23:50:09 +00:00
val = "" ;
ast_uri_decode ( var );
if (( v = ast_variable_new ( var , val ))) {
if ( vars )
prev -> next = v ;
else
vars = v ;
prev = v ;
}
}
}
2006-04-01 08:49:54 +00:00
if ( prev )
prev -> next = * cookies ;
else
vars = * cookies ;
* cookies = NULL ;
2006-03-25 23:50:09 +00:00
ast_uri_decode ( uri );
if ( ! strncasecmp ( uri , prefix , prefix_len )) {
uri += prefix_len ;
if ( !* uri || ( * uri == '/' )) {
if ( * uri == '/' )
uri ++ ;
2006-12-28 19:52:46 +00:00
ast_rwlock_rdlock ( & uris_lock );
2006-03-25 23:50:09 +00:00
urih = uris ;
while ( urih ) {
len = strlen ( urih -> uri );
if ( ! strncasecmp ( urih -> uri , uri , len )) {
if ( ! uri [ len ] || uri [ len ] == '/' ) {
turi = uri + len ;
if ( * turi == '/' )
turi ++ ;
if ( !* turi || urih -> has_subtree ) {
uri = turi ;
break ;
}
}
}
urih = urih -> next ;
}
2006-12-23 20:22:52 +00:00
if ( ! urih )
2006-12-28 19:52:46 +00:00
ast_rwlock_unlock ( & uris_lock );
2006-03-25 23:50:09 +00:00
}
}
if ( urih ) {
c = urih -> callback ( sin , uri , vars , status , title , contentlength );
2006-12-28 19:52:46 +00:00
ast_rwlock_unlock ( & uris_lock );
2006-04-02 09:10:43 +00:00
} else if ( ast_strlen_zero ( uri ) && ast_strlen_zero ( prefix )) {
/* Special case: If no prefix, and no URI, send to /static/index.html */
c = ast_http_error ( 302 , "Moved Temporarily" , "Location: /static/index.html \r\n " , "This is not the page you are looking for..." );
* status = 302 ;
* title = strdup ( "Moved Temporarily" );
2006-03-25 23:50:09 +00:00
} else {
2006-12-28 20:40:23 +00:00
c = ast_http_error ( 404 , "Not Found" , NULL , "The requested URL was not found on this server." );
2006-03-25 23:50:09 +00:00
* status = 404 ;
* title = strdup ( "Not Found" );
}
2006-10-18 04:09:50 +00:00
ast_variables_destroy ( vars );
2006-03-25 23:50:09 +00:00
return c ;
}
static void * ast_httpd_helper_thread ( void * data )
{
char buf [ 4096 ];
2006-04-01 08:49:54 +00:00
char cookie [ 4096 ];
2006-03-25 23:50:09 +00:00
char timebuf [ 256 ];
struct ast_http_server_instance * ser = data ;
2007-04-10 16:05:55 +00:00
struct ast_variable * var , * prev = NULL , * vars = NULL ;
2006-03-25 23:50:09 +00:00
char * uri , * c , * title = NULL ;
2006-04-01 08:49:54 +00:00
char * vname , * vval ;
2006-03-25 23:50:09 +00:00
int status = 200 , contentlength = 0 ;
time_t t ;
if ( fgets ( buf , sizeof ( buf ), ser -> f )) {
/* Skip method */
uri = buf ;
2006-04-03 18:38:28 +00:00
while ( * uri && ( * uri > 32 ))
uri ++ ;
2006-03-25 23:50:09 +00:00
if ( * uri ) {
* uri = '\0' ;
uri ++ ;
}
/* Skip white space */
2006-04-03 18:38:28 +00:00
while ( * uri && ( * uri < 33 ))
uri ++ ;
2006-03-25 23:50:09 +00:00
if ( * uri ) {
c = uri ;
2006-04-03 18:38:28 +00:00
while ( * c && ( * c > 32 ))
c ++ ;
2006-03-25 23:50:09 +00:00
if ( * c ) {
* c = '\0' ;
}
}
2006-04-01 08:49:54 +00:00
while ( fgets ( cookie , sizeof ( cookie ), ser -> f )) {
/* Trim trailing characters */
while ( ! ast_strlen_zero ( cookie ) && ( cookie [ strlen ( cookie ) - 1 ] < 33 )) {
cookie [ strlen ( cookie ) - 1 ] = '\0' ;
}
if ( ast_strlen_zero ( cookie ))
break ;
if ( ! strncasecmp ( cookie , "Cookie: " , 8 )) {
2006-08-17 04:26:28 +00:00
2006-09-19 19:58:09 +00:00
/* TODO - The cookie parsing code below seems to work
in IE6 and FireFox 1.5. However, it is not entirely
correct, and therefore may not work in all
circumstances.
2006-08-17 04:26:28 +00:00
For more details see RFC 2109 and RFC 2965 */
2006-09-19 19:58:09 +00:00
/* FireFox cookie strings look like:
Cookie: mansession_id="********"
InternetExplorer's look like:
2006-08-17 04:26:28 +00:00
Cookie: $Version="1"; mansession_id="********" */
2006-09-19 19:58:09 +00:00
/* If we got a FireFox cookie string, the name's right
2006-08-17 04:26:28 +00:00
after "Cookie: " */
vname = cookie + 8 ;
2006-09-19 19:58:09 +00:00
/* If we got an IE cookie string, we need to skip to
2006-08-17 04:26:28 +00:00
past the version to get to the name */
if ( * vname == '$' ) {
vname = strchr ( vname , ';' );
2006-09-19 19:58:09 +00:00
if ( vname ) {
2006-08-17 04:26:28 +00:00
vname ++ ;
if ( * vname == ' ' )
vname ++ ;
}
}
2006-09-19 19:58:09 +00:00
2006-08-17 04:26:28 +00:00
if ( vname ) {
vval = strchr ( vname , '=' );
if ( vval ) {
/* Ditch the = and the quotes */
* vval ++ = '\0' ;
if ( * vval )
vval ++ ;
if ( strlen ( vval ))
vval [ strlen ( vval ) - 1 ] = '\0' ;
var = ast_variable_new ( vname , vval );
if ( var ) {
if ( prev )
prev -> next = var ;
else
vars = var ;
prev = var ;
}
2006-04-01 08:49:54 +00:00
}
}
}
}
2006-03-25 23:50:09 +00:00
if ( * uri ) {
2006-09-19 19:58:09 +00:00
if ( ! strcasecmp ( buf , "get" ))
2006-04-01 08:49:54 +00:00
c = handle_uri ( & ser -> requestor , uri , & status , & title , & contentlength , & vars );
2006-09-19 19:58:09 +00:00
else
2006-03-25 23:50:09 +00:00
c = ast_http_error ( 501 , "Not Implemented" , NULL , "Attempt to use unimplemented / unsupported method" ); \
2006-09-19 19:58:09 +00:00
} else
2006-03-25 23:50:09 +00:00
c = ast_http_error ( 400 , "Bad Request" , NULL , "Invalid Request" );
2006-04-01 08:49:54 +00:00
/* If they aren't mopped up already, clean up the cookies */
if ( vars )
ast_variables_destroy ( vars );
2006-03-25 23:50:09 +00:00
if ( ! c )
c = ast_http_error ( 500 , "Internal Error" , NULL , "Internal Server Error" );
if ( c ) {
time ( & t );
strftime ( timebuf , sizeof ( timebuf ), "%a, %d %b %Y %H:%M:%S GMT" , gmtime ( & t ));
2006-04-01 08:49:54 +00:00
ast_cli ( ser -> fd , "HTTP/1.1 %d %s \r\n " , status , title ? title : "OK" );
2007-02-20 20:26:06 +00:00
ast_cli ( ser -> fd , "Server: Asterisk/%s \r\n " , ASTERISK_VERSION );
2006-03-25 23:50:09 +00:00
ast_cli ( ser -> fd , "Date: %s \r\n " , timebuf );
ast_cli ( ser -> fd , "Connection: close \r\n " );
2006-04-01 08:49:54 +00:00
if ( contentlength ) {
char * tmp ;
tmp = strstr ( c , " \r\n\r\n " );
if ( tmp ) {
ast_cli ( ser -> fd , "Content-length: %d \r\n " , contentlength );
write ( ser -> fd , c , ( tmp + 4 - c ));
write ( ser -> fd , tmp + 4 , contentlength );
}
} else
ast_cli ( ser -> fd , "%s" , c );
2006-03-25 23:50:09 +00:00
free ( c );
}
if ( title )
free ( title );
}
fclose ( ser -> f );
free ( ser );
return NULL ;
}
static void * http_root ( void * data )
{
int fd ;
struct sockaddr_in sin ;
2006-07-07 06:57:02 +00:00
socklen_t sinlen ;
2006-03-25 23:50:09 +00:00
struct ast_http_server_instance * ser ;
pthread_t launched ;
2006-04-02 23:55:15 +00:00
pthread_attr_t attr ;
2006-09-19 19:58:09 +00:00
2006-03-25 23:50:09 +00:00
for (;;) {
2006-10-24 08:34:23 +00:00
int flags ;
2006-03-25 23:50:09 +00:00
ast_wait_for_input ( httpfd , - 1 );
sinlen = sizeof ( sin );
fd = accept ( httpfd , ( struct sockaddr * ) & sin , & sinlen );
if ( fd < 0 ) {
if (( errno != EAGAIN ) && ( errno != EINTR ))
ast_log ( LOG_WARNING , "Accept failed: %s \n " , strerror ( errno ));
continue ;
}
2006-04-01 08:49:54 +00:00
ser = ast_calloc ( 1 , sizeof ( * ser ));
2006-10-24 08:34:23 +00:00
if ( ! ser ) {
ast_log ( LOG_WARNING , "No memory for new session: %s \n " , strerror ( errno ));
close ( fd );
continue ;
}
flags = fcntl ( fd , F_GETFL );
fcntl ( fd , F_SETFL , flags & ~ O_NONBLOCK );
ser -> fd = fd ;
memcpy ( & ser -> requestor , & sin , sizeof ( ser -> requestor ));
if (( ser -> f = fdopen ( ser -> fd , "w+" ))) {
pthread_attr_init ( & attr );
pthread_attr_setdetachstate ( & attr , PTHREAD_CREATE_DETACHED );
if ( ast_pthread_create_background ( & launched , & attr , ast_httpd_helper_thread , ser )) {
ast_log ( LOG_WARNING , "Unable to launch helper thread: %s \n " , strerror ( errno ));
fclose ( ser -> f );
2006-03-25 23:50:09 +00:00
free ( ser );
}
2007-01-31 21:32:08 +00:00
pthread_attr_destroy ( & attr );
2006-10-24 08:34:23 +00:00
} else {
ast_log ( LOG_WARNING , "fdopen failed! \n " );
close ( ser -> fd );
free ( ser );
2006-03-25 23:50:09 +00:00
}
}
return NULL ;
}
2006-04-03 18:38:28 +00:00
char * ast_http_setcookie ( const char * var , const char * val , int expires , char * buf , size_t buflen )
2006-04-01 08:49:54 +00:00
{
char * c ;
c = buf ;
ast_build_string ( & c , & buflen , "Set-Cookie: %s= \" %s \" ; Version= \" 1 \" " , var , val );
if ( expires )
ast_build_string ( & c , & buflen , "; Max-Age=%d" , expires );
ast_build_string ( & c , & buflen , " \r\n " );
return buf ;
}
2006-03-25 23:50:09 +00:00
static void http_server_start ( struct sockaddr_in * sin )
{
int flags ;
int x = 1 ;
2006-09-19 19:58:09 +00:00
2006-03-25 23:50:09 +00:00
/* Do nothing if nothing has changed */
if ( ! memcmp ( & oldsin , sin , sizeof ( oldsin ))) {
ast_log ( LOG_DEBUG , "Nothing changed in http \n " );
return ;
}
2006-09-19 19:58:09 +00:00
2006-03-25 23:50:09 +00:00
memcpy ( & oldsin , sin , sizeof ( oldsin ));
2006-09-19 19:58:09 +00:00
2006-03-25 23:50:09 +00:00
/* Shutdown a running server if there is one */
if ( master != AST_PTHREADT_NULL ) {
pthread_cancel ( master );
pthread_kill ( master , SIGURG );
pthread_join ( master , NULL );
}
2006-09-19 19:58:09 +00:00
2006-03-25 23:50:09 +00:00
if ( httpfd != - 1 )
close ( httpfd );
/* If there's no new server, stop here */
if ( ! sin -> sin_family )
return ;
2006-09-19 19:58:09 +00:00
2006-03-25 23:50:09 +00:00
httpfd = socket ( AF_INET , SOCK_STREAM , 0 );
if ( httpfd < 0 ) {
ast_log ( LOG_WARNING , "Unable to allocate socket: %s \n " , strerror ( errno ));
return ;
}
2006-09-19 19:58:09 +00:00
2006-03-25 23:50:09 +00:00
setsockopt ( httpfd , SOL_SOCKET , SO_REUSEADDR , & x , sizeof ( x ));
if ( bind ( httpfd , ( struct sockaddr * ) sin , sizeof ( * sin ))) {
ast_log ( LOG_NOTICE , "Unable to bind http server to %s:%d: %s \n " ,
2006-07-21 17:31:28 +00:00
ast_inet_ntoa ( sin -> sin_addr ), ntohs ( sin -> sin_port ),
2006-03-25 23:50:09 +00:00
strerror ( errno ));
close ( httpfd );
httpfd = - 1 ;
return ;
}
if ( listen ( httpfd , 10 )) {
ast_log ( LOG_NOTICE , "Unable to listen! \n " );
close ( httpfd );
httpfd = - 1 ;
return ;
}
flags = fcntl ( httpfd , F_GETFL );
fcntl ( httpfd , F_SETFL , flags | O_NONBLOCK );
2006-10-04 19:47:22 +00:00
if ( ast_pthread_create_background ( & master , NULL , http_root , NULL )) {
2006-03-25 23:50:09 +00:00
ast_log ( LOG_NOTICE , "Unable to launch http server on %s:%d: %s \n " ,
2006-07-21 17:31:28 +00:00
ast_inet_ntoa ( sin -> sin_addr ), ntohs ( sin -> sin_port ),
2006-03-25 23:50:09 +00:00
strerror ( errno ));
close ( httpfd );
httpfd = - 1 ;
}
}
static int __ast_http_load ( int reload )
{
struct ast_config * cfg ;
struct ast_variable * v ;
int enabled = 0 ;
2006-04-01 08:49:54 +00:00
int newenablestatic = 0 ;
2006-03-25 23:50:09 +00:00
struct sockaddr_in sin ;
struct hostent * hp ;
struct ast_hostent ahp ;
char newprefix [ MAX_PREFIX ];
2006-04-03 18:38:28 +00:00
2006-03-25 23:50:09 +00:00
memset ( & sin , 0 , sizeof ( sin ));
2007-01-23 21:52:52 +00:00
sin . sin_port = htons ( 8088 );
2007-04-06 20:58:43 +00:00
2006-03-25 23:50:09 +00:00
strcpy ( newprefix , DEFAULT_PREFIX );
2007-04-06 20:58:43 +00:00
2006-03-25 23:50:09 +00:00
cfg = ast_config_load ( "http.conf" );
if ( cfg ) {
v = ast_variable_browse ( cfg , "general" );
while ( v ) {
if ( ! strcasecmp ( v -> name , "enabled" ))
enabled = ast_true ( v -> value );
2006-04-01 08:49:54 +00:00
else if ( ! strcasecmp ( v -> name , "enablestatic" ))
newenablestatic = ast_true ( v -> value );
2006-03-25 23:50:09 +00:00
else if ( ! strcasecmp ( v -> name , "bindport" ))
sin . sin_port = ntohs ( atoi ( v -> value ));
else if ( ! strcasecmp ( v -> name , "bindaddr" )) {
if (( hp = ast_gethostbyname ( v -> value , & ahp ))) {
memcpy ( & sin . sin_addr , hp -> h_addr , sizeof ( sin . sin_addr ));
} else {
ast_log ( LOG_WARNING , "Invalid bind address '%s' \n " , v -> value );
}
2006-04-02 09:10:43 +00:00
} else if ( ! strcasecmp ( v -> name , "prefix" )) {
if ( ! ast_strlen_zero ( v -> value )) {
newprefix [ 0 ] = '/' ;
ast_copy_string ( newprefix + 1 , v -> value , sizeof ( newprefix ) - 1 );
} else {
newprefix [ 0 ] = '\0' ;
}
2006-09-19 19:58:09 +00:00
2006-04-02 09:10:43 +00:00
}
2006-03-25 23:50:09 +00:00
v = v -> next ;
}
ast_config_destroy ( cfg );
}
if ( enabled )
sin . sin_family = AF_INET ;
if ( strcmp ( prefix , newprefix )) {
ast_copy_string ( prefix , newprefix , sizeof ( prefix ));
prefix_len = strlen ( prefix );
}
2006-04-01 08:49:54 +00:00
enablestatic = newenablestatic ;
2007-04-06 20:58:43 +00:00
2006-03-25 23:50:09 +00:00
http_server_start ( & sin );
2007-04-06 20:58:43 +00:00
2006-03-25 23:50:09 +00:00
return 0 ;
}
static int handle_show_http ( int fd , int argc , char * argv [])
{
struct ast_http_uri * urih ;
2007-04-06 20:58:43 +00:00
2006-04-14 04:59:11 +00:00
if ( argc != 3 )
2006-03-25 23:50:09 +00:00
return RESULT_SHOWUSAGE ;
2007-04-06 20:58:43 +00:00
2006-03-25 23:50:09 +00:00
ast_cli ( fd , "HTTP Server Status: \n " );
ast_cli ( fd , "Prefix: %s \n " , prefix );
if ( oldsin . sin_family )
ast_cli ( fd , "Server Enabled and Bound to %s:%d \n\n " ,
2006-07-21 17:31:28 +00:00
ast_inet_ntoa ( oldsin . sin_addr ),
2006-03-25 23:50:09 +00:00
ntohs ( oldsin . sin_port ));
else
ast_cli ( fd , "Server Disabled \n\n " );
ast_cli ( fd , "Enabled URI's: \n " );
2006-12-28 19:52:46 +00:00
ast_rwlock_rdlock ( & uris_lock );
2006-03-25 23:50:09 +00:00
urih = uris ;
while ( urih ){
2006-04-02 09:10:43 +00:00
ast_cli ( fd , "%s/%s%s => %s \n " , prefix , urih -> uri , ( urih -> has_subtree ? "/..." : "" ), urih -> description );
2006-03-25 23:50:09 +00:00
urih = urih -> next ;
}
if ( ! uris )
ast_cli ( fd , "None. \n " );
2006-12-28 19:52:46 +00:00
ast_rwlock_unlock ( & uris_lock );
2007-04-06 20:58:43 +00:00
2006-03-25 23:50:09 +00:00
return RESULT_SUCCESS ;
}
int ast_http_reload ( void )
{
return __ast_http_load ( 1 );
}
static char show_http_help [] =
2006-11-02 23:00:20 +00:00
"Usage: http show status \n "
2006-09-18 19:54:18 +00:00
" Lists status of internal HTTP engine \n " ;
static struct ast_cli_entry cli_http [] = {
2006-11-02 23:00:20 +00:00
{ { "http" , "show" , "status" , NULL },
2006-09-18 19:54:18 +00:00
handle_show_http , "Display HTTP server status" ,
2006-11-02 23:00:20 +00:00
show_http_help },
2006-03-25 23:50:09 +00:00
};
int ast_http_init ( void )
{
ast_http_uri_link ( & statusuri );
2006-04-01 08:49:54 +00:00
ast_http_uri_link ( & staticuri );
2006-09-18 19:54:18 +00:00
ast_cli_register_multiple ( cli_http , sizeof ( cli_http ) / sizeof ( struct ast_cli_entry ));
2007-04-06 20:58:43 +00:00
2006-03-25 23:50:09 +00:00
return __ast_http_load ( 0 );
}