webserver interface nearly complete... allows custom auth and req dispatchers
This commit is contained in:
parent
4e7e48c6f4
commit
1aba1107cd
|
@ -0,0 +1,13 @@
|
|||
<HTML>
|
||||
<HEAD>
|
||||
<TITLE>mt-daapd administration</TITLE>
|
||||
</HEAD>
|
||||
<BODY>
|
||||
<h1>Configuration Updated</h1>
|
||||
<B>Web Root:</B> @WEB_ROOT@<BR>
|
||||
<B>Port:</B> @PORT@<BR>
|
||||
<B>Admin Password:</B> @ADMINPW@
|
||||
<HR>
|
||||
<i>@RELEASE@</i>
|
||||
</BODY>
|
||||
</HTML>
|
|
@ -0,0 +1,30 @@
|
|||
<HTML>
|
||||
<HEAD>
|
||||
<TITLE>mt-daapd administration</TITLE>
|
||||
</HEAD>
|
||||
<BODY>
|
||||
<h1>Config</h1>
|
||||
<FORM METHOD="POST" ACTION="/config-update.html">
|
||||
<TABLE>
|
||||
<TR>
|
||||
<TD>Web Root</TD>
|
||||
<TD><INPUT TYPE="TEXT" NAME="WEB_ROOT" READONLY
|
||||
VALUE="@WEB_ROOT@"></TD>
|
||||
</TR>
|
||||
<TR>
|
||||
<TD>Port</TD>
|
||||
<TD><INPUT TYPE="TEXT" NAME="PORT" READONLY
|
||||
VALUE="@PORT@"></TD>
|
||||
</TR>
|
||||
<TR>
|
||||
<TD>Admin Password</TD>
|
||||
<TD><INPUT TYPE="TEXT" NAME="ADMINPW"
|
||||
VALUE="@ADMINPW@"></TD>
|
||||
</TR>
|
||||
</TABLE>
|
||||
<INPUT TYPE="SUBMIT" NAME="SUBMIT" VALUE="Submit">
|
||||
</FORM>
|
||||
<HR>
|
||||
<i>@RELEASE@</i>
|
||||
</BODY>
|
||||
</HTML>
|
|
@ -44,4 +44,4 @@ AC_CHECK_LIB(socket,socket,V_NETLIBS="-lsocket $V_NETLIBS",,)
|
|||
|
||||
AC_SUBST(V_NETLIBS)
|
||||
|
||||
AC_OUTPUT(src/Makefile Makefile)
|
||||
AC_OUTPUT(src/Makefile docs/Makefile Makefile)
|
||||
|
|
25
src/Makefile
25
src/Makefile
|
@ -38,10 +38,10 @@ pkglibdir = $(libdir)/mt-daapd
|
|||
pkgincludedir = $(includedir)/mt-daapd
|
||||
top_builddir = ..
|
||||
|
||||
ACLOCAL = aclocal-1.6
|
||||
AUTOCONF = autoconf
|
||||
AUTOMAKE = automake-1.6
|
||||
AUTOHEADER = autoheader
|
||||
ACLOCAL = ${SHELL} /Users/ron/Documents/School/cs4953-Concurrency/mt-daapd/missing --run aclocal-1.6
|
||||
AUTOCONF = ${SHELL} /Users/ron/Documents/School/cs4953-Concurrency/mt-daapd/missing --run autoconf
|
||||
AUTOMAKE = ${SHELL} /Users/ron/Documents/School/cs4953-Concurrency/mt-daapd/missing --run automake-1.6
|
||||
AUTOHEADER = ${SHELL} /Users/ron/Documents/School/cs4953-Concurrency/mt-daapd/missing --run autoheader
|
||||
|
||||
am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
|
||||
INSTALL = /sw/bin/install -c
|
||||
|
@ -65,7 +65,7 @@ host_triplet = powerpc-apple-darwin6.8
|
|||
EXEEXT =
|
||||
OBJEXT = o
|
||||
PATH_SEPARATOR = :
|
||||
AMTAR = tar
|
||||
AMTAR = ${SHELL} /Users/ron/Documents/School/cs4953-Concurrency/mt-daapd/missing --run tar
|
||||
AWK = awk
|
||||
CC = gcc
|
||||
DEPDIR = .deps
|
||||
|
@ -76,13 +76,13 @@ VERSION = 0.1.0
|
|||
V_NETLIBS =
|
||||
am__include = include
|
||||
am__quote =
|
||||
install_sh = /Users/ron/Documents/School/cs4953 - Concurrency/mt-daapd/install-sh
|
||||
install_sh = /Users/ron/Documents/School/cs4953-Concurrency/mt-daapd/install-sh
|
||||
|
||||
# $Id$
|
||||
#
|
||||
sbin_PROGRAMS = mt-daapd
|
||||
|
||||
mt_daapd_SOURCES = main.c uici.c webserver.c configfile.c err.c
|
||||
mt_daapd_SOURCES = main.c uici.c webserver.c configfile.c err.c restart.c
|
||||
subdir = src
|
||||
mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
|
||||
CONFIG_HEADER = $(top_builddir)/config.h
|
||||
|
@ -91,7 +91,7 @@ sbin_PROGRAMS = mt-daapd$(EXEEXT)
|
|||
PROGRAMS = $(sbin_PROGRAMS)
|
||||
|
||||
am_mt_daapd_OBJECTS = main.$(OBJEXT) uici.$(OBJEXT) webserver.$(OBJEXT) \
|
||||
configfile.$(OBJEXT) err.$(OBJEXT)
|
||||
configfile.$(OBJEXT) err.$(OBJEXT) restart.$(OBJEXT)
|
||||
mt_daapd_OBJECTS = $(am_mt_daapd_OBJECTS)
|
||||
mt_daapd_LDADD = $(LDADD)
|
||||
mt_daapd_DEPENDENCIES =
|
||||
|
@ -105,8 +105,8 @@ LIBS =
|
|||
depcomp = $(SHELL) $(top_srcdir)/depcomp
|
||||
am__depfiles_maybe = depfiles
|
||||
DEP_FILES = ./$(DEPDIR)/configfile.Po ./$(DEPDIR)/err.Po \
|
||||
./$(DEPDIR)/main.Po ./$(DEPDIR)/uici.Po \
|
||||
./$(DEPDIR)/webserver.Po
|
||||
./$(DEPDIR)/main.Po ./$(DEPDIR)/restart.Po \
|
||||
./$(DEPDIR)/uici.Po ./$(DEPDIR)/webserver.Po
|
||||
COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
|
||||
$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
|
||||
CCLD = $(CC)
|
||||
|
@ -162,6 +162,7 @@ distclean-compile:
|
|||
include ./$(DEPDIR)/configfile.Po
|
||||
include ./$(DEPDIR)/err.Po
|
||||
include ./$(DEPDIR)/main.Po
|
||||
include ./$(DEPDIR)/restart.Po
|
||||
include ./$(DEPDIR)/uici.Po
|
||||
include ./$(DEPDIR)/webserver.Po
|
||||
|
||||
|
@ -322,10 +323,6 @@ uninstall-am: uninstall-info-am uninstall-sbinPROGRAMS
|
|||
mostlyclean-generic tags uninstall uninstall-am \
|
||||
uninstall-info-am uninstall-sbinPROGRAMS
|
||||
|
||||
#netview_LDADD = -ldotconf -lpcap -lncurses
|
||||
|
||||
# This should be autoconfiscated
|
||||
#INCLUDES = -I/usr/include/pcap
|
||||
# Tell versions [3.59,3.63) of GNU make to not export all variables.
|
||||
# Otherwise a system limit (for SysV at least) may be exceeded.
|
||||
.NOEXPORT:
|
||||
|
|
100
src/configfile.c
100
src/configfile.c
|
@ -19,27 +19,111 @@
|
|||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "configfile.h"
|
||||
#include "err.h"
|
||||
|
||||
/*
|
||||
* Defines
|
||||
*/
|
||||
|
||||
#define MAX_LINE 1024
|
||||
|
||||
|
||||
/*
|
||||
* config_read
|
||||
*
|
||||
* Read the specified config file.
|
||||
* Read the specified config file, padding the config structure
|
||||
* appropriately.
|
||||
*
|
||||
* This function returns 0 on success, errorcode on failure
|
||||
*/
|
||||
int config_read(char *file);
|
||||
int config_read(char *file, CONFIG *pconfig) {
|
||||
FILE *fin;
|
||||
char *buffer;
|
||||
int err;
|
||||
char *value;
|
||||
char *comment;
|
||||
err=0;
|
||||
|
||||
buffer=(char*)malloc(MAX_LINE);
|
||||
if(!buffer)
|
||||
return -1;
|
||||
|
||||
if((fin=fopen(file,"r")) == NULL) {
|
||||
err=errno;
|
||||
free(buffer);
|
||||
errno=err;
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(pconfig,0,sizeof(CONFIG));
|
||||
|
||||
pconfig->configfile=strdup(file);
|
||||
|
||||
while(fgets(buffer,MAX_LINE,fin)) {
|
||||
if(*buffer != '#') {
|
||||
value=buffer;
|
||||
strsep(&value,"\t ");
|
||||
if(value) {
|
||||
while((*value==' ')||(*value=='\t'))
|
||||
value++;
|
||||
|
||||
comment=value;
|
||||
strsep(&comment,"#");
|
||||
|
||||
if(value[strlen(value)-1] == '\n')
|
||||
value[strlen(value)-1] = '\0';
|
||||
|
||||
if(!strcasecmp(buffer,"web_root")) {
|
||||
pconfig->web_root=strdup(value);
|
||||
DPRINTF(ERR_DEBUG,"Web root: %s\n",value);
|
||||
} else if(!strcasecmp(buffer,"port")) {
|
||||
pconfig->port=atoi(value);
|
||||
DPRINTF(ERR_DEBUG,"Port: %d\n",pconfig->port);
|
||||
} else if(!strcasecmp(buffer,"admin_password")) {
|
||||
pconfig->adminpassword=strdup(value);
|
||||
DPRINTF(ERR_DEBUG,"Admin pw: %s\n",value);
|
||||
} else {
|
||||
DPRINTF(ERR_INFO,"Bad config directive: %s\n",buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fclose(fin);
|
||||
|
||||
if(!pconfig->web_root) {
|
||||
fprintf(stderr,"Config: missing web_root entry\n");
|
||||
errno=EINVAL;
|
||||
err=-1;
|
||||
}
|
||||
|
||||
if(!pconfig->adminpassword) {
|
||||
fprintf(stderr,"Config: missing admin_password entry\n");
|
||||
errno=EINVAL;
|
||||
err=-1;
|
||||
}
|
||||
|
||||
if(!pconfig->port) {
|
||||
fprintf(stderr,"Config: missing port entry\n");
|
||||
errno=EINVAL;
|
||||
err=-1;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* config_write
|
||||
*
|
||||
*/
|
||||
int config_write(void);
|
||||
int config_write(CONFIG *pconfig) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* config_change
|
||||
*
|
||||
*/
|
||||
int config_change(void);
|
||||
|
|
|
@ -22,8 +22,9 @@
|
|||
#ifndef _CONFIGFILE_H_
|
||||
#define _CONFIGFILE_H_
|
||||
|
||||
extern int config_read(char *file);
|
||||
extern int config_write(void);
|
||||
extern int config_change(void);
|
||||
#include "daapd.h"
|
||||
|
||||
extern int config_read(char *file, CONFIG *pconfig);
|
||||
extern int config_write(CONFIG *pconfig);
|
||||
|
||||
#endif /* _CONFIGFILE_H_ */
|
||||
|
|
|
@ -31,6 +31,7 @@ typedef struct tag_songentry {
|
|||
|
||||
typedef struct tag_config {
|
||||
char *configfile;
|
||||
char *web_root;
|
||||
int port;
|
||||
char *adminpassword;
|
||||
char *readpassword;
|
||||
|
|
198
src/main.c
198
src/main.c
|
@ -19,33 +19,217 @@
|
|||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "configfile.h"
|
||||
#include "err.h"
|
||||
#include "webserver.h"
|
||||
|
||||
// 3689
|
||||
|
||||
|
||||
/*
|
||||
* Globals
|
||||
*/
|
||||
CONFIG config;
|
||||
|
||||
|
||||
/*
|
||||
* config_handler
|
||||
*
|
||||
* Handle config web pages
|
||||
*/
|
||||
|
||||
void config_handler(WS_CONNINFO *pwsc) {
|
||||
char path[PATH_MAX];
|
||||
char resolved_path[PATH_MAX];
|
||||
int file_fd;
|
||||
struct stat sb;
|
||||
char argbuffer[30];
|
||||
int in_arg;
|
||||
char *argptr;
|
||||
char next;
|
||||
|
||||
pwsc->close=1;
|
||||
ws_addresponseheader(pwsc,"Connection","close");
|
||||
|
||||
snprintf(path,PATH_MAX,"%s/%s",config.web_root,pwsc->uri);
|
||||
if(!realpath(path,resolved_path)) {
|
||||
pwsc->error=errno;
|
||||
DPRINTF(ERR_WARN,"Cannot resolve %s\n",path);
|
||||
ws_returnerror(pwsc,404,"Not found");
|
||||
return;
|
||||
}
|
||||
|
||||
/* this should really return a 302:Found */
|
||||
stat(resolved_path,&sb);
|
||||
if(sb.st_mode & S_IFDIR)
|
||||
strcat(resolved_path,"/index.html");
|
||||
|
||||
DPRINTF(ERR_DEBUG,"Thread %d: Preparing to serve %s\n",
|
||||
pwsc->threadno, resolved_path);
|
||||
|
||||
if(strncmp(resolved_path,config.web_root,
|
||||
strlen(config.web_root))) {
|
||||
pwsc->error=EINVAL;
|
||||
DPRINTF(ERR_WARN,"Thread %d: Requested file %s out of root\n",
|
||||
pwsc->threadno,resolved_path);
|
||||
ws_returnerror(pwsc,403,"Forbidden");
|
||||
return;
|
||||
}
|
||||
|
||||
file_fd=open(resolved_path,O_RDONLY);
|
||||
if(file_fd == -1) {
|
||||
pwsc->error=errno;
|
||||
DPRINTF(ERR_WARN,"Thread %d: Error opening %s: %s\n",
|
||||
pwsc->threadno,resolved_path,strerror(errno));
|
||||
ws_returnerror(pwsc,404,"Not found");
|
||||
return;
|
||||
}
|
||||
|
||||
if(strcasecmp(pwsc->uri,"/config-update.html")==0) {
|
||||
/* we need to update stuff */
|
||||
argptr=ws_getvar(pwsc,"adminpw");
|
||||
if(argptr) {
|
||||
config.adminpassword=strdup(argptr);
|
||||
}
|
||||
}
|
||||
|
||||
ws_writefd(pwsc,"HTTP/1.1 200 OK\r\n");
|
||||
ws_emitheaders(pwsc);
|
||||
|
||||
/* now throw out the file, with replacements */
|
||||
in_arg=0;
|
||||
argptr=argbuffer;
|
||||
|
||||
while(1) {
|
||||
if(r_read(file_fd,&next,1) <= 0)
|
||||
break;
|
||||
|
||||
if(in_arg) {
|
||||
if(next == '@') {
|
||||
in_arg=0;
|
||||
if(strcasecmp(argbuffer,"WEB_ROOT") == 0) {
|
||||
ws_writefd(pwsc,"%s",config.web_root);
|
||||
} else if (strcasecmp(argbuffer,"PORT") == 0) {
|
||||
ws_writefd(pwsc,"%d",config.port);
|
||||
} else if (strcasecmp(argbuffer,"ADMINPW") == 0) {
|
||||
ws_writefd(pwsc,"%s",config.adminpassword);
|
||||
} else if (strcasecmp(argbuffer,"RELEASE") == 0) {
|
||||
ws_writefd(pwsc,"mt-daapd %s\n",VERSION);
|
||||
} else {
|
||||
ws_writefd(pwsc,"@ERR@");
|
||||
}
|
||||
} else {
|
||||
if((argptr - argbuffer) < (sizeof(argbuffer)-1))
|
||||
*argptr++ = next;
|
||||
}
|
||||
} else {
|
||||
if(next == '@') {
|
||||
argptr=argbuffer;
|
||||
memset(argbuffer,0,sizeof(argbuffer));
|
||||
in_arg=1;
|
||||
} else {
|
||||
if(r_write(pwsc->fd,&next,1) == -1)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
close(file_fd);
|
||||
DPRINTF(ERR_DEBUG,"Thread %d: Served successfully\n",pwsc->threadno);
|
||||
return;
|
||||
}
|
||||
|
||||
int config_auth(char *user, char *password) {
|
||||
return !strcmp(password,config.adminpassword);
|
||||
}
|
||||
|
||||
|
||||
void usage(char *program) {
|
||||
printf("Usage: %s [options]\n\n",program);
|
||||
printf("Options:\n");
|
||||
#ifdef DEBUG
|
||||
printf(" -d <number> Debuglevel (0-9)\n");
|
||||
#endif
|
||||
printf(" -c <file> Use configfile specified");
|
||||
printf("\n\n");
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
int status;
|
||||
char *confdir=NULL;
|
||||
int option;
|
||||
char *configfile=NULL;
|
||||
WSCONFIG ws_config;
|
||||
WSHANDLE server;
|
||||
#ifdef DEBUG
|
||||
char *optval="d:c:";
|
||||
#else
|
||||
char *optval="c:";
|
||||
#endif /* DEBUG */
|
||||
|
||||
printf("mt-daapd: version $Revision$\n\n");
|
||||
|
||||
ws_config.web_root="/Users/ron/Documents/rfc";
|
||||
ws_config.port=80;
|
||||
printf("mt-daapd: version $Revision$\n");
|
||||
printf("Copyright (c) 2003 Ron Pedde. All rights reserved\n");
|
||||
printf("Portions Copyright (c) 1999-2001 Apple Computer, Inc. All rights Reserved.\n\n");
|
||||
|
||||
while((option=getopt(argc,argv,optval)) != -1) {
|
||||
switch(option) {
|
||||
#ifdef DEBUG
|
||||
case 'd':
|
||||
err_debuglevel=atoi(optarg);
|
||||
break;
|
||||
#endif
|
||||
case 'c':
|
||||
configfile=optarg;
|
||||
break;
|
||||
default:
|
||||
usage(argv[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* read the configfile, if specified, otherwise
|
||||
* try defaults */
|
||||
|
||||
if(!configfile) {
|
||||
if(config_read("/etc/mt-daapd.conf",&config))
|
||||
if(config_read("./mt-daapd.conf",&config)) {
|
||||
perror("configfile_read");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
} else {
|
||||
if(config_read(configfile,&config)) {
|
||||
perror("config_read");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
/* should verify the configfile has enough info
|
||||
* to start */
|
||||
|
||||
/* start up the web server */
|
||||
ws_config.web_root=config.web_root;
|
||||
ws_config.port=config.port;
|
||||
|
||||
err_debuglevel=9;
|
||||
server=ws_start(&ws_config);
|
||||
if(!server) {
|
||||
perror("ws_start");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
ws_registerhandler(server, "^.*$",config_handler,config_auth);
|
||||
|
||||
while(1) {
|
||||
sleep(20);
|
||||
}
|
||||
|
|
|
@ -1,2 +1,5 @@
|
|||
web_root /Users/ron/Documents/rfc
|
||||
port 80
|
||||
web_root /Users/ron/Documents/School/cs4953-Concurrency/mt-daapd/admin-root
|
||||
port 3689
|
||||
admin_password secret
|
||||
|
||||
|
||||
|
|
329
src/restart.c
329
src/restart.c
|
@ -5,6 +5,7 @@
|
|||
#include <sys/select.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/wait.h>
|
||||
#include "err.h"
|
||||
#include "restart.h"
|
||||
#define BLKSIZE PIPE_BUF
|
||||
#define MILLION 1000000L
|
||||
|
@ -13,220 +14,244 @@
|
|||
/* Private functions */
|
||||
|
||||
static int gettimeout(struct timeval end,
|
||||
struct timeval *timeoutp) {
|
||||
gettimeofday(timeoutp, NULL);
|
||||
timeoutp->tv_sec = end.tv_sec - timeoutp->tv_sec;
|
||||
timeoutp->tv_usec = end.tv_usec - timeoutp->tv_usec;
|
||||
if (timeoutp->tv_usec >= MILLION) {
|
||||
timeoutp->tv_sec++;
|
||||
timeoutp->tv_usec -= MILLION;
|
||||
}
|
||||
if (timeoutp->tv_usec < 0) {
|
||||
timeoutp->tv_sec--;
|
||||
timeoutp->tv_usec += MILLION;
|
||||
}
|
||||
if ((timeoutp->tv_sec < 0) ||
|
||||
((timeoutp->tv_sec == 0) && (timeoutp->tv_usec == 0))) {
|
||||
errno = ETIME;
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
struct timeval *timeoutp) {
|
||||
gettimeofday(timeoutp, NULL);
|
||||
timeoutp->tv_sec = end.tv_sec - timeoutp->tv_sec;
|
||||
timeoutp->tv_usec = end.tv_usec - timeoutp->tv_usec;
|
||||
if (timeoutp->tv_usec >= MILLION) {
|
||||
timeoutp->tv_sec++;
|
||||
timeoutp->tv_usec -= MILLION;
|
||||
}
|
||||
if (timeoutp->tv_usec < 0) {
|
||||
timeoutp->tv_sec--;
|
||||
timeoutp->tv_usec += MILLION;
|
||||
}
|
||||
if ((timeoutp->tv_sec < 0) ||
|
||||
((timeoutp->tv_sec == 0) && (timeoutp->tv_usec == 0))) {
|
||||
errno = ETIME;
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Restart versions of traditional functions */
|
||||
|
||||
int r_close(int fildes) {
|
||||
int retval;
|
||||
while (retval = close(fildes), retval == -1 && errno == EINTR) ;
|
||||
return retval;
|
||||
int retval;
|
||||
while (retval = close(fildes), retval == -1 && errno == EINTR) ;
|
||||
return retval;
|
||||
}
|
||||
|
||||
int r_dup2(int fildes, int fildes2) {
|
||||
int retval;
|
||||
while (retval = dup2(fildes, fildes2), retval == -1 && errno == EINTR) ;
|
||||
return retval;
|
||||
int retval;
|
||||
while (retval = dup2(fildes, fildes2), retval == -1 && errno == EINTR) ;
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
int r_open2(const char *path, int oflag) {
|
||||
int retval;
|
||||
while (retval = open(path, oflag), retval == -1 && errno == EINTR) ;
|
||||
return retval;
|
||||
int retval;
|
||||
while (retval = open(path, oflag), retval == -1 && errno == EINTR) ;
|
||||
return retval;
|
||||
}
|
||||
|
||||
int r_open3(const char *path, int oflag, mode_t mode) {
|
||||
int retval;
|
||||
while (retval = open(path, oflag, mode), retval == -1 && errno == EINTR) ;
|
||||
return retval;
|
||||
int retval;
|
||||
while (retval = open(path, oflag, mode), retval == -1 && errno == EINTR) ;
|
||||
return retval;
|
||||
}
|
||||
|
||||
ssize_t r_read(int fd, void *buf, size_t size) {
|
||||
ssize_t retval;
|
||||
while (retval = read(fd, buf, size), retval == -1 && errno == EINTR) ;
|
||||
return retval;
|
||||
ssize_t retval;
|
||||
while (((retval = read(fd, buf, size)) == -1) && (errno==EINTR)) {};
|
||||
return retval;
|
||||
}
|
||||
|
||||
pid_t r_wait(int *stat_loc) {
|
||||
pid_t retval;
|
||||
while (((retval = wait(stat_loc)) == -1) && (errno == EINTR)) ;
|
||||
return retval;
|
||||
pid_t retval;
|
||||
while (((retval = wait(stat_loc)) == -1) && (errno == EINTR)) ;
|
||||
return retval;
|
||||
}
|
||||
|
||||
pid_t r_waitpid(pid_t pid, int *stat_loc, int options) {
|
||||
pid_t retval;
|
||||
while (((retval = waitpid(pid, stat_loc, options)) == -1) &&
|
||||
pid_t retval;
|
||||
while (((retval = waitpid(pid, stat_loc, options)) == -1) &&
|
||||
(errno == EINTR)) ;
|
||||
return retval;
|
||||
return retval;
|
||||
}
|
||||
|
||||
ssize_t r_write(int fd, void *buf, size_t size) {
|
||||
char *bufp;
|
||||
size_t bytestowrite;
|
||||
ssize_t byteswritten;
|
||||
size_t totalbytes;
|
||||
char *bufp;
|
||||
size_t bytestowrite;
|
||||
ssize_t byteswritten;
|
||||
size_t totalbytes;
|
||||
|
||||
for (bufp = buf, bytestowrite = size, totalbytes = 0;
|
||||
bytestowrite > 0;
|
||||
bufp += byteswritten, bytestowrite -= byteswritten) {
|
||||
byteswritten = write(fd, bufp, bytestowrite);
|
||||
if ((byteswritten) == -1 && (errno != EINTR))
|
||||
return -1;
|
||||
if (byteswritten == -1)
|
||||
byteswritten = 0;
|
||||
totalbytes += byteswritten;
|
||||
}
|
||||
return totalbytes;
|
||||
for (bufp = buf, bytestowrite = size, totalbytes = 0;
|
||||
bytestowrite > 0;
|
||||
bufp += byteswritten, bytestowrite -= byteswritten) {
|
||||
byteswritten = write(fd, bufp, bytestowrite);
|
||||
if ((byteswritten) == -1 && (errno != EINTR))
|
||||
return -1;
|
||||
if (byteswritten == -1)
|
||||
byteswritten = 0;
|
||||
totalbytes += byteswritten;
|
||||
}
|
||||
return totalbytes;
|
||||
}
|
||||
|
||||
/* Utility functions */
|
||||
|
||||
struct timeval add2currenttime(double seconds) {
|
||||
struct timeval newtime;
|
||||
struct timeval newtime;
|
||||
|
||||
gettimeofday(&newtime, NULL);
|
||||
newtime.tv_sec += (int)seconds;
|
||||
newtime.tv_usec += (int)((seconds - (int)seconds)*D_MILLION + 0.5);
|
||||
if (newtime.tv_usec >= MILLION) {
|
||||
newtime.tv_sec++;
|
||||
newtime.tv_usec -= MILLION;
|
||||
}
|
||||
return newtime;
|
||||
gettimeofday(&newtime, NULL);
|
||||
newtime.tv_sec += (int)seconds;
|
||||
newtime.tv_usec += (int)((seconds - (int)seconds)*D_MILLION + 0.5);
|
||||
if (newtime.tv_usec >= MILLION) {
|
||||
newtime.tv_sec++;
|
||||
newtime.tv_usec -= MILLION;
|
||||
}
|
||||
return newtime;
|
||||
}
|
||||
|
||||
int copyfile(int fromfd, int tofd) {
|
||||
int bytesread;
|
||||
int totalbytes = 0;
|
||||
int bytesread;
|
||||
int totalbytes = 0;
|
||||
|
||||
while ((bytesread = readwrite(fromfd, tofd)) > 0)
|
||||
totalbytes += bytesread;
|
||||
return totalbytes;
|
||||
while ((bytesread = readwrite(fromfd, tofd)) > 0)
|
||||
totalbytes += bytesread;
|
||||
return totalbytes;
|
||||
}
|
||||
|
||||
ssize_t readblock(int fd, void *buf, size_t size) {
|
||||
char *bufp;
|
||||
ssize_t bytesread;
|
||||
size_t bytestoread;
|
||||
size_t totalbytes;
|
||||
char *bufp;
|
||||
ssize_t bytesread;
|
||||
size_t bytestoread;
|
||||
size_t totalbytes;
|
||||
|
||||
for (bufp = buf, bytestoread = size, totalbytes = 0;
|
||||
bytestoread > 0;
|
||||
bufp += bytesread, bytestoread -= bytesread) {
|
||||
bytesread = read(fd, bufp, bytestoread);
|
||||
if ((bytesread == 0) && (totalbytes == 0))
|
||||
return 0;
|
||||
if (bytesread == 0) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
if ((bytesread) == -1 && (errno != EINTR))
|
||||
return -1;
|
||||
if (bytesread == -1)
|
||||
bytesread = 0;
|
||||
totalbytes += bytesread;
|
||||
}
|
||||
return totalbytes;
|
||||
for (bufp = buf, bytestoread = size, totalbytes = 0;
|
||||
bytestoread > 0;
|
||||
bufp += bytesread, bytestoread -= bytesread) {
|
||||
bytesread = read(fd, bufp, bytestoread);
|
||||
if ((bytesread == 0) && (totalbytes == 0))
|
||||
return 0;
|
||||
if (bytesread == 0) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
if ((bytesread) == -1 && (errno != EINTR))
|
||||
return -1;
|
||||
if (bytesread == -1)
|
||||
bytesread = 0;
|
||||
totalbytes += bytesread;
|
||||
}
|
||||
return totalbytes;
|
||||
}
|
||||
|
||||
int readline(int fd, char *buf, int nbytes) {
|
||||
int numread = 0;
|
||||
int returnval;
|
||||
int numread = 0;
|
||||
int returnval;
|
||||
|
||||
while (numread < nbytes - 1) {
|
||||
returnval = read(fd, buf + numread, 1);
|
||||
if ((returnval == -1) && (errno == EINTR))
|
||||
continue;
|
||||
if ((returnval == 0) && (numread == 0))
|
||||
return 0;
|
||||
if (returnval == 0)
|
||||
break;
|
||||
if (returnval == -1)
|
||||
return -1;
|
||||
numread++;
|
||||
if (buf[numread-1] == '\n') {
|
||||
buf[numread] = '\0';
|
||||
return numread;
|
||||
}
|
||||
}
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
while (numread < nbytes - 1) {
|
||||
returnval = read(fd, buf + numread, 1);
|
||||
if ((returnval == -1) && (errno == EINTR))
|
||||
continue;
|
||||
if ((returnval == 0) && (numread == 0))
|
||||
return 0;
|
||||
if (returnval == 0)
|
||||
break;
|
||||
if (returnval == -1)
|
||||
return -1;
|
||||
numread++;
|
||||
if (buf[numread-1] == '\n') {
|
||||
buf[numread] = '\0';
|
||||
return numread;
|
||||
}
|
||||
}
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int readlinetimed(int fd, char *buf, int nbytes, double seconds) {
|
||||
int numread = 0;
|
||||
int returnval;
|
||||
|
||||
while (numread < nbytes - 1) {
|
||||
returnval = (int)readtimed(fd, buf + numread, 1, seconds);
|
||||
if ((returnval == -1) && (errno == EINTR))
|
||||
continue;
|
||||
if ((returnval == 0) && (numread == 0))
|
||||
return 0;
|
||||
if (returnval == 0)
|
||||
break;
|
||||
if (returnval == -1)
|
||||
return -1;
|
||||
numread++;
|
||||
if (buf[numread-1] == '\n') {
|
||||
buf[numread] = '\0';
|
||||
return numread;
|
||||
}
|
||||
}
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
ssize_t readtimed(int fd, void *buf, size_t nbyte, double seconds) {
|
||||
struct timeval timedone;
|
||||
struct timeval timedone;
|
||||
|
||||
timedone = add2currenttime(seconds);
|
||||
if (waitfdtimed(fd, timedone) == -1)
|
||||
return (ssize_t)(-1);
|
||||
return r_read(fd, buf, nbyte);
|
||||
timedone = add2currenttime(seconds);
|
||||
if (waitfdtimed(fd, timedone) == -1)
|
||||
return (ssize_t)(-1);
|
||||
return r_read(fd, buf, nbyte);
|
||||
}
|
||||
|
||||
int readwrite(int fromfd, int tofd) {
|
||||
char buf[BLKSIZE];
|
||||
int bytesread;
|
||||
char buf[BLKSIZE];
|
||||
int bytesread;
|
||||
|
||||
if ((bytesread = r_read(fromfd, buf, BLKSIZE)) < 0)
|
||||
return -1;
|
||||
if (bytesread == 0)
|
||||
return 0;
|
||||
if (r_write(tofd, buf, bytesread) < 0)
|
||||
return -1;
|
||||
return bytesread;
|
||||
if ((bytesread = r_read(fromfd, buf, BLKSIZE)) < 0)
|
||||
return -1;
|
||||
if (bytesread == 0)
|
||||
return 0;
|
||||
if (r_write(tofd, buf, bytesread) < 0)
|
||||
return -1;
|
||||
return bytesread;
|
||||
}
|
||||
|
||||
int readwriteblock(int fromfd, int tofd, char *buf, int size) {
|
||||
int bytesread;
|
||||
int bytesread;
|
||||
|
||||
bytesread = readblock(fromfd, buf, size);
|
||||
if (bytesread != size) /* can only be 0 or -1 */
|
||||
return bytesread;
|
||||
return r_write(tofd, buf, size);
|
||||
bytesread = readblock(fromfd, buf, size);
|
||||
if (bytesread != size) /* can only be 0 or -1 */
|
||||
return bytesread;
|
||||
return r_write(tofd, buf, size);
|
||||
}
|
||||
|
||||
int waitfdtimed(int fd, struct timeval end) {
|
||||
fd_set readset;
|
||||
int retval;
|
||||
struct timeval timeout;
|
||||
fd_set readset;
|
||||
int retval;
|
||||
struct timeval timeout;
|
||||
|
||||
if ((fd < 0) || (fd >= FD_SETSIZE)) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
FD_ZERO(&readset);
|
||||
FD_SET(fd, &readset);
|
||||
if (gettimeout(end, &timeout) == -1)
|
||||
return -1;
|
||||
while (((retval = select(fd+1, &readset, NULL, NULL, &timeout)) == -1)
|
||||
if ((fd < 0) || (fd >= FD_SETSIZE)) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
FD_ZERO(&readset);
|
||||
FD_SET(fd, &readset);
|
||||
if (gettimeout(end, &timeout) == -1)
|
||||
return -1;
|
||||
while (((retval = select(fd+1, &readset, NULL, NULL, &timeout)) == -1)
|
||||
&& (errno == EINTR)) {
|
||||
if (gettimeout(end, &timeout) == -1)
|
||||
return -1;
|
||||
FD_ZERO(&readset);
|
||||
FD_SET(fd, &readset);
|
||||
}
|
||||
if (retval == 0) {
|
||||
errno = ETIME;
|
||||
return -1;
|
||||
}
|
||||
if (retval == -1)
|
||||
return -1;
|
||||
return 0;
|
||||
if (gettimeout(end, &timeout) == -1)
|
||||
return -1;
|
||||
FD_ZERO(&readset);
|
||||
FD_SET(fd, &readset);
|
||||
}
|
||||
if (retval == 0) {
|
||||
errno = ETIME;
|
||||
return -1;
|
||||
}
|
||||
if (retval == -1)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ pid_t r_waitpid(pid_t pid, int *stat_loc, int options);
|
|||
ssize_t r_write(int fd, void *buf, size_t size);
|
||||
ssize_t readblock(int fd, void *buf, size_t size);
|
||||
int readline(int fd, char *buf, int nbytes);
|
||||
int readlinetimed(int fd, char *buf, int nbytes, double seconds);
|
||||
ssize_t readtimed(int fd, void *buf, size_t nbyte, double seconds);
|
||||
int readwrite(int fromfd, int tofd);
|
||||
int readwriteblock(int fromfd, int tofd, char *buf, int size);
|
||||
|
|
748
src/webserver.c
748
src/webserver.c
|
@ -19,8 +19,13 @@
|
|||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <regex.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
@ -28,6 +33,7 @@
|
|||
#include <pthread.h>
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "err.h"
|
||||
#include "webserver.h"
|
||||
|
@ -39,18 +45,24 @@
|
|||
#define MAX_HOSTNAME 256
|
||||
#define MAX_LINEBUFFER 256
|
||||
|
||||
#ifndef VERSION
|
||||
# define VERSION "Version 0.1 beta"
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Local (private) typedefs
|
||||
*/
|
||||
|
||||
typedef struct tag_ws_handler {
|
||||
regex_t regex;
|
||||
void (*req_handler)(WS_CONNINFO*);
|
||||
int(*auth_handler)(char *, char *);
|
||||
struct tag_ws_handler *next;
|
||||
} WS_HANDLER;
|
||||
|
||||
typedef struct tag_ws_private {
|
||||
WSCONFIG wsconfig;
|
||||
WS_HANDLER handlers;
|
||||
int server_fd;
|
||||
int stop;
|
||||
int running;
|
||||
int threadno;
|
||||
pthread_t server_tid;
|
||||
} WS_PRIVATE;
|
||||
|
||||
|
@ -67,12 +79,23 @@ void *ws_dispatcher(void*);
|
|||
int ws_makeargv(const char *s, const char *delimiters, char ***argvp);
|
||||
int ws_lock_unsafe(void);
|
||||
int ws_unlock_unsafe(void);
|
||||
int ws_writefd(WS_CONNINFO *pwsc,char *buffer, int len, char *fmt, ...);
|
||||
void ws_defaulthandler(WS_PRIVATE *pwsp, WS_CONNINFO *pwsc);
|
||||
int ws_addarg(ARGLIST *root, char *key, char *value);
|
||||
int ws_addarg(ARGLIST *root, char *key, char *fmt, ...);
|
||||
void ws_freearglist(ARGLIST *root);
|
||||
char *ws_urldecode(char *string);
|
||||
int ws_getheaders(WS_CONNINFO *pwsc);
|
||||
int ws_getpostvars(WS_CONNINFO *pwsc);
|
||||
int ws_getgetvars(WS_CONNINFO *pwsc, char *string);
|
||||
char *ws_getarg(ARGLIST *root, char *key);
|
||||
int ws_testarg(ARGLIST *root, char *key, char *value);
|
||||
void ws_emitheaders(WS_CONNINFO *pwsc);
|
||||
int ws_findhandler(WS_PRIVATE *pwsp, WS_CONNINFO *pwsc,
|
||||
void(**preq)(WS_CONNINFO*),
|
||||
int(**pauth)(char *, char *));
|
||||
int ws_registerhandler(WSHANDLE ws, char *regex,
|
||||
void(*handler)(WS_CONNINFO*),
|
||||
int(*auth)(char *, char *));
|
||||
int ws_decodepassword(char *header, char **username, char **password);
|
||||
|
||||
/*
|
||||
* Globals
|
||||
|
@ -140,6 +163,8 @@ WSHANDLE ws_start(WSCONFIG *config) {
|
|||
|
||||
memcpy(&pwsp->wsconfig,config,sizeof(WS_PRIVATE));
|
||||
pwsp->running=0;
|
||||
pwsp->threadno=0;
|
||||
pwsp->handlers.next=NULL;
|
||||
|
||||
DPRINTF(ERR_INFO,"Preparing to listen on port %d\n",pwsp->wsconfig.port);
|
||||
|
||||
|
@ -172,6 +197,7 @@ WSHANDLE ws_start(WSCONFIG *config) {
|
|||
int ws_stop(WSHANDLE arg) {
|
||||
WS_PRIVATE *pwsp = (WS_PRIVATE*)arg;
|
||||
|
||||
/* free the ws_handlers */
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -222,6 +248,7 @@ void *ws_mainthread(void *arg) {
|
|||
pwsd=(WS_DISPATCHINFO*)malloc(sizeof(WS_DISPATCHINFO));
|
||||
if(!pwsd) {
|
||||
/* keep on trucking until we crash bigger */
|
||||
pwsc->error=EINVAL;
|
||||
DPRINTF(ERR_FATAL,"Error: %s\n",strerror(errno));
|
||||
free(pwsd);
|
||||
ws_close(pwsc);
|
||||
|
@ -229,8 +256,15 @@ void *ws_mainthread(void *arg) {
|
|||
pwsd->pwsp=pwsp;
|
||||
pwsd->pwsc=pwsc;
|
||||
|
||||
/* don't really care if it locks or not */
|
||||
ws_lock_unsafe();
|
||||
pwsc->threadno=pwsp->threadno;
|
||||
pwsp->threadno++;
|
||||
ws_unlock_unsafe();
|
||||
|
||||
/* now, throw off a dispatch thread */
|
||||
if(err=pthread_create(&tid,NULL,ws_dispatcher,(void*)pwsd)) {
|
||||
pwsc->error=err;
|
||||
DPRINTF(ERR_WARN,"Could not spawn thread: %s\n",strerror(err));
|
||||
free(pwsd);
|
||||
ws_close(pwsc);
|
||||
|
@ -254,13 +288,20 @@ void *ws_mainthread(void *arg) {
|
|||
* allocated memory has been freed
|
||||
*/
|
||||
void ws_close(WS_CONNINFO *pwsc) {
|
||||
DPRINTF(ERR_DEBUG,"Thread %d: Terminating\n",pwsc->threadno);
|
||||
DPRINTF(ERR_DEBUG,"Thread %d: Freeing request headers\n",pwsc->threadno);
|
||||
ws_freearglist(&pwsc->request_headers);
|
||||
DPRINTF(ERR_DEBUG,"Thread %d: Freeing response headers\n",pwsc->threadno);
|
||||
ws_freearglist(&pwsc->response_headers);
|
||||
DPRINTF(ERR_DEBUG,"Thread %d: Freeing request vars\n",pwsc->threadno);
|
||||
ws_freearglist(&pwsc->request_vars);
|
||||
|
||||
close(pwsc->fd);
|
||||
free(pwsc->hostname);
|
||||
free(pwsc);
|
||||
if((pwsc->close)||(pwsc->error)) {
|
||||
DPRINTF(ERR_DEBUG,"Thread %d: Closing fd\n",pwsc->threadno);
|
||||
close(pwsc->fd);
|
||||
free(pwsc->hostname);
|
||||
free(pwsc);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -280,6 +321,68 @@ void ws_freearglist(ARGLIST *root) {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
void ws_emitheaders(WS_CONNINFO *pwsc) {
|
||||
ARGLIST *pcurrent=pwsc->response_headers.next;
|
||||
|
||||
while(pcurrent) {
|
||||
DPRINTF(ERR_DEBUG,"Emitting reponse header %s: %s\n",pcurrent->key,
|
||||
pcurrent->value);
|
||||
ws_writefd(pwsc,"%s: %s\r\n",pcurrent->key,pcurrent->value);
|
||||
pcurrent=pcurrent->next;
|
||||
}
|
||||
|
||||
ws_writefd(pwsc,"\r\n");
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* ws_getpostvars
|
||||
*
|
||||
* Receive and parse headers. These will trump
|
||||
* get headers
|
||||
*/
|
||||
int ws_getpostvars(WS_CONNINFO *pwsc) {
|
||||
char *first, *last;
|
||||
char *content_length;
|
||||
int length;
|
||||
char *buffer;
|
||||
|
||||
content_length = ws_getarg(&pwsc->request_headers,"Content-Length");
|
||||
if(!content_length) {
|
||||
pwsc->error = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
length=atoi(content_length);
|
||||
DPRINTF(ERR_DEBUG,"Thread %d: Post var length: %d\n",
|
||||
pwsc->threadno,length);
|
||||
|
||||
buffer=(char*)malloc(length+1);
|
||||
|
||||
if(!buffer) {
|
||||
pwsc->error = errno;
|
||||
DPRINTF(ERR_INFO,"Thread %d: Could not malloc %d bytes\n",
|
||||
pwsc->threadno, length);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if((readtimed(pwsc->fd, buffer, length, 30.0)) == -1) {
|
||||
DPRINTF(ERR_INFO,"Thread %d: Timeout reading post vars\n",
|
||||
pwsc->threadno);
|
||||
pwsc->error=errno;
|
||||
return -1;
|
||||
}
|
||||
|
||||
DPRINTF(ERR_DEBUG,"Thread %d: Read post vars: %s\n",pwsc->threadno,buffer);
|
||||
|
||||
pwsc->error=ws_getgetvars(pwsc,buffer);
|
||||
|
||||
free(buffer);
|
||||
return pwsc->error;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* ws_getheaders
|
||||
*
|
||||
|
@ -295,11 +398,12 @@ int ws_getheaders(WS_CONNINFO *pwsc) {
|
|||
done=0;
|
||||
while(!done) {
|
||||
if(readline(pwsc->fd,buffer,sizeof(buffer)) == -1) {
|
||||
DPRINTF(ERR_INFO,"Unexpected close\n");
|
||||
pwsc->error=errno;
|
||||
DPRINTF(ERR_INFO,"Thread %d: Unexpected close\n",pwsc->threadno);
|
||||
return -1;
|
||||
}
|
||||
|
||||
DPRINTF(ERR_DEBUG,"Read: %s",buffer);
|
||||
DPRINTF(ERR_DEBUG,"Thread %d: Read: %s",pwsc->threadno,buffer);
|
||||
|
||||
first=buffer;
|
||||
if(buffer[0] == '\r')
|
||||
|
@ -310,7 +414,7 @@ int ws_getheaders(WS_CONNINFO *pwsc) {
|
|||
first[strlen(first)-1] = '\0';
|
||||
|
||||
if(strlen(first) == 0) {
|
||||
DPRINTF(ERR_DEBUG,"Headers parsed!\n");
|
||||
DPRINTF(ERR_DEBUG,"Thread %d: Headers parsed!\n",pwsc->threadno);
|
||||
done=1;
|
||||
} else {
|
||||
/* we have a header! */
|
||||
|
@ -318,14 +422,22 @@ int ws_getheaders(WS_CONNINFO *pwsc) {
|
|||
strsep(&last,":");
|
||||
|
||||
if(last==first) {
|
||||
DPRINTF(ERR_WARN,"Invalid header: %s\n",first);
|
||||
DPRINTF(ERR_WARN,"Thread %d: Invalid header: %s\n",
|
||||
pwsc->threadno,first);
|
||||
} else {
|
||||
while(*last==' ')
|
||||
last++;
|
||||
|
||||
DPRINTF(ERR_DEBUG,"Adding header %s=%s\n",first,last);
|
||||
while(last[strlen(last)-1] == '\r')
|
||||
last[strlen(last)-1] = '\0';
|
||||
|
||||
DPRINTF(ERR_DEBUG,"Thread %d: Adding header *%s=%s*\n",
|
||||
pwsc->threadno,first,last);
|
||||
|
||||
if(ws_addarg(&pwsc->request_headers,first,last)) {
|
||||
DPRINTF(ERR_FATAL,"Out of memory\n");
|
||||
DPRINTF(ERR_FATAL,"Thread %d: Out of memory\n",
|
||||
pwsc->threadno);
|
||||
pwsc->error=ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
@ -335,6 +447,55 @@ int ws_getheaders(WS_CONNINFO *pwsc) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* ws_getgetvars
|
||||
*
|
||||
* parse a GET string of variables (or POST)
|
||||
*/
|
||||
int ws_getgetvars(WS_CONNINFO *pwsc, char *string) {
|
||||
char *new_string;
|
||||
char *first, *last, *middle;
|
||||
int done;
|
||||
|
||||
DPRINTF(ERR_DEBUG,"Thread %d: Original string: %s\n",
|
||||
pwsc->threadno,string);
|
||||
|
||||
new_string=ws_urldecode(string);
|
||||
|
||||
DPRINTF(ERR_DEBUG,"Thread %d: Processing GET/POSTs from %s\n",
|
||||
pwsc->threadno,string);
|
||||
|
||||
done=0;
|
||||
|
||||
first=new_string;
|
||||
|
||||
while((!done) && (first)) {
|
||||
last=middle=first;
|
||||
strsep(&last,"&");
|
||||
strsep(&middle,"=");
|
||||
|
||||
if(!middle) {
|
||||
DPRINTF(ERR_WARN,"Thread %d: Bad arg: %s\n",
|
||||
pwsc->threadno,first);
|
||||
} else {
|
||||
DPRINTF(ERR_DEBUG,"Thread %d: Adding arg %s = %s\n",
|
||||
pwsc->threadno,first,middle);
|
||||
ws_addarg(&pwsc->request_vars,first,middle);
|
||||
}
|
||||
|
||||
if(!last) {
|
||||
DPRINTF(ERR_DEBUG,"Thread %d: Done parsing GET/POST args!\n",
|
||||
pwsc->threadno);
|
||||
done=1;
|
||||
} else {
|
||||
first=last;
|
||||
}
|
||||
}
|
||||
|
||||
free(new_string);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* ws_dispatcher
|
||||
|
@ -349,103 +510,161 @@ void *ws_dispatcher(void *arg) {
|
|||
WS_CONNINFO *pwsc=pwsd->pwsc;
|
||||
char buffer[MAX_LINEBUFFER];
|
||||
char *buffp;
|
||||
char *last,*first,*middle;
|
||||
char *first;
|
||||
char **argvp;
|
||||
int tokens;
|
||||
int done;
|
||||
int connection_done=0;
|
||||
int can_dispatch;
|
||||
char *auth, *username, *password;
|
||||
void (*req_handler)(WS_CONNINFO*);
|
||||
int(*auth_handler)(char *, char *);
|
||||
|
||||
free(pwsd);
|
||||
|
||||
DPRINTF(ERR_DEBUG,"Connection from %s\n",pwsc->hostname);
|
||||
|
||||
/* Now, get the request from the other end
|
||||
* and decide where to dispatch it
|
||||
*/
|
||||
|
||||
if(readline(pwsc->fd,buffer,sizeof(buffer)) == -1) {
|
||||
DPRINTF(ERR_WARN,"Could not read from client: %s\n",strerror(errno));
|
||||
ws_close(pwsc);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
tokens=ws_makeargv(buffer," ",&argvp);
|
||||
if(tokens != 3) {
|
||||
free(argvp[0]);
|
||||
ws_returnerror(pwsc,400,"Bad request");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(!strcasecmp(argvp[0],"get")) {
|
||||
pwsc->request_type = RT_GET;
|
||||
} else if(!strcasecmp(argvp[0],"post")) {
|
||||
pwsc->request_type = RT_POST;
|
||||
} else {
|
||||
/* return a 501 not implemented */
|
||||
free(argvp[0]);
|
||||
ws_returnerror(pwsc,501,"Not implemented");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Get headers */
|
||||
if(ws_getheaders(pwsc)) {
|
||||
DPRINTF(ERR_FATAL,"Could not parse headers - aborting\n");
|
||||
ws_close(pwsc);
|
||||
return NULL;
|
||||
}
|
||||
DPRINTF(ERR_DEBUG,"Thread %d: Connection from %s\n",pwsc->threadno,
|
||||
pwsc->hostname);
|
||||
|
||||
while(!connection_done) {
|
||||
/* Now, get the request from the other end
|
||||
* and decide where to dispatch it
|
||||
*/
|
||||
|
||||
pwsc->uri=strdup(argvp[1]);
|
||||
free(argvp[0]);
|
||||
if((readlinetimed(pwsc->fd,buffer,sizeof(buffer),30.0)) < 1) {
|
||||
pwsc->error=errno;
|
||||
pwsc->close=1;
|
||||
DPRINTF(ERR_WARN,"Thread %d: could not read: %s\n",
|
||||
pwsc->threadno,strerror(errno));
|
||||
ws_close(pwsc);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(!pwsc->uri) {
|
||||
/* We have memory allocation errors... might just
|
||||
* as well bail */
|
||||
DPRINTF(ERR_FATAL,"Error allocation URI\n");
|
||||
ws_returnerror(pwsc,500,"Internal server error");
|
||||
return NULL;
|
||||
}
|
||||
DPRINTF(ERR_DEBUG,"Thread %d: got request\n",pwsc->threadno);
|
||||
|
||||
/* fix the URI by un urlencoding it */
|
||||
tokens=ws_makeargv(buffer," ",&argvp);
|
||||
if(tokens != 3) {
|
||||
pwsc->error=EINVAL;
|
||||
free(argvp[0]);
|
||||
ws_returnerror(pwsc,400,"Bad request");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(!strcasecmp(argvp[0],"get")) {
|
||||
pwsc->request_type = RT_GET;
|
||||
} else if(!strcasecmp(argvp[0],"post")) {
|
||||
pwsc->request_type = RT_POST;
|
||||
} else {
|
||||
/* return a 501 not implemented */
|
||||
pwsc->error=EINVAL;
|
||||
free(argvp[0]);
|
||||
ws_returnerror(pwsc,501,"Not implemented");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Get headers */
|
||||
if(ws_getheaders(pwsc)) {
|
||||
/* error already set */
|
||||
DPRINTF(ERR_FATAL,"Thread %d: Couldn't parse headers - aborting\n",
|
||||
pwsc->threadno);
|
||||
ws_close(pwsc);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
DPRINTF(ERR_DEBUG,"Original URI: %s\n",pwsc->uri);
|
||||
|
||||
first=ws_urldecode(pwsc->uri);
|
||||
free(pwsc->uri);
|
||||
pwsc->uri=first;
|
||||
/* Now that we have the headers, we can
|
||||
* decide whether or not this is a persistant
|
||||
* connection */
|
||||
|
||||
DPRINTF(ERR_DEBUG,"Translated URI: %s\n",pwsc->uri);
|
||||
pwsc->close=!ws_testarg(&pwsc->request_headers,"connection",
|
||||
"keep-alive");
|
||||
|
||||
/* now, trim the URI */
|
||||
first=last=pwsc->uri;
|
||||
strsep(&first,"?");
|
||||
done=0;
|
||||
if(first != last) {
|
||||
while((!done) && (first)) {
|
||||
/* we've got GET args */
|
||||
last=middle=first;
|
||||
strsep(&last,"&");
|
||||
strsep(&middle,"=");
|
||||
ws_addarg(&pwsc->response_headers,"Connection",
|
||||
pwsc->close ? "close" : "keep-alive");
|
||||
|
||||
ws_addarg(&pwsc->response_headers,"Server",
|
||||
"mt-daapd/%s",VERSION);
|
||||
|
||||
if(!middle) {
|
||||
DPRINTF(ERR_WARN,"Bad arg: %s\n",first);
|
||||
} else {
|
||||
DPRINTF(ERR_DEBUG,"Adding arg %s = %s\n",first,middle);
|
||||
ws_addarg(&pwsc->request_vars,first,middle);
|
||||
}
|
||||
ws_addarg(&pwsc->response_headers,"Content-Type","text/html");
|
||||
ws_addarg(&pwsc->response_headers,"Content-Language","en_us");
|
||||
|
||||
if(!last) {
|
||||
DPRINTF(ERR_DEBUG,"Done parsing GET args!\n");
|
||||
done=1;
|
||||
} else {
|
||||
first=last;
|
||||
pwsc->uri=strdup(argvp[1]);
|
||||
free(argvp[0]);
|
||||
|
||||
if(!pwsc->uri) {
|
||||
/* We have memory allocation errors... might just
|
||||
* as well bail */
|
||||
pwsc->error=ENOMEM;
|
||||
DPRINTF(ERR_FATAL,"Thread %d: Error allocation URI\n",
|
||||
pwsc->threadno);
|
||||
ws_returnerror(pwsc,500,"Internal server error");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* trim the URI */
|
||||
first=pwsc->uri;
|
||||
strsep(&first,"?");
|
||||
|
||||
if(first) { /* got some GET args */
|
||||
DPRINTF(ERR_DEBUG,"Thread %d: parsing GET args\n",pwsc->threadno);
|
||||
ws_getgetvars(pwsc,first);
|
||||
}
|
||||
|
||||
/* fix the URI by un urldecoding it */
|
||||
|
||||
DPRINTF(ERR_DEBUG,"Thread %d: Original URI: %s\n",
|
||||
pwsc->threadno,pwsc->uri);
|
||||
|
||||
first=ws_urldecode(pwsc->uri);
|
||||
free(pwsc->uri);
|
||||
pwsc->uri=first;
|
||||
|
||||
DPRINTF(ERR_DEBUG,"Thread %d: Translated URI: %s\n",pwsc->threadno,
|
||||
pwsc->uri);
|
||||
|
||||
/* now, parse POST args */
|
||||
if(pwsc->request_type == RT_POST)
|
||||
ws_getpostvars(pwsc);
|
||||
|
||||
/* Find the appropriate handler and dispatch it */
|
||||
if(ws_findhandler(pwsp,pwsc,&req_handler,&auth_handler) == -1) {
|
||||
DPRINTF(ERR_DEBUG,"Thread %d: Using default handler.\n",
|
||||
pwsc->threadno);
|
||||
ws_defaulthandler(pwsp,pwsc);
|
||||
} else {
|
||||
DPRINTF(ERR_DEBUG,"Thread %d: Using non-default handler\n",
|
||||
pwsc->threadno);
|
||||
|
||||
can_dispatch=0;
|
||||
if(auth_handler) {
|
||||
/* do the auth thing */
|
||||
auth=ws_getarg(&pwsc->request_headers,"Authorization");
|
||||
if(auth) {
|
||||
ws_decodepassword(auth,&username,&password);
|
||||
if(auth_handler(username,password))
|
||||
can_dispatch=1;
|
||||
free(username);
|
||||
}
|
||||
|
||||
if(!can_dispatch) { /* auth failed, or need auth */
|
||||
ws_addarg(&pwsc->response_headers,"Connection","close");
|
||||
ws_addarg(&pwsc->response_headers,"WWW-Authenticate",
|
||||
"Basic");
|
||||
pwsc->close=1;
|
||||
ws_returnerror(pwsc,401,"Unauthorized");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(req_handler)
|
||||
req_handler(pwsc);
|
||||
else
|
||||
ws_defaulthandler(pwsp,pwsc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* now, parse POST args */
|
||||
|
||||
/* Find the appropriate handler and dispatch it */
|
||||
ws_defaulthandler(pwsp, pwsc);
|
||||
if((pwsc->close)||(pwsc->error))
|
||||
connection_done=1;
|
||||
ws_close(pwsc);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -455,11 +674,13 @@ void *ws_dispatcher(void *arg) {
|
|||
*
|
||||
* Write a printf-style output to a connfd
|
||||
*/
|
||||
int ws_writefd(WS_CONNINFO *pwsc,char *buffer, int len, char *fmt, ...) {
|
||||
int ws_writefd(WS_CONNINFO *pwsc, char *fmt, ...) {
|
||||
char buffer[1024];
|
||||
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
vsnprintf(buffer, len, fmt, ap);
|
||||
vsnprintf(buffer, 1024, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
return r_write(pwsc->fd,buffer,strlen(buffer));
|
||||
|
@ -476,16 +697,21 @@ int ws_writefd(WS_CONNINFO *pwsc,char *buffer, int len, char *fmt, ...) {
|
|||
* just close the connection with prejudice.
|
||||
*/
|
||||
int ws_returnerror(WS_CONNINFO *pwsc,int error, char *description) {
|
||||
char buffer[MAX_LINEBUFFER]; /* so we don't have to allocate */
|
||||
DPRINTF(ERR_WARN,"Thread %d: Pushing a %d: %s\n",
|
||||
pwsc->threadno,error,description);
|
||||
ws_writefd(pwsc,"HTTP/1.1 %d %s\r\n",error,description);
|
||||
|
||||
DPRINTF(ERR_WARN,"Pushing a %d: %s\n",error,description);
|
||||
ws_writefd(pwsc,buffer,MAX_LINEBUFFER,"HTTP/1.1 %d %s",error,description);
|
||||
ws_writefd(pwsc,buffer,MAX_LINEBUFFER,"\n\r\n\r<HTML>\n\r<TITLE>");
|
||||
ws_writefd(pwsc,buffer,MAX_LINEBUFFER,"ERROR %d</TITLE>\n\r<BODY>",error);
|
||||
ws_writefd(pwsc,buffer,MAX_LINEBUFFER,"\n\r<H1>%d Error</H1>\n\r",error);
|
||||
ws_writefd(pwsc,buffer,MAX_LINEBUFFER,"%s\n\r<hr>\n\r",description);
|
||||
ws_writefd(pwsc,buffer,MAX_LINEBUFFER,"mt-daapd: %s\n\r",VERSION);
|
||||
ws_writefd(pwsc,buffer,MAX_LINEBUFFER,"</BODY>\n\r</HTML>\n\r");
|
||||
ws_emitheaders(pwsc);
|
||||
|
||||
ws_writefd(pwsc,"<HTML>\r\n<TITLE>");
|
||||
ws_writefd(pwsc,"%d %s</TITLE>\r\n<BODY>",error,description);
|
||||
ws_writefd(pwsc,"\r\n<H1>%s</H1>\r\n",description);
|
||||
ws_writefd(pwsc,"Error %d\r\n<hr>\r\n",error);
|
||||
ws_writefd(pwsc,"<i>mt-daapd: %s\r\n<br>",VERSION);
|
||||
if(errno)
|
||||
ws_writefd(pwsc,"Error: %s\r\n",strerror(errno));
|
||||
|
||||
ws_writefd(pwsc,"</i></BODY>\r\n</HTML>\r\n");
|
||||
|
||||
ws_close(pwsc);
|
||||
return 0;
|
||||
|
@ -564,40 +790,106 @@ void ws_defaulthandler(WS_PRIVATE *pwsp, WS_CONNINFO *pwsc) {
|
|||
char path[MAXPATHLEN];
|
||||
char resolved_path[MAXPATHLEN];
|
||||
int file_fd;
|
||||
off_t len;
|
||||
|
||||
snprintf(path,MAXPATHLEN,"%s/%s",pwsp->wsconfig.web_root,pwsc->uri);
|
||||
if(!realpath(path,resolved_path)) {
|
||||
pwsc->error=errno;
|
||||
DPRINTF(ERR_WARN,"Cannot resolve %s\n",path);
|
||||
ws_returnerror(pwsc,404,"Not found");
|
||||
return;
|
||||
}
|
||||
|
||||
DPRINTF(ERR_DEBUG,"Preparing to serve %s\n",resolved_path);
|
||||
DPRINTF(ERR_DEBUG,"Thread %d: Preparing to serve %s\n",
|
||||
pwsc->threadno, resolved_path);
|
||||
|
||||
if(strncmp(resolved_path,pwsp->wsconfig.web_root,
|
||||
strlen(pwsp->wsconfig.web_root))) {
|
||||
DPRINTF(ERR_WARN,"Requested file %s out of root\n",resolved_path);
|
||||
pwsc->error=EINVAL;
|
||||
DPRINTF(ERR_WARN,"Thread %d: Requested file %s out of root\n",
|
||||
pwsc->threadno,resolved_path);
|
||||
ws_returnerror(pwsc,403,"Forbidden");
|
||||
return;
|
||||
}
|
||||
|
||||
file_fd=open(resolved_path,O_RDONLY);
|
||||
if(file_fd == -1) {
|
||||
DPRINTF(ERR_WARN,"Error opening %s: %s\n",resolved_path,strerror(errno));
|
||||
pwsc->error=errno;
|
||||
DPRINTF(ERR_WARN,"Thread %d: Error opening %s: %s\n",
|
||||
pwsc->threadno,resolved_path,strerror(errno));
|
||||
ws_returnerror(pwsc,404,"Not found");
|
||||
return;
|
||||
}
|
||||
|
||||
/* set the Content-Length response header */
|
||||
len=lseek(file_fd,0,SEEK_END);
|
||||
|
||||
ws_writefd(pwsc,path,MAXPATHLEN,"HTTP/1.1 200 OK\n\r\n\r");
|
||||
/* FIXME: assumes off_t == long */
|
||||
if(len != -1) {
|
||||
/* we have a real length */
|
||||
DPRINTF(ERR_DEBUG,"Length of file is %ld\n",(long)len);
|
||||
ws_addarg(&pwsc->response_headers,"Content-Length","%ld",(long)len);
|
||||
lseek(file_fd,0,SEEK_SET);
|
||||
}
|
||||
|
||||
|
||||
ws_writefd(pwsc,"HTTP/1.1 200 OK\r\n");
|
||||
ws_emitheaders(pwsc);
|
||||
|
||||
/* now throw out the file */
|
||||
copyfile(file_fd,pwsc->fd);
|
||||
|
||||
close(file_fd);
|
||||
DPRINTF(ERR_DEBUG,"Served successfully\n");
|
||||
ws_close(pwsc);
|
||||
DPRINTF(ERR_DEBUG,"Thread %d: Served successfully\n",pwsc->threadno);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* ws_testarg
|
||||
*
|
||||
* Check an arg for a particular value
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* pwsc->close=ws_queryarg(&pwsc->request_headers,"Connection","close");
|
||||
*
|
||||
*/
|
||||
int ws_testarg(ARGLIST *root, char *key, char *value) {
|
||||
DPRINTF(ERR_DEBUG,"Checking to see if %s matches %s\n",key,value);
|
||||
|
||||
char *retval=ws_getarg(root,key);
|
||||
if(!retval)
|
||||
return 0;
|
||||
|
||||
return !strcasecmp(value,retval);
|
||||
}
|
||||
|
||||
/*
|
||||
* ws_getarg
|
||||
*
|
||||
* Find an argument in an argument list
|
||||
*
|
||||
* returns a pointer to the value if successful,
|
||||
* NULL otherwise.
|
||||
*
|
||||
* This should be passed the pointer to the
|
||||
* stub in the pwsc.
|
||||
*
|
||||
* Ex: ws_getarg(pwsc->request_headers,"Connection");
|
||||
*/
|
||||
char *ws_getarg(ARGLIST *root, char *key) {
|
||||
ARGLIST *pcurrent=root->next;
|
||||
|
||||
while((pcurrent)&&(strcasecmp(pcurrent->key,key)))
|
||||
pcurrent=pcurrent->next;
|
||||
|
||||
if(pcurrent)
|
||||
return pcurrent->value;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* ws_addarg
|
||||
*
|
||||
|
@ -612,10 +904,17 @@ void ws_defaulthandler(WS_PRIVATE *pwsp, WS_CONNINFO *pwsc) {
|
|||
* -1 on failure, with errno set (ENOMEM)
|
||||
* 0 on success
|
||||
*/
|
||||
int ws_addarg(ARGLIST *root, char *key, char *value) {
|
||||
int ws_addarg(ARGLIST *root, char *key, char *fmt, ...) {
|
||||
char *newkey;
|
||||
char *newvalue;
|
||||
ARGLIST *pnew;
|
||||
ARGLIST *current;
|
||||
va_list ap;
|
||||
char value[MAX_LINEBUFFER];
|
||||
|
||||
va_start(ap,fmt);
|
||||
vsnprintf(value,sizeof(value),fmt,ap);
|
||||
va_end(ap);
|
||||
|
||||
newkey=strdup(key);
|
||||
newvalue=strdup(value);
|
||||
|
@ -626,8 +925,31 @@ int ws_addarg(ARGLIST *root, char *key, char *value) {
|
|||
|
||||
pnew->key=newkey;
|
||||
pnew->value=newvalue;
|
||||
|
||||
/* first, see if the key exists... if it does, simply
|
||||
* replace it rather than adding a duplicate key
|
||||
*/
|
||||
|
||||
current=root->next;
|
||||
while(current) {
|
||||
if(!strcmp(current->key,key)) {
|
||||
/* got a match! */
|
||||
DPRINTF(ERR_DEBUG,"Updating %s from %s to %s\n",
|
||||
key,current->value,value);
|
||||
free(current->value);
|
||||
current->value = newvalue;
|
||||
free(newkey);
|
||||
free(pnew);
|
||||
return 0;
|
||||
}
|
||||
current=current->next;
|
||||
}
|
||||
|
||||
|
||||
pnew->next=root->next;
|
||||
DPRINTF(ERR_DEBUG,"Added *%s=%s*\n",newkey,newvalue);
|
||||
root->next=pnew;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -688,3 +1010,207 @@ char *ws_urldecode(char *string) {
|
|||
*dst='\0';
|
||||
return pnew;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* ws_registerhandler
|
||||
*
|
||||
* Register a page and auth handler. Returns 0 on success,
|
||||
* returns -1 on failure.
|
||||
*
|
||||
* If the regex is not well-formed, it returns -1 iwth
|
||||
* errno set to EINVAL. It is up to the caller to use
|
||||
* regerror to display a more interesting error message,
|
||||
* if appropriate.
|
||||
*/
|
||||
int ws_registerhandler(WSHANDLE ws, char *regex,
|
||||
void(*handler)(WS_CONNINFO*),
|
||||
int(*auth)(char *, char *)) {
|
||||
WS_HANDLER *phandler;
|
||||
WS_PRIVATE *pwsp = (WS_PRIVATE *)ws;
|
||||
|
||||
phandler=(WS_HANDLER *)malloc(sizeof(WS_HANDLER));
|
||||
if(!phandler)
|
||||
return -1;
|
||||
|
||||
if(regcomp(&phandler->regex,regex,REG_EXTENDED | REG_NOSUB)) {
|
||||
free(phandler);
|
||||
errno=EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
phandler->req_handler=handler;
|
||||
phandler->auth_handler=auth;
|
||||
|
||||
ws_lock_unsafe();
|
||||
phandler->next=pwsp->handlers.next;
|
||||
pwsp->handlers.next=phandler;
|
||||
ws_unlock_unsafe();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* ws_findhandler
|
||||
*
|
||||
* Given a URI, determine the appropriate handler.
|
||||
*
|
||||
* If a handler is found, it returns 0, otherwise, returns
|
||||
* -1
|
||||
*/
|
||||
int ws_findhandler(WS_PRIVATE *pwsp, WS_CONNINFO *pwsc,
|
||||
void(**preq)(WS_CONNINFO*),
|
||||
int(**pauth)(char *, char *)) {
|
||||
WS_HANDLER *phandler=pwsp->handlers.next;
|
||||
|
||||
ws_lock_unsafe();
|
||||
|
||||
DPRINTF(ERR_DEBUG,"Thread %d: Preparing to find handler\n",
|
||||
pwsc->threadno);
|
||||
|
||||
while(phandler) {
|
||||
if(!regexec(&phandler->regex,pwsc->uri,0,NULL,0)) {
|
||||
/* that's a match */
|
||||
DPRINTF(ERR_DEBUG,"Thread %d: URI Match!\n",pwsc->threadno);
|
||||
*preq=phandler->req_handler;
|
||||
*pauth=phandler->auth_handler;
|
||||
ws_unlock_unsafe();
|
||||
return 0;
|
||||
}
|
||||
phandler=phandler->next;
|
||||
}
|
||||
|
||||
ws_unlock_unsafe();
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* ws_decodepassword
|
||||
*
|
||||
* Given a base64 encoded Authentication request header,
|
||||
* decode it into the username and password
|
||||
*/
|
||||
int ws_decodepassword(char *header, char **username, char **password) {
|
||||
static char ws_xlat[256];
|
||||
static int ws_xlat_init=0;
|
||||
int index;
|
||||
int len;
|
||||
int rack=0;
|
||||
int pads=0;
|
||||
unsigned char *decodebuffer;
|
||||
unsigned char *pin, *pout;
|
||||
int lookup;
|
||||
|
||||
*username=NULL;
|
||||
*password=NULL;
|
||||
|
||||
if(ws_lock_unsafe() == -1)
|
||||
return -1;
|
||||
|
||||
if(!ws_xlat_init) {
|
||||
ws_xlat_init=1;
|
||||
|
||||
memset((char*)&ws_xlat,0xFF,sizeof(ws_xlat));
|
||||
for(index=0; index < 26; index++) {
|
||||
ws_xlat['A' + index] = index;
|
||||
ws_xlat['a' + index] = index + 26;
|
||||
}
|
||||
|
||||
for(index=0; index < 10; index++) {
|
||||
ws_xlat['0' + index] = index + 52;
|
||||
}
|
||||
|
||||
ws_xlat['+'] = 62;
|
||||
ws_xlat['/'] = 63;
|
||||
}
|
||||
if(ws_unlock_unsafe() == -1)
|
||||
return -1;
|
||||
|
||||
/* xlat table is initialized */
|
||||
while(*header != ' ')
|
||||
header++;
|
||||
|
||||
header++;
|
||||
|
||||
decodebuffer=(unsigned char *)malloc(strlen(header));
|
||||
if(!decodebuffer)
|
||||
return;
|
||||
|
||||
DPRINTF(ERR_DEBUG,"Preparing to decode %s\n",header);
|
||||
|
||||
memset(decodebuffer,0,strlen(header));
|
||||
len=0;
|
||||
pout=decodebuffer;
|
||||
pin=header;
|
||||
|
||||
/* this is more than a little sloppy */
|
||||
while(pin[rack]) {
|
||||
if(pin[rack] != '=') {
|
||||
lookup=ws_xlat[pin[rack]];
|
||||
if(lookup == 0xFF) {
|
||||
DPRINTF(ERR_WARN,"Got garbage Authenticate header\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* valid character */
|
||||
switch(rack) {
|
||||
case 0:
|
||||
pout[0]=(lookup << 2);
|
||||
break;
|
||||
case 1:
|
||||
pout[0] |= (lookup >> 4);
|
||||
pout[1] = (lookup << 4);
|
||||
break;
|
||||
case 2:
|
||||
pout[1] |= (lookup >> 2);
|
||||
pout[2] = (lookup << 6);
|
||||
break;
|
||||
case 3:
|
||||
pout[2] |= lookup;
|
||||
break;
|
||||
}
|
||||
rack++;
|
||||
} else {
|
||||
/* padding char */
|
||||
pads++;
|
||||
rack++;
|
||||
}
|
||||
|
||||
if(rack == 4) {
|
||||
pin += 4;
|
||||
pout += 3;
|
||||
|
||||
len += (3-pads);
|
||||
rack=0;
|
||||
}
|
||||
}
|
||||
|
||||
/* we now have the decoded string */
|
||||
DPRINTF(ERR_DEBUG,"Decoded %s\n",decodebuffer);
|
||||
|
||||
*username = decodebuffer;
|
||||
*password = *username;
|
||||
|
||||
strsep(password,":");
|
||||
|
||||
DPRINTF(ERR_DEBUG,"Decoded user=%s, pw=%s\n",*username,*password);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* ws_addreponseheader
|
||||
*
|
||||
* Simple wrapper around the CONNINFO response headers
|
||||
*/
|
||||
int ws_addresponseheader(WS_CONNINFO *pwsc, char *header, char *val) {
|
||||
return ws_addarg(&pwsc->response_headers,header,val);
|
||||
}
|
||||
|
||||
/*
|
||||
* ws_getvar
|
||||
*
|
||||
* Simple wrapper around the CONNINFO request vars
|
||||
*/
|
||||
char *ws_getvar(WS_CONNINFO *pwsc, char *var) {
|
||||
return ws_getarg(&pwsc->request_vars,var);
|
||||
}
|
||||
|
|
|
@ -22,7 +22,6 @@
|
|||
#ifndef _WEBSERVER_H_
|
||||
#define _WEBSERVER_H_
|
||||
|
||||
|
||||
/*
|
||||
* Defines
|
||||
*/
|
||||
|
@ -47,10 +46,13 @@ typedef struct tag_arglist {
|
|||
} ARGLIST;
|
||||
|
||||
typedef struct tag_ws_conninfo {
|
||||
int threadno;
|
||||
int error;
|
||||
int fd;
|
||||
int request_type;
|
||||
char *uri;
|
||||
char *hostname;
|
||||
int close;
|
||||
ARGLIST request_headers;
|
||||
ARGLIST response_headers;
|
||||
ARGLIST request_vars;
|
||||
|
@ -62,9 +64,19 @@ typedef void* WSHANDLE;
|
|||
* Externs
|
||||
*/
|
||||
|
||||
#define WS_REQ_HANDLER void (*)(WS_CONNINFO *)
|
||||
#define WS_AUTH_HANDLER int (*)(char *, char *)
|
||||
|
||||
extern WSHANDLE ws_start(WSCONFIG *config);
|
||||
extern int ws_stop(WSHANDLE ws);
|
||||
extern int ws_registerhandler(WSHANDLE ws, char *regex,
|
||||
void(*handler)(WS_CONNINFO*),
|
||||
int(*auth)(char *, char *));
|
||||
|
||||
/* for handlers */
|
||||
extern void ws_close(WS_CONNINFO *pwsc);
|
||||
extern int ws_returnerror(WS_CONNINFO *pwsc, int error, char *description);
|
||||
|
||||
extern int ws_addresponseheader(WS_CONNINFO *pwsc, char *header, char *val);
|
||||
extern int ws_writefd(WS_CONNINFO *pwsc, char *fmt, ...);
|
||||
extern char *ws_getvar(WS_CONNINFO *pwsc, char *var);
|
||||
#endif /* _WEBSERVER_H_ */
|
||||
|
|
Loading…
Reference in New Issue