Initial revision
This commit is contained in:
parent
ebcb8c471b
commit
36818cbeb5
|
@ -0,0 +1,4 @@
|
|||
# $Id$
|
||||
#
|
||||
EXTRA_DIST = reconf configure
|
||||
SUBDIRS=src
|
|
@ -0,0 +1,26 @@
|
|||
dnl
|
||||
dnl Much of this is autoscan generated. Some of it I don't really know
|
||||
dnl what to do with.
|
||||
dnl
|
||||
|
||||
dnl Process this file with autoconf to produce a configure script.
|
||||
AC_INIT(src/netview.c)
|
||||
dnl AC_CANONICAL_SYSTEM
|
||||
|
||||
AM_CONFIG_HEADER(config.h)
|
||||
AM_INIT_AUTOMAKE(netview,0.1.0)
|
||||
|
||||
dnl Checks for programs.
|
||||
AC_PROG_CC
|
||||
|
||||
dnl Checks for libraries.
|
||||
|
||||
dnl Checks for header files.
|
||||
AC_HEADER_STDC
|
||||
|
||||
dnl Checks for typedefs, structures, and compiler characteristics.
|
||||
AC_C_CONST
|
||||
AC_TYPE_MODE_T
|
||||
|
||||
dnl Checks for library functions.
|
||||
AC_OUTPUT(src/Makefile Makefile)
|
|
@ -0,0 +1,8 @@
|
|||
#!/bin/bash
|
||||
rm -f config.cache
|
||||
rm -f config.status
|
||||
#rm -f acconfig.h
|
||||
aclocal
|
||||
autoconf
|
||||
autoheader
|
||||
automake -a
|
|
@ -0,0 +1,14 @@
|
|||
PROJECT=mt-daapd
|
||||
SOURCES=main.c configfile.c err.c webserver.c restart.c uici.c
|
||||
OBJECTS=$(SOURCES:.c=.o)
|
||||
CFLAGS=-g -no-cpp-precomp -DDEBUG
|
||||
|
||||
all: $(PROJECT)
|
||||
$(CC) $(OBJECTS) -o $(PROJECT)
|
||||
|
||||
$(PROJECT): $(OBJECTS)
|
||||
|
||||
clean:
|
||||
rm -f $(PROJECT) *\.o *~ \#*
|
||||
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* $Id$
|
||||
* Functions for reading and writing the config file
|
||||
*
|
||||
* Copyright (C) 2003 Ron Pedde (ron@corbey.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "configfile.h"
|
||||
|
||||
/*
|
||||
* config_read
|
||||
*
|
||||
* Read the specified config file.
|
||||
*
|
||||
* This function returns 0 on success, errorcode on failure
|
||||
*/
|
||||
int config_read(char *file);
|
||||
|
||||
/*
|
||||
* config_write
|
||||
*
|
||||
*/
|
||||
int config_write(void);
|
||||
|
||||
/*
|
||||
* config_change
|
||||
*
|
||||
*/
|
||||
int config_change(void);
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* $Id$
|
||||
* Functions for reading and writing the config file
|
||||
*
|
||||
* Copyright (C) 2003 Ron Pedde (ron@corbey.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _CONFIGFILE_H_
|
||||
#define _CONFIGFILE_H_
|
||||
|
||||
extern int config_read(char *file);
|
||||
extern int config_write(void);
|
||||
extern int config_change(void);
|
||||
|
||||
#endif /* _CONFIGFILE_H_ */
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* $Id$
|
||||
* Header info for daapd server
|
||||
*
|
||||
* Copyright (C) 2003 Ron Pedde (ron@corbey.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _DAAPD_H_
|
||||
#define _DAAPD_H_
|
||||
|
||||
typedef struct tag_songentry {
|
||||
int index;
|
||||
char *file;
|
||||
|
||||
struct tag_songentry *next;
|
||||
} SONGENTRY;
|
||||
|
||||
typedef struct tag_config {
|
||||
char *configfile;
|
||||
int port;
|
||||
char *adminpassword;
|
||||
char *readpassword;
|
||||
char *mp3dir;
|
||||
|
||||
SONGENTRY songlist;
|
||||
} CONFIG;
|
||||
|
||||
|
||||
|
||||
#endif /* _DAAPD_H_ */
|
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* $Id$
|
||||
* Generic error handling
|
||||
*
|
||||
* Copyright (C) 2003 Ron Pedde (ron@corbey.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <errno.h>
|
||||
#include <syslog.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "err.h"
|
||||
|
||||
|
||||
int err_debuglevel=0;
|
||||
int err_logdestination=LOGDEST_STDERR;
|
||||
|
||||
/****************************************************
|
||||
* log_err
|
||||
****************************************************/
|
||||
void log_err(int quit, char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
char errbuf[256];
|
||||
|
||||
va_start(ap, fmt);
|
||||
vsnprintf(errbuf, sizeof(errbuf), fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
switch(err_logdestination) {
|
||||
case LOGDEST_STDERR:
|
||||
fprintf(stderr, errbuf);
|
||||
break;
|
||||
case LOGDEST_SYSLOG:
|
||||
syslog(LOG_INFO, errbuf);
|
||||
break;
|
||||
}
|
||||
|
||||
if(quit) {
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************
|
||||
* log_setdest
|
||||
****************************************************/
|
||||
void log_setdest(char *app, int destination) {
|
||||
switch(destination) {
|
||||
case LOGDEST_SYSLOG:
|
||||
if(err_logdestination != LOGDEST_SYSLOG) {
|
||||
openlog(app,LOG_PID,LOG_DAEMON);
|
||||
}
|
||||
break;
|
||||
case LOGDEST_STDERR:
|
||||
if(err_logdestination == LOGDEST_SYSLOG) {
|
||||
/* close the syslog */
|
||||
closelog();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* $Id$
|
||||
* Error related routines
|
||||
*
|
||||
* Copyright (C) 2003 Ron Pedde (ron@corbey.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef __ERR_H__
|
||||
#define __ERR_H__
|
||||
|
||||
#define LOGDEST_STDERR 0
|
||||
#define LOGDEST_SYSLOG 1
|
||||
|
||||
#define ERR_DEBUG 9
|
||||
#define ERR_INFO 5
|
||||
#define ERR_WARN 2
|
||||
#define ERR_FATAL 0
|
||||
|
||||
extern int err_debuglevel;
|
||||
extern int err_logdestination;
|
||||
|
||||
extern void log_err(int quit, char *fmt, ...);
|
||||
extern void log_setdest(char *app, int destination);
|
||||
|
||||
#ifdef DEBUG
|
||||
#define DPRINTF(level, fmt, arg...) \
|
||||
{ if((level) <= err_debuglevel) { log_err(0,"%s: ",__FILE__); log_err(0,fmt,##arg); }}
|
||||
#else
|
||||
#define DPRINTF(level, fmt, arg...)
|
||||
#endif /* DEBUG */
|
||||
|
||||
#endif /* __ERR_H__ */
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* $Id$
|
||||
* Driver for multi-threaded daap server
|
||||
*
|
||||
* Copyright (C) 2003 Ron Pedde (ron@corbey.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include "err.h"
|
||||
#include "webserver.h"
|
||||
|
||||
// 3689
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
int status;
|
||||
char *confdir=NULL;
|
||||
WSCONFIG ws_config;
|
||||
WSHANDLE server;
|
||||
|
||||
printf("mt-daapd: version $Revision$\n\n");
|
||||
|
||||
ws_config.web_root="/Users/ron/Documents/rfc";
|
||||
ws_config.port=80;
|
||||
|
||||
err_debuglevel=9;
|
||||
server=ws_start(&ws_config);
|
||||
if(!server) {
|
||||
perror("ws_start");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
while(1) {
|
||||
sleep(20);
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
int makeargv(const char *s, const char *delimiters, char ***argvp) {
|
||||
int i;
|
||||
int numtokens;
|
||||
const char *snew;
|
||||
char *t;
|
||||
|
||||
if ((s == NULL) || (delimiters == NULL) || (argvp == NULL))
|
||||
return -1;
|
||||
*argvp = NULL;
|
||||
snew = s + strspn(s, delimiters); /* snew is real start of string */
|
||||
if ((t = malloc(strlen(snew) + 1)) == NULL)
|
||||
return -1;
|
||||
|
||||
/* count the number of tokens in s */
|
||||
strcpy(t, snew);
|
||||
numtokens = 0;
|
||||
if (strtok(t, delimiters) != NULL)
|
||||
for (numtokens = 1; strtok(NULL, delimiters) != NULL; numtokens++) ;
|
||||
|
||||
/* create argument array for ptrs to the tokens */
|
||||
if ((*argvp = malloc((numtokens + 1)*sizeof(char *))) == NULL) {
|
||||
free(t);
|
||||
return -1;
|
||||
}
|
||||
/* insert pointers to tokens into the argument array */
|
||||
if (numtokens == 0)
|
||||
free(t);
|
||||
else {
|
||||
strcpy(t, snew);
|
||||
**argvp = strtok(t, delimiters);
|
||||
for (i = 1; i < numtokens; i++)
|
||||
*((*argvp) + i) = strtok(NULL, delimiters);
|
||||
}
|
||||
/* put in the final NULL pointer and return */
|
||||
*((*argvp) + numtokens) = NULL;
|
||||
return numtokens;
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
web_root /Users/ron/Documents/rfc
|
||||
port 80
|
|
@ -0,0 +1,232 @@
|
|||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <string.h>
|
||||
#include <sys/select.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/wait.h>
|
||||
#include "restart.h"
|
||||
#define BLKSIZE PIPE_BUF
|
||||
#define MILLION 1000000L
|
||||
#define D_MILLION 1000000.0
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/* Restart versions of traditional functions */
|
||||
|
||||
int r_close(int fildes) {
|
||||
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 r_open2(const char *path, int oflag) {
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
pid_t r_wait(int *stat_loc) {
|
||||
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) &&
|
||||
(errno == EINTR)) ;
|
||||
return retval;
|
||||
}
|
||||
|
||||
ssize_t r_write(int fd, void *buf, size_t size) {
|
||||
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;
|
||||
}
|
||||
|
||||
/* Utility functions */
|
||||
|
||||
struct timeval add2currenttime(double seconds) {
|
||||
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;
|
||||
}
|
||||
|
||||
int copyfile(int fromfd, int tofd) {
|
||||
int bytesread;
|
||||
int totalbytes = 0;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
ssize_t readtimed(int fd, void *buf, size_t nbyte, double seconds) {
|
||||
struct timeval timedone;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifndef ETIME
|
||||
#define ETIME ETIMEDOUT
|
||||
#endif
|
||||
|
||||
struct timeval add2currenttime(double seconds);
|
||||
int copyfile(int fromfd, int tofd);
|
||||
int r_close(int fildes);
|
||||
int r_dup2(int fildes, int fildes2);
|
||||
int r_open2(const char *path, int oflag);
|
||||
int r_open3(const char *path, int oflag, mode_t mode);
|
||||
ssize_t r_read(int fd, void *buf, size_t size);
|
||||
pid_t r_wait(int *stat_loc);
|
||||
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);
|
||||
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);
|
||||
int waitfdtimed(int fd, struct timeval end);
|
|
@ -0,0 +1,176 @@
|
|||
/* uici.c sockets implementation */
|
||||
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <netinet/in.h>
|
||||
#include <sys/select.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include "uici.h"
|
||||
|
||||
#ifndef MAXBACKLOG
|
||||
#define MAXBACKLOG 50
|
||||
#endif
|
||||
|
||||
/*
|
||||
* u_igniore_sigpipe
|
||||
* Ignore SIGPIPE if the default action is in effect.
|
||||
*
|
||||
* returns: 0 if successful
|
||||
* -1 on error and sets errno
|
||||
*/
|
||||
static int u_ignore_sigpipe() {
|
||||
struct sigaction act;
|
||||
|
||||
if (sigaction(SIGPIPE, (struct sigaction *)NULL, &act) == -1)
|
||||
return -1;
|
||||
if (act.sa_handler == SIG_DFL) {
|
||||
act.sa_handler = SIG_IGN;
|
||||
if (sigaction(SIGPIPE, &act, (struct sigaction *)NULL) == -1)
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* u_open
|
||||
* Return a file descriptor, which is bound to the given port.
|
||||
*
|
||||
* parameter:
|
||||
* s = number of port to bind to
|
||||
* returns: file descriptor if successful
|
||||
* -1 on error and sets errno
|
||||
*/
|
||||
int u_open(u_port_t port) {
|
||||
int error;
|
||||
struct sockaddr_in server;
|
||||
int sock;
|
||||
int true = 1;
|
||||
|
||||
if ((u_ignore_sigpipe() == -1) ||
|
||||
((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1))
|
||||
return -1;
|
||||
|
||||
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&true,
|
||||
sizeof(true)) == -1) {
|
||||
error = errno;
|
||||
while ((close(sock) == -1) && (errno == EINTR));
|
||||
errno = error;
|
||||
return -1;
|
||||
}
|
||||
|
||||
server.sin_family = AF_INET;
|
||||
server.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
server.sin_port = htons((short)port);
|
||||
if ((bind(sock, (struct sockaddr *)&server, sizeof(server)) == -1) ||
|
||||
(listen(sock, MAXBACKLOG) == -1)) {
|
||||
error = errno;
|
||||
while ((close(sock) == -1) && (errno == EINTR));
|
||||
errno = error;
|
||||
return -1;
|
||||
}
|
||||
return sock;
|
||||
}
|
||||
|
||||
/*
|
||||
* u_accept
|
||||
* Wait for a connection request from a host on a specified port.
|
||||
*
|
||||
* parameters:
|
||||
* fd = file descriptor previously bound to listening port
|
||||
* hostn = a buffer that will hold the name of the remote host
|
||||
* hostnsize = size of hostn buffer
|
||||
* returns: a communication file descriptor on success
|
||||
* hostn is filled with the name of the remote host.
|
||||
* -1 on error with errno set
|
||||
*
|
||||
* comments: This function is used by the server to wait for a
|
||||
* communication. It blocks until a remote request is received
|
||||
* from the port bound to the given file descriptor.
|
||||
* hostn is filled with an ASCII string containing the remote
|
||||
* host name. It must point to a buffer of size at least hostnsize.
|
||||
* If the name does not fit, as much of the name as is possible is put
|
||||
* into the buffer.
|
||||
* If hostn is NULL or hostnsize <= 0, no hostname is copied.
|
||||
*/
|
||||
int u_accept(int fd, char *hostn, int hostnsize) {
|
||||
int len = sizeof(struct sockaddr);
|
||||
struct sockaddr_in netclient;
|
||||
int retval;
|
||||
|
||||
while (((retval =
|
||||
accept(fd, (struct sockaddr *)(&netclient), &len)) == -1) &&
|
||||
(errno == EINTR))
|
||||
;
|
||||
if ((retval == -1) || (hostn == NULL) || (hostnsize <= 0))
|
||||
return retval;
|
||||
|
||||
strncpy(hostn,inet_ntoa(netclient.sin_addr),hostnsize);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*
|
||||
* u_connect
|
||||
* Initiate communication with a remote server.
|
||||
*
|
||||
* parameters:
|
||||
* port = well-known port on remote server
|
||||
* hostn = character string giving the Internet name of remote host
|
||||
* returns: a communication file descriptor if successful
|
||||
* -1 on error with errno set
|
||||
*/
|
||||
int u_connect(u_port_t port, char *hostn) {
|
||||
int error;
|
||||
int retval;
|
||||
struct sockaddr_in server;
|
||||
int sock;
|
||||
fd_set sockset;
|
||||
struct hostent *phe;
|
||||
|
||||
|
||||
/* fix broken name resolution for hosts beginning with a
|
||||
* digit, plus get rid of hostname lookup stuff
|
||||
*/
|
||||
if(inet_addr(hostn) == INADDR_NONE) {
|
||||
phe=gethostbyname(hostn);
|
||||
if(phe == NULL) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy((char*)&server.sin_addr,(char*)(phe->h_addr_list[0]),
|
||||
sizeof(struct in_addr));
|
||||
} else {
|
||||
server.sin_addr.s_addr=inet_addr(hostn);
|
||||
}
|
||||
|
||||
server.sin_port = htons((short)port);
|
||||
server.sin_family = AF_INET;
|
||||
|
||||
if ((u_ignore_sigpipe() == -1) ||
|
||||
((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1))
|
||||
return -1;
|
||||
|
||||
if (((retval =
|
||||
connect(sock, (struct sockaddr *)&server, sizeof(server))) == -1) &&
|
||||
((errno == EINTR) || (errno == EALREADY))) {
|
||||
FD_ZERO(&sockset);
|
||||
FD_SET(sock, &sockset);
|
||||
while ( ((retval = select(sock+1, NULL, &sockset, NULL, NULL)) == -1) &&
|
||||
(errno == EINTR) ) {
|
||||
FD_ZERO(&sockset);
|
||||
FD_SET(sock, &sockset);
|
||||
}
|
||||
}
|
||||
if (retval == -1) {
|
||||
error = errno;
|
||||
while ((close(sock) == -1) && (errno == EINTR));
|
||||
errno = error;
|
||||
return -1;
|
||||
}
|
||||
return sock;
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
/*********************************** uici.h **************************/
|
||||
/* Prototypes for the three public UICI functions */
|
||||
/*********************************************************************/
|
||||
#define UPORT
|
||||
typedef unsigned short u_port_t;
|
||||
int u_open(u_port_t port);
|
||||
int u_accept(int fd, char *hostn, int hostnsize);
|
||||
int u_connect(u_port_t port, char *hostn);
|
|
@ -0,0 +1,690 @@
|
|||
/*
|
||||
* $Id$
|
||||
* Webserver library
|
||||
*
|
||||
* Copyright (C) 2003 Ron Pedde (ron@corbey.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include <sys/param.h>
|
||||
|
||||
#include "err.h"
|
||||
#include "webserver.h"
|
||||
|
||||
/*
|
||||
* Defines
|
||||
*/
|
||||
|
||||
#define MAX_HOSTNAME 256
|
||||
#define MAX_LINEBUFFER 256
|
||||
|
||||
#ifndef VERSION
|
||||
# define VERSION "Version 0.1 beta"
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Local (private) typedefs
|
||||
*/
|
||||
typedef struct tag_ws_private {
|
||||
WSCONFIG wsconfig;
|
||||
int server_fd;
|
||||
int stop;
|
||||
int running;
|
||||
pthread_t server_tid;
|
||||
} WS_PRIVATE;
|
||||
|
||||
typedef struct tag_ws_dispatchinfo {
|
||||
WS_PRIVATE *pwsp;
|
||||
WS_CONNINFO *pwsc;
|
||||
} WS_DISPATCHINFO;
|
||||
|
||||
/*
|
||||
* Forwards
|
||||
*/
|
||||
void *ws_mainthread(void*);
|
||||
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);
|
||||
void ws_freearglist(ARGLIST *root);
|
||||
char *ws_urldecode(char *string);
|
||||
int ws_getheaders(WS_CONNINFO *pwsc);
|
||||
|
||||
/*
|
||||
* Globals
|
||||
*/
|
||||
pthread_mutex_t munsafe=PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
/*
|
||||
* ws_lock_unsafe
|
||||
*
|
||||
* Lock non-thread-safe functions
|
||||
*
|
||||
* returns 0 on success,
|
||||
* returns -1 on failure, with errno set
|
||||
*/
|
||||
int ws_lock_unsafe(void) {
|
||||
int err;
|
||||
|
||||
if(err=pthread_mutex_lock(&munsafe)) {
|
||||
errno=err;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* ws_unlock_unsafe
|
||||
*
|
||||
* Lock non-thread-safe functions
|
||||
*
|
||||
* returns 0 on success,
|
||||
* returns -1 on failure, with errno set
|
||||
*/
|
||||
int ws_unlock_unsafe(void) {
|
||||
int err;
|
||||
|
||||
if(err=pthread_mutex_unlock(&munsafe)) {
|
||||
errno=err;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* ws_start
|
||||
*
|
||||
* Start the main webserver thread. Should really
|
||||
* bind and listen to the port before spawning the thread,
|
||||
* since it will be hard to detect and return the error unless
|
||||
* we listen first
|
||||
*
|
||||
* RETURNS
|
||||
* Success: WSHANDLE
|
||||
* Failure: NULL, with errno set
|
||||
*
|
||||
*/
|
||||
WSHANDLE ws_start(WSCONFIG *config) {
|
||||
int err;
|
||||
|
||||
WS_PRIVATE *pwsp;
|
||||
|
||||
if((pwsp=(WS_PRIVATE*)malloc(sizeof(WS_PRIVATE))) == NULL)
|
||||
return NULL;
|
||||
|
||||
memcpy(&pwsp->wsconfig,config,sizeof(WS_PRIVATE));
|
||||
pwsp->running=0;
|
||||
|
||||
DPRINTF(ERR_INFO,"Preparing to listen on port %d\n",pwsp->wsconfig.port);
|
||||
|
||||
if((pwsp->server_fd = u_open(pwsp->wsconfig.port)) == -1) {
|
||||
err=errno;
|
||||
DPRINTF(ERR_WARN,"Could not open port: %s\n",strerror(errno));
|
||||
errno=err;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
DPRINTF(ERR_INFO,"Starting server thread\n");
|
||||
if(err=pthread_create(&pwsp->server_tid,NULL,ws_mainthread,(void*)pwsp)) {
|
||||
DPRINTF(ERR_WARN,"Could not spawn thread: %s\n",strerror(err));
|
||||
close(pwsp->server_fd);
|
||||
errno=err;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* we're really running */
|
||||
pwsp->running=1;
|
||||
|
||||
return (WSHANDLE)pwsp;
|
||||
}
|
||||
|
||||
/*
|
||||
* ws_stop
|
||||
*
|
||||
* Stop the web server and all the child threads
|
||||
*/
|
||||
int ws_stop(WSHANDLE arg) {
|
||||
WS_PRIVATE *pwsp = (WS_PRIVATE*)arg;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* ws_mainthread
|
||||
*
|
||||
* Main thread for webserver - this accepts connections
|
||||
* and spawns a handler thread for each incoming connection.
|
||||
*
|
||||
* For a persistant connection, these threads will be
|
||||
* long-lived, otherwise, they will terminate as soon as
|
||||
* the request has been honored.
|
||||
*
|
||||
* These client threads will, of course, be detached
|
||||
*/
|
||||
void *ws_mainthread(void *arg) {
|
||||
int fd;
|
||||
int err;
|
||||
WS_PRIVATE *pwsp = (WS_PRIVATE*)arg;
|
||||
WS_DISPATCHINFO *pwsd;
|
||||
WS_CONNINFO *pwsc;
|
||||
pthread_t tid;
|
||||
char hostname[MAX_HOSTNAME];
|
||||
|
||||
while(1) {
|
||||
pwsc=(WS_CONNINFO*)malloc(sizeof(WS_CONNINFO));
|
||||
if(!pwsc) {
|
||||
/* can't very well service any more threads! */
|
||||
DPRINTF(ERR_FATAL,"Error: %s\n",strerror(errno));
|
||||
pwsp->running=0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(pwsc,0,sizeof(WS_CONNINFO));
|
||||
|
||||
if((fd=u_accept(pwsp->server_fd,hostname,MAX_HOSTNAME)) == -1) {
|
||||
close(pwsp->server_fd);
|
||||
pwsp->running=0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pwsc->hostname=strdup(hostname);
|
||||
pwsc->fd=fd;
|
||||
|
||||
/* Spawn off a dispatcher to decide what to do with
|
||||
* the request
|
||||
*/
|
||||
pwsd=(WS_DISPATCHINFO*)malloc(sizeof(WS_DISPATCHINFO));
|
||||
if(!pwsd) {
|
||||
/* keep on trucking until we crash bigger */
|
||||
DPRINTF(ERR_FATAL,"Error: %s\n",strerror(errno));
|
||||
free(pwsd);
|
||||
ws_close(pwsc);
|
||||
} else {
|
||||
pwsd->pwsp=pwsp;
|
||||
pwsd->pwsc=pwsc;
|
||||
|
||||
/* now, throw off a dispatch thread */
|
||||
if(err=pthread_create(&tid,NULL,ws_dispatcher,(void*)pwsd)) {
|
||||
DPRINTF(ERR_WARN,"Could not spawn thread: %s\n",strerror(err));
|
||||
free(pwsd);
|
||||
ws_close(pwsc);
|
||||
}
|
||||
|
||||
pthread_detach(tid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* ws_close
|
||||
*
|
||||
* Close the connection. This might be called when things
|
||||
* are already in bad shape, so we'll ignore errors and let
|
||||
* them be detected back in either the dispatch
|
||||
* thread or the main server thread
|
||||
*
|
||||
* Mainly, we just want to make sure that any
|
||||
* allocated memory has been freed
|
||||
*/
|
||||
void ws_close(WS_CONNINFO *pwsc) {
|
||||
ws_freearglist(&pwsc->request_headers);
|
||||
ws_freearglist(&pwsc->response_headers);
|
||||
ws_freearglist(&pwsc->request_vars);
|
||||
|
||||
close(pwsc->fd);
|
||||
free(pwsc->hostname);
|
||||
free(pwsc);
|
||||
}
|
||||
|
||||
/*
|
||||
* ws_freearglist
|
||||
*
|
||||
* Walks through an arg list freeing as it goes
|
||||
*/
|
||||
void ws_freearglist(ARGLIST *root) {
|
||||
ARGLIST *current;
|
||||
|
||||
while(root->next) {
|
||||
free(root->next->key);
|
||||
free(root->next->value);
|
||||
current=root->next;
|
||||
root->next=current->next;
|
||||
free(current);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* ws_getheaders
|
||||
*
|
||||
* Receive and parse headers. This is called from
|
||||
* ws_dispatcher
|
||||
*/
|
||||
int ws_getheaders(WS_CONNINFO *pwsc) {
|
||||
char *first, *last;
|
||||
int done;
|
||||
char buffer[MAX_LINEBUFFER];
|
||||
|
||||
/* Break down the headers into some kind of header list */
|
||||
done=0;
|
||||
while(!done) {
|
||||
if(readline(pwsc->fd,buffer,sizeof(buffer)) == -1) {
|
||||
DPRINTF(ERR_INFO,"Unexpected close\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
DPRINTF(ERR_DEBUG,"Read: %s",buffer);
|
||||
|
||||
first=buffer;
|
||||
if(buffer[0] == '\r')
|
||||
first=&buffer[1];
|
||||
|
||||
/* trim the trailing \n */
|
||||
if(first[strlen(first)-1] == '\n')
|
||||
first[strlen(first)-1] = '\0';
|
||||
|
||||
if(strlen(first) == 0) {
|
||||
DPRINTF(ERR_DEBUG,"Headers parsed!\n");
|
||||
done=1;
|
||||
} else {
|
||||
/* we have a header! */
|
||||
last=first;
|
||||
strsep(&last,":");
|
||||
|
||||
if(last==first) {
|
||||
DPRINTF(ERR_WARN,"Invalid header: %s\n",first);
|
||||
} else {
|
||||
while(*last==' ')
|
||||
last++;
|
||||
|
||||
DPRINTF(ERR_DEBUG,"Adding header %s=%s\n",first,last);
|
||||
if(ws_addarg(&pwsc->request_headers,first,last)) {
|
||||
DPRINTF(ERR_FATAL,"Out of memory\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* ws_dispatcher
|
||||
*
|
||||
* Main dispatch thread. This gets the request, reads the
|
||||
* headers, decodes the GET'd or POST'd variables,
|
||||
* then decides what function should service the request
|
||||
*/
|
||||
void *ws_dispatcher(void *arg) {
|
||||
WS_DISPATCHINFO *pwsd=(WS_DISPATCHINFO *)arg;
|
||||
WS_PRIVATE *pwsp=pwsd->pwsp;
|
||||
WS_CONNINFO *pwsc=pwsd->pwsc;
|
||||
char buffer[MAX_LINEBUFFER];
|
||||
char *buffp;
|
||||
char *last,*first,*middle;
|
||||
char **argvp;
|
||||
int tokens;
|
||||
int done;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
pwsc->uri=strdup(argvp[1]);
|
||||
free(argvp[0]);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/* fix the URI by un urlencoding it */
|
||||
|
||||
DPRINTF(ERR_DEBUG,"Original URI: %s\n",pwsc->uri);
|
||||
|
||||
first=ws_urldecode(pwsc->uri);
|
||||
free(pwsc->uri);
|
||||
pwsc->uri=first;
|
||||
|
||||
DPRINTF(ERR_DEBUG,"Translated URI: %s\n",pwsc->uri);
|
||||
|
||||
/* 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,"=");
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
if(!last) {
|
||||
DPRINTF(ERR_DEBUG,"Done parsing GET args!\n");
|
||||
done=1;
|
||||
} else {
|
||||
first=last;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* now, parse POST args */
|
||||
|
||||
/* Find the appropriate handler and dispatch it */
|
||||
ws_defaulthandler(pwsp, pwsc);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* ws_writefd
|
||||
*
|
||||
* Write a printf-style output to a connfd
|
||||
*/
|
||||
int ws_writefd(WS_CONNINFO *pwsc,char *buffer, int len, char *fmt, ...) {
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
vsnprintf(buffer, len, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
return r_write(pwsc->fd,buffer,strlen(buffer));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* ws_returnerror
|
||||
*
|
||||
* return a particular error code to the requesting
|
||||
* agent
|
||||
*
|
||||
* This will always succeed. If it cannot, it will
|
||||
* 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,"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_close(pwsc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* ws_makeargv
|
||||
*
|
||||
* Turn a string into an argv-like array of char *.
|
||||
* the array must be destroyed by freeing the first
|
||||
* element.
|
||||
*
|
||||
* This code was stolen from Dr. Robbins.
|
||||
* (srobbins@cs.utsa.edu). Any errors in it
|
||||
* were introduced by me, though.
|
||||
*/
|
||||
int ws_makeargv(const char *s, const char *delimiters, char ***argvp) {
|
||||
int i;
|
||||
int numtokens;
|
||||
const char *snew;
|
||||
char *t;
|
||||
int err;
|
||||
|
||||
/* this whole function is sickeningly unsafe */
|
||||
DPRINTF(ERR_DEBUG,"Parsing input: %s",s);
|
||||
|
||||
if(ws_lock_unsafe())
|
||||
return -1;
|
||||
|
||||
if ((s == NULL) || (delimiters == NULL) || (argvp == NULL)) {
|
||||
ws_unlock_unsafe();
|
||||
return -1;
|
||||
}
|
||||
*argvp = NULL;
|
||||
snew = s + strspn(s, delimiters); /* snew is real start of string */
|
||||
if ((t = malloc(strlen(snew) + 1)) == NULL) {
|
||||
ws_unlock_unsafe();
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* count the number of tokens in s */
|
||||
strcpy(t, snew);
|
||||
numtokens = 0;
|
||||
if (strtok(t, delimiters) != NULL)
|
||||
for (numtokens = 1; strtok(NULL, delimiters) != NULL; numtokens++) ;
|
||||
|
||||
/* create argument array for ptrs to the tokens */
|
||||
if ((*argvp = malloc((numtokens + 1)*sizeof(char *))) == NULL) {
|
||||
free(t);
|
||||
ws_unlock_unsafe();
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* insert pointers to tokens into the argument array */
|
||||
if (numtokens == 0) {
|
||||
free(t);
|
||||
} else {
|
||||
strcpy(t, snew);
|
||||
**argvp = strtok(t, delimiters);
|
||||
for (i = 1; i < numtokens; i++)
|
||||
*((*argvp) + i) = strtok(NULL, delimiters);
|
||||
}
|
||||
|
||||
/* put in the final NULL pointer and return */
|
||||
*((*argvp) + numtokens) = NULL;
|
||||
return ws_unlock_unsafe() ? -1 : numtokens;
|
||||
}
|
||||
|
||||
/*
|
||||
* ws_defaulthandler
|
||||
*
|
||||
* default URI handler. This simply finds the file
|
||||
* and serves it up
|
||||
*/
|
||||
void ws_defaulthandler(WS_PRIVATE *pwsp, WS_CONNINFO *pwsc) {
|
||||
char path[MAXPATHLEN];
|
||||
char resolved_path[MAXPATHLEN];
|
||||
int file_fd;
|
||||
|
||||
snprintf(path,MAXPATHLEN,"%s/%s",pwsp->wsconfig.web_root,pwsc->uri);
|
||||
if(!realpath(path,resolved_path)) {
|
||||
ws_returnerror(pwsc,404,"Not found");
|
||||
return;
|
||||
}
|
||||
|
||||
DPRINTF(ERR_DEBUG,"Preparing to serve %s\n",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);
|
||||
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));
|
||||
ws_returnerror(pwsc,404,"Not found");
|
||||
return;
|
||||
}
|
||||
|
||||
ws_writefd(pwsc,path,MAXPATHLEN,"HTTP/1.1 200 OK\n\r\n\r");
|
||||
|
||||
/* now throw out the file */
|
||||
copyfile(file_fd,pwsc->fd);
|
||||
|
||||
close(file_fd);
|
||||
DPRINTF(ERR_DEBUG,"Served successfully\n");
|
||||
ws_close(pwsc);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* ws_addarg
|
||||
*
|
||||
* Add an argument to an arg list
|
||||
*
|
||||
* This will strdup the passed key and value.
|
||||
* The arglist will then have to be freed. This should
|
||||
* be done in ws_close, and handler functions will
|
||||
* have to remember to ws_close them.
|
||||
*
|
||||
* RETURNS
|
||||
* -1 on failure, with errno set (ENOMEM)
|
||||
* 0 on success
|
||||
*/
|
||||
int ws_addarg(ARGLIST *root, char *key, char *value) {
|
||||
char *newkey;
|
||||
char *newvalue;
|
||||
ARGLIST *pnew;
|
||||
|
||||
newkey=strdup(key);
|
||||
newvalue=strdup(value);
|
||||
pnew=(ARGLIST*)malloc(sizeof(ARGLIST));
|
||||
|
||||
if((!pnew)||(!newkey)||(!newvalue))
|
||||
return -1;
|
||||
|
||||
pnew->key=newkey;
|
||||
pnew->value=newvalue;
|
||||
pnew->next=root->next;
|
||||
root->next=pnew;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* ws_urldecode
|
||||
*
|
||||
* decode a urlencoded string
|
||||
*
|
||||
* the returned char will be malloced -- it must be
|
||||
* freed by the caller
|
||||
*
|
||||
* returns NULL on error (ENOMEM)
|
||||
*/
|
||||
char *ws_urldecode(char *string) {
|
||||
char *pnew;
|
||||
char *src,*dst;
|
||||
int val;
|
||||
|
||||
pnew=(char*)malloc(strlen(string));
|
||||
if(!pnew)
|
||||
return NULL;
|
||||
|
||||
src=string;
|
||||
dst=pnew;
|
||||
|
||||
while(*src) {
|
||||
switch(*src) {
|
||||
case '+':
|
||||
*dst++=' ';
|
||||
src++;
|
||||
break;
|
||||
case '%':
|
||||
/* this is hideous */
|
||||
src++;
|
||||
if(*src) {
|
||||
if((*src <= '9') && (*src >='0'))
|
||||
val=(*src - '0');
|
||||
else if((tolower(*src) <= 'f')&&(tolower(*src) >= 'a'))
|
||||
val=10+(tolower(*src) - 'a');
|
||||
src++;
|
||||
}
|
||||
if(*src) {
|
||||
val *= 16;
|
||||
if((*src <= '9') && (*src >='0'))
|
||||
val+=(*src - '0');
|
||||
else if((tolower(*src) <= 'f')&&(tolower(*src) >= 'a'))
|
||||
val+=(10+(tolower(*src) - 'a'));
|
||||
src++;
|
||||
}
|
||||
*dst++=val;
|
||||
break;
|
||||
default:
|
||||
*dst++=*src++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
*dst='\0';
|
||||
return pnew;
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* $Id$
|
||||
* Webserver library
|
||||
*
|
||||
* Copyright (C) 2003 Ron Pedde (ron@corbey.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _WEBSERVER_H_
|
||||
#define _WEBSERVER_H_
|
||||
|
||||
|
||||
/*
|
||||
* Defines
|
||||
*/
|
||||
|
||||
#define RT_GET 0
|
||||
#define RT_POST 1
|
||||
|
||||
/*
|
||||
* Typedefs
|
||||
*/
|
||||
|
||||
typedef struct tag_wsconfig {
|
||||
char *web_root;
|
||||
char *id;
|
||||
unsigned short port;
|
||||
} WSCONFIG;
|
||||
|
||||
typedef struct tag_arglist {
|
||||
char *key;
|
||||
char *value;
|
||||
struct tag_arglist *next;
|
||||
} ARGLIST;
|
||||
|
||||
typedef struct tag_ws_conninfo {
|
||||
int fd;
|
||||
int request_type;
|
||||
char *uri;
|
||||
char *hostname;
|
||||
ARGLIST request_headers;
|
||||
ARGLIST response_headers;
|
||||
ARGLIST request_vars;
|
||||
} WS_CONNINFO;
|
||||
|
||||
typedef void* WSHANDLE;
|
||||
|
||||
/*
|
||||
* Externs
|
||||
*/
|
||||
|
||||
extern WSHANDLE ws_start(WSCONFIG *config);
|
||||
extern int ws_stop(WSHANDLE ws);
|
||||
extern void ws_close(WS_CONNINFO *pwsc);
|
||||
extern int ws_returnerror(WS_CONNINFO *pwsc, int error, char *description);
|
||||
|
||||
#endif /* _WEBSERVER_H_ */
|
Loading…
Reference in New Issue