2006-12-21 03:57:49 +00:00
|
|
|
/*******************************************************************************
|
|
|
|
**
|
|
|
|
** server.c
|
|
|
|
**
|
|
|
|
** This file is part of the ABYSS Web server project.
|
|
|
|
**
|
|
|
|
** Copyright (C) 2000 by Moez Mahfoudh <mmoez@bigfoot.com>.
|
|
|
|
** All rights reserved.
|
|
|
|
**
|
|
|
|
** Redistribution and use in source and binary forms, with or without
|
|
|
|
** modification, are permitted provided that the following conditions
|
|
|
|
** are met:
|
|
|
|
** 1. Redistributions of source code must retain the above copyright
|
|
|
|
** notice, this list of conditions and the following disclaimer.
|
|
|
|
** 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
** notice, this list of conditions and the following disclaimer in the
|
|
|
|
** documentation and/or other materials provided with the distribution.
|
|
|
|
** 3. The name of the author may not be used to endorse or promote products
|
|
|
|
** derived from this software without specific prior written permission.
|
|
|
|
**
|
|
|
|
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
|
|
** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
|
** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
|
|
** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
|
|
** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
|
|
** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
|
|
** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
|
|
** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
|
|
** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
|
|
** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
|
|
** SUCH DAMAGE.
|
|
|
|
**
|
|
|
|
*******************************************************************************/
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <time.h>
|
|
|
|
#ifdef ABYSS_WIN32
|
|
|
|
#include <io.h>
|
|
|
|
#else
|
|
|
|
/* Check this
|
|
|
|
#include <sys/io.h>
|
|
|
|
*/
|
|
|
|
#endif /* ABYSS_WIN32 */
|
|
|
|
#include <fcntl.h>
|
|
|
|
|
|
|
|
#include "mallocvar.h"
|
|
|
|
|
|
|
|
#include "xmlrpc-c/abyss.h"
|
|
|
|
|
|
|
|
#define BOUNDARY "##123456789###BOUNDARY"
|
|
|
|
|
|
|
|
typedef int (*TQSortProc)(const void *, const void *);
|
|
|
|
|
|
|
|
static int
|
|
|
|
cmpfilenames(const TFileInfo **f1,const TFileInfo **f2) {
|
|
|
|
if (((*f1)->attrib & A_SUBDIR) && !((*f2)->attrib & A_SUBDIR))
|
|
|
|
return (-1);
|
|
|
|
if (!((*f1)->attrib & A_SUBDIR) && ((*f2)->attrib & A_SUBDIR))
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
return strcmp((*f1)->name,(*f2)->name);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
cmpfiledates(const TFileInfo **f1,const TFileInfo **f2) {
|
|
|
|
if (((*f1)->attrib & A_SUBDIR) && !((*f2)->attrib & A_SUBDIR))
|
|
|
|
return (-1);
|
|
|
|
if (!((*f1)->attrib & A_SUBDIR) && ((*f2)->attrib & A_SUBDIR))
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
return ((*f1)->time_write-(*f2)->time_write);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static abyss_bool
|
|
|
|
ServerDirectoryHandler(TSession *r,char *z,TDate *dirdate) {
|
|
|
|
TFileInfo fileinfo,*fi;
|
|
|
|
TFileFind findhandle;
|
|
|
|
char *p,z1[26],z2[20],z3[9],u,*z4;
|
|
|
|
TList list;
|
|
|
|
int16_t i;
|
|
|
|
uint32_t k;
|
|
|
|
abyss_bool text=FALSE;
|
|
|
|
abyss_bool ascending=TRUE;
|
|
|
|
uint16_t sort=1; /* 1=by name, 2=by date */
|
|
|
|
struct tm ftm;
|
|
|
|
TPool pool;
|
|
|
|
TDate date;
|
|
|
|
|
|
|
|
if (r->query) {
|
|
|
|
if (strcmp(r->query,"plain")==0)
|
|
|
|
text=TRUE;
|
|
|
|
else if (strcmp(r->query,"name-up")==0)
|
|
|
|
{
|
|
|
|
sort=1;
|
|
|
|
ascending=TRUE;
|
|
|
|
}
|
|
|
|
else if (strcmp(r->query,"name-down")==0)
|
|
|
|
{
|
|
|
|
sort=1;
|
|
|
|
ascending=FALSE;
|
|
|
|
}
|
|
|
|
else if (strcmp(r->query,"date-up")==0)
|
|
|
|
{
|
|
|
|
sort=2;
|
|
|
|
ascending=TRUE;
|
|
|
|
}
|
|
|
|
else if (strcmp(r->query,"date-down")==0)
|
|
|
|
{
|
|
|
|
sort=2;
|
|
|
|
ascending=FALSE;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ResponseStatus(r,400);
|
|
|
|
return TRUE;
|
|
|
|
};
|
|
|
|
}
|
|
|
|
if (DateCompare(&r->date,dirdate)<0)
|
|
|
|
dirdate=&r->date;
|
|
|
|
|
|
|
|
p=RequestHeaderValue(r,"If-Modified-Since");
|
|
|
|
if (p) {
|
|
|
|
if (DateDecode(p,&date)) {
|
|
|
|
if (DateCompare(dirdate,&date)<=0)
|
|
|
|
{
|
|
|
|
ResponseStatus(r,304);
|
|
|
|
ResponseWrite(r);
|
|
|
|
return TRUE;
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!FileFindFirst(&findhandle,z,&fileinfo))
|
|
|
|
{
|
|
|
|
ResponseStatusErrno(r);
|
|
|
|
return TRUE;
|
|
|
|
};
|
|
|
|
|
|
|
|
ListInit(&list);
|
|
|
|
|
|
|
|
if (!PoolCreate(&pool,1024))
|
|
|
|
{
|
|
|
|
ResponseStatus(r,500);
|
|
|
|
return TRUE;
|
|
|
|
};
|
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
/* Files which names start with a dot are ignored */
|
|
|
|
/* This includes implicitly the ./ and ../ */
|
|
|
|
if (*fileinfo.name=='.') {
|
|
|
|
if (strcmp(fileinfo.name,"..")==0)
|
|
|
|
{
|
|
|
|
if (strcmp(r->uri,"/")==0)
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
fi=(TFileInfo *)PoolAlloc(&pool,sizeof(fileinfo));
|
|
|
|
if (fi)
|
|
|
|
{
|
|
|
|
memcpy(fi,&fileinfo,sizeof(fileinfo));
|
|
|
|
if (ListAdd(&list,fi))
|
|
|
|
continue;
|
|
|
|
};
|
|
|
|
|
|
|
|
ResponseStatus(r,500);
|
|
|
|
FileFindClose(&findhandle);
|
|
|
|
ListFree(&list);
|
|
|
|
PoolFree(&pool);
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
} while (FileFindNext(&findhandle,&fileinfo));
|
|
|
|
|
|
|
|
FileFindClose(&findhandle);
|
|
|
|
|
|
|
|
/* Send something to the user to show that we are still alive */
|
|
|
|
ResponseStatus(r,200);
|
|
|
|
ResponseContentType(r,(text?"text/plain":"text/html"));
|
|
|
|
|
|
|
|
if (DateToString(dirdate,z))
|
|
|
|
ResponseAddField(r,"Last-Modified",z);
|
|
|
|
|
|
|
|
ResponseChunked(r);
|
|
|
|
ResponseWrite(r);
|
|
|
|
|
|
|
|
if (r->method!=m_head)
|
|
|
|
{
|
|
|
|
if (text)
|
|
|
|
{
|
|
|
|
sprintf(z,"Index of %s" CRLF,r->uri);
|
|
|
|
i=strlen(z)-2;
|
|
|
|
p=z+i+2;
|
|
|
|
|
|
|
|
while (i>0)
|
|
|
|
{
|
|
|
|
*(p++)='-';
|
|
|
|
i--;
|
|
|
|
};
|
|
|
|
|
|
|
|
*p='\0';
|
|
|
|
strcat(z,CRLF CRLF
|
|
|
|
"Name Size "
|
|
|
|
"Date-Time Type" CRLF
|
|
|
|
"------------------------------------"
|
|
|
|
"--------------------------------------------"CRLF);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
sprintf(z,"<HTML><HEAD><TITLE>Index of %s</TITLE></HEAD><BODY>"
|
|
|
|
"<H1>Index of %s</H1><PRE>",r->uri,r->uri);
|
|
|
|
strcat(z,"Name Size "
|
|
|
|
"Date-Time Type<HR WIDTH=100%>"CRLF);
|
|
|
|
};
|
|
|
|
|
|
|
|
HTTPWrite(r,z,strlen(z));
|
|
|
|
|
|
|
|
/* Sort the files */
|
|
|
|
qsort(list.item,list.size,sizeof(void *),
|
|
|
|
(TQSortProc)(sort==1?cmpfilenames:cmpfiledates));
|
|
|
|
|
|
|
|
/* Write the listing */
|
|
|
|
if (ascending)
|
|
|
|
i=0;
|
|
|
|
else
|
|
|
|
i=list.size-1;
|
|
|
|
|
|
|
|
while ((i<list.size) && (i>=0))
|
|
|
|
{
|
|
|
|
fi=list.item[i];
|
|
|
|
|
|
|
|
if (ascending)
|
|
|
|
i++;
|
|
|
|
else
|
|
|
|
i--;
|
|
|
|
|
|
|
|
strcpy(z,fi->name);
|
|
|
|
|
|
|
|
k=strlen(z);
|
|
|
|
|
|
|
|
if (fi->attrib & A_SUBDIR)
|
|
|
|
{
|
|
|
|
z[k++]='/';
|
|
|
|
z[k]='\0';
|
|
|
|
};
|
|
|
|
|
|
|
|
if (k>24)
|
|
|
|
{
|
|
|
|
z[10]='\0';
|
|
|
|
strcpy(z1,z);
|
|
|
|
strcat(z1,"...");
|
|
|
|
strcat(z1,z+k-11);
|
|
|
|
k=24;
|
|
|
|
p=z1+24;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
strcpy(z1,z);
|
|
|
|
|
|
|
|
k++;
|
|
|
|
p=z1+k;
|
|
|
|
while (k<25)
|
|
|
|
z1[k++]=' ';
|
|
|
|
|
|
|
|
z1[25]='\0';
|
|
|
|
};
|
|
|
|
|
|
|
|
ftm=*(gmtime(&fi->time_write));
|
|
|
|
sprintf(z2,"%02u/%02u/%04u %02u:%02u:%02u",ftm.tm_mday,ftm.tm_mon,
|
|
|
|
ftm.tm_year+1900,ftm.tm_hour,ftm.tm_min,ftm.tm_sec);
|
|
|
|
|
|
|
|
if (fi->attrib & A_SUBDIR)
|
|
|
|
{
|
|
|
|
strcpy(z3," -- ");
|
|
|
|
z4="Directory";
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (fi->size<9999)
|
|
|
|
u='b';
|
|
|
|
else
|
|
|
|
{
|
|
|
|
fi->size/=1024;
|
|
|
|
if (fi->size<9999)
|
|
|
|
u='K';
|
|
|
|
else
|
|
|
|
{
|
|
|
|
fi->size/=1024;
|
|
|
|
if (fi->size<9999)
|
|
|
|
u='M';
|
|
|
|
else
|
|
|
|
u='G';
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
sprintf(z3, "%5llu %c", fi->size, u);
|
|
|
|
|
|
|
|
if (strcmp(fi->name,"..")==0)
|
|
|
|
z4="";
|
|
|
|
else
|
|
|
|
z4=MIMETypeFromFileName(fi->name);
|
|
|
|
|
|
|
|
if (!z4)
|
|
|
|
z4="Unknown";
|
|
|
|
};
|
|
|
|
|
|
|
|
if (text)
|
|
|
|
sprintf(z,"%s%s %s %s %s"CRLF,
|
|
|
|
z1,p,z3,z2,z4);
|
|
|
|
else
|
|
|
|
sprintf(z,"<A HREF=\"%s%s\">%s</A>%s %s %s %s"CRLF,
|
|
|
|
fi->name,(fi->attrib & A_SUBDIR?"/":""),z1,p,z3,z2,z4);
|
|
|
|
|
|
|
|
HTTPWrite(r,z,strlen(z));
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Write the tail of the file */
|
|
|
|
if (text)
|
|
|
|
strcpy(z,SERVER_PLAIN_INFO);
|
|
|
|
else
|
|
|
|
strcpy(z,"</PRE>" SERVER_HTML_INFO "</BODY></HTML>" CRLF CRLF);
|
|
|
|
|
|
|
|
HTTPWrite(r,z,strlen(z));
|
|
|
|
};
|
|
|
|
|
|
|
|
HTTPWriteEnd(r);
|
|
|
|
/* Free memory and exit */
|
|
|
|
ListFree(&list);
|
|
|
|
PoolFree(&pool);
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static abyss_bool
|
|
|
|
ServerFileHandler(TSession *r,char *z,TDate *filedate) {
|
|
|
|
char *mediatype;
|
|
|
|
TFile file;
|
|
|
|
uint64_t filesize,start,end;
|
|
|
|
uint16_t i;
|
|
|
|
TDate date;
|
|
|
|
char *p;
|
|
|
|
|
|
|
|
mediatype=MIMETypeGuessFromFile(z);
|
|
|
|
|
|
|
|
if (!FileOpen(&file,z,O_BINARY | O_RDONLY))
|
|
|
|
{
|
|
|
|
ResponseStatusErrno(r);
|
|
|
|
return TRUE;
|
|
|
|
};
|
|
|
|
|
|
|
|
if (DateCompare(&r->date,filedate)<0)
|
|
|
|
filedate=&r->date;
|
|
|
|
|
|
|
|
p=RequestHeaderValue(r,"if-modified-since");
|
|
|
|
if (p) {
|
|
|
|
if (DateDecode(p,&date)) {
|
|
|
|
if (DateCompare(filedate,&date)<=0) {
|
|
|
|
ResponseStatus(r,304);
|
|
|
|
ResponseWrite(r);
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
r->ranges.size=0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
filesize=FileSize(&file);
|
|
|
|
|
|
|
|
switch (r->ranges.size)
|
|
|
|
{
|
|
|
|
case 0:
|
|
|
|
ResponseStatus(r,200);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 1:
|
|
|
|
if (!RangeDecode((char *)(r->ranges.item[0]),filesize,&start,&end))
|
|
|
|
{
|
|
|
|
ListFree(&(r->ranges));
|
|
|
|
ResponseStatus(r,200);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
sprintf(z, "bytes %llu-%llu/%llu", start, end, filesize);
|
|
|
|
|
|
|
|
ResponseAddField(r,"Content-range",z);
|
|
|
|
ResponseContentLength(r,end-start+1);
|
|
|
|
ResponseStatus(r,206);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
ResponseContentType(r,"multipart/ranges; boundary=" BOUNDARY);
|
|
|
|
ResponseStatus(r,206);
|
|
|
|
break;
|
|
|
|
};
|
|
|
|
|
|
|
|
if (r->ranges.size==0)
|
|
|
|
{
|
|
|
|
ResponseContentLength(r,filesize);
|
|
|
|
ResponseContentType(r,mediatype);
|
|
|
|
};
|
|
|
|
|
|
|
|
if (DateToString(filedate,z))
|
|
|
|
ResponseAddField(r,"Last-Modified",z);
|
|
|
|
|
|
|
|
ResponseWrite(r);
|
|
|
|
|
|
|
|
if (r->method!=m_head)
|
|
|
|
{
|
|
|
|
if (r->ranges.size==0)
|
|
|
|
ConnWriteFromFile(r->conn,&file,0,filesize-1,z,4096,0);
|
|
|
|
else if (r->ranges.size==1)
|
|
|
|
ConnWriteFromFile(r->conn,&file,start,end,z,4096,0);
|
|
|
|
else
|
|
|
|
for (i=0;i<=r->ranges.size;i++)
|
|
|
|
{
|
|
|
|
ConnWrite(r->conn,"--",2);
|
|
|
|
ConnWrite(r->conn,BOUNDARY,strlen(BOUNDARY));
|
|
|
|
ConnWrite(r->conn,CRLF,2);
|
|
|
|
|
|
|
|
if (i<r->ranges.size)
|
|
|
|
if (RangeDecode((char *)(r->ranges.item[i]),
|
|
|
|
filesize,
|
|
|
|
&start,&end))
|
|
|
|
{
|
|
|
|
/* Entity header, not response header */
|
|
|
|
sprintf(z,"Content-type: %s" CRLF
|
|
|
|
"Content-range: bytes %llu-%llu/%llu" CRLF
|
|
|
|
"Content-length: %llu" CRLF
|
|
|
|
CRLF, mediatype, start, end,
|
|
|
|
filesize, end-start+1);
|
|
|
|
|
|
|
|
ConnWrite(r->conn,z,strlen(z));
|
|
|
|
|
|
|
|
ConnWriteFromFile(r->conn,&file,start,end,z,4096,0);
|
|
|
|
};
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
FileClose(&file);
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static abyss_bool
|
|
|
|
ServerDefaultHandlerFunc(TSession *r) {
|
|
|
|
char *p,z[4096];
|
|
|
|
TFileStat fs;
|
|
|
|
uint16_t i;
|
|
|
|
abyss_bool endingslash=FALSE;
|
|
|
|
TDate objdate;
|
|
|
|
|
|
|
|
if (!RequestValidURIPath(r))
|
|
|
|
{
|
|
|
|
ResponseStatus(r,400);
|
|
|
|
return TRUE;
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Must check for * (asterisk uri) in the future */
|
|
|
|
if (r->method==m_options)
|
|
|
|
{
|
|
|
|
ResponseAddField(r,"Allow","GET, HEAD");
|
|
|
|
ResponseContentLength(r,0);
|
|
|
|
ResponseStatus(r,200);
|
|
|
|
return TRUE;
|
|
|
|
};
|
|
|
|
|
|
|
|
if ((r->method!=m_get) && (r->method!=m_head))
|
|
|
|
{
|
|
|
|
ResponseAddField(r,"Allow","GET, HEAD");
|
|
|
|
ResponseStatus(r,405);
|
|
|
|
return TRUE;
|
|
|
|
};
|
|
|
|
|
|
|
|
strcpy(z,r->server->filespath);
|
|
|
|
strcat(z,r->uri);
|
|
|
|
|
|
|
|
p=z+strlen(z)-1;
|
|
|
|
if (*p=='/')
|
|
|
|
{
|
|
|
|
endingslash=TRUE;
|
|
|
|
*p='\0';
|
|
|
|
};
|
|
|
|
|
|
|
|
#ifdef ABYSS_WIN32
|
|
|
|
p=z;
|
|
|
|
while (*p)
|
|
|
|
{
|
|
|
|
if ((*p)=='/')
|
|
|
|
*p='\\';
|
|
|
|
|
|
|
|
p++;
|
|
|
|
};
|
|
|
|
#endif /* ABYSS_WIN32 */
|
|
|
|
|
|
|
|
if (!FileStat(z,&fs))
|
|
|
|
{
|
|
|
|
ResponseStatusErrno(r);
|
|
|
|
return TRUE;
|
|
|
|
};
|
|
|
|
|
|
|
|
if (fs.st_mode & S_IFDIR)
|
|
|
|
{
|
|
|
|
/* Redirect to the same directory but with the ending slash
|
|
|
|
** to avoid problems with some browsers (IE for examples) when
|
|
|
|
** they generate relative urls */
|
|
|
|
if (!endingslash)
|
|
|
|
{
|
|
|
|
strcpy(z,r->uri);
|
|
|
|
p=z+strlen(z);
|
|
|
|
*p='/';
|
|
|
|
*(p+1)='\0';
|
|
|
|
ResponseAddField(r,"Location",z);
|
|
|
|
ResponseStatus(r,302);
|
|
|
|
ResponseWrite(r);
|
|
|
|
return TRUE;
|
|
|
|
};
|
|
|
|
|
|
|
|
#ifdef ABYSS_WIN32
|
|
|
|
*p='\\';
|
|
|
|
#else
|
|
|
|
*p='/';
|
|
|
|
#endif /* ABYSS_WIN32 */
|
|
|
|
p++;
|
|
|
|
|
|
|
|
i=r->server->defaultfilenames.size;
|
|
|
|
while (i-->0) {
|
|
|
|
*p='\0';
|
|
|
|
strcat(z,(char *)(r->server->defaultfilenames.item[i]));
|
|
|
|
if (FileStat(z,&fs)) {
|
|
|
|
if (!(fs.st_mode & S_IFDIR)) {
|
|
|
|
if (DateFromLocal(&objdate,fs.st_mtime))
|
|
|
|
return ServerFileHandler(r,z,&objdate);
|
|
|
|
else
|
|
|
|
return ServerFileHandler(r,z,NULL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
*(p-1)='\0';
|
|
|
|
|
|
|
|
if (!FileStat(z,&fs))
|
|
|
|
{
|
|
|
|
ResponseStatusErrno(r);
|
|
|
|
return TRUE;
|
|
|
|
};
|
|
|
|
|
|
|
|
if (DateFromLocal(&objdate,fs.st_mtime))
|
|
|
|
return ServerDirectoryHandler(r,z,&objdate);
|
|
|
|
else
|
|
|
|
return ServerDirectoryHandler(r,z,NULL);
|
|
|
|
|
|
|
|
}
|
|
|
|
else
|
|
|
|
if (DateFromLocal(&objdate,fs.st_mtime))
|
|
|
|
return ServerFileHandler(r,z,&objdate);
|
|
|
|
else
|
|
|
|
return ServerFileHandler(r,z,NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
abyss_bool
|
|
|
|
ServerCreate(TServer * const srvP,
|
|
|
|
const char * const name,
|
|
|
|
uint16_t const port,
|
|
|
|
const char * const filespath,
|
|
|
|
const char * const logfilename) {
|
|
|
|
|
|
|
|
abyss_bool success;
|
|
|
|
|
|
|
|
if (name)
|
|
|
|
srvP->name = strdup(name);
|
|
|
|
else
|
|
|
|
srvP->name = "unnamed";
|
|
|
|
|
|
|
|
srvP->port = port;
|
|
|
|
|
|
|
|
srvP->defaulthandler = ServerDefaultHandlerFunc;
|
|
|
|
|
|
|
|
if (filespath)
|
|
|
|
srvP->filespath = strdup(filespath);
|
|
|
|
else
|
|
|
|
srvP->filespath = strdup(DEFAULT_DOCS);
|
|
|
|
|
|
|
|
srvP->keepalivetimeout = 15;
|
|
|
|
srvP->keepalivemaxconn = 30;
|
|
|
|
srvP->timeout = 15;
|
|
|
|
srvP->advertise = TRUE;
|
|
|
|
#ifdef _UNIX
|
|
|
|
srvP->pidfile = srvP->uid = srvP->gid = -1;
|
|
|
|
#endif /* _UNIX */
|
|
|
|
|
|
|
|
ListInit(&srvP->handlers);
|
|
|
|
ListInitAutoFree(&srvP->defaultfilenames);
|
|
|
|
|
|
|
|
if (logfilename)
|
|
|
|
success = LogOpen(srvP, logfilename);
|
|
|
|
else {
|
|
|
|
srvP->logfile = -1;
|
|
|
|
success = TRUE;
|
|
|
|
}
|
|
|
|
return success;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
terminateHandlers(TList * const handlersP) {
|
|
|
|
/*----------------------------------------------------------------------------
|
|
|
|
Terminate all handlers in the list '*handlersP'.
|
|
|
|
|
|
|
|
I.e. call each handler's terminate function.
|
|
|
|
-----------------------------------------------------------------------------*/
|
|
|
|
if (handlersP->item) {
|
|
|
|
unsigned int i;
|
|
|
|
for (i = handlersP->size; i > 0; --i) {
|
|
|
|
URIHandler2 * const handlerP = handlersP->item[i-1];
|
|
|
|
if (handlerP->term)
|
|
|
|
handlerP->term(handlerP);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
ServerFree(TServer * const srvP) {
|
|
|
|
|
|
|
|
free(srvP->name);
|
|
|
|
|
|
|
|
free(srvP->filespath);
|
|
|
|
|
|
|
|
terminateHandlers(&srvP->handlers);
|
|
|
|
|
|
|
|
ListFree(&srvP->handlers);
|
|
|
|
|
|
|
|
ListInitAutoFree(&srvP->defaultfilenames);
|
|
|
|
|
|
|
|
LogClose(srvP);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
ServerFunc(TConn * c) {
|
|
|
|
|
|
|
|
TSession r;
|
|
|
|
uint32_t ka;
|
|
|
|
TList handlers = c->server->handlers;
|
|
|
|
|
|
|
|
ka=c->server->keepalivemaxconn;
|
|
|
|
|
|
|
|
while (ka--) {
|
|
|
|
RequestInit(&r,c);
|
|
|
|
|
|
|
|
/* Wait to read until timeout */
|
|
|
|
if (!ConnRead(c,c->server->keepalivetimeout))
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (RequestRead(&r)) {
|
|
|
|
/* Check if it is the last keepalive */
|
|
|
|
if (ka==1)
|
|
|
|
r.keepalive=FALSE;
|
|
|
|
|
|
|
|
r.cankeepalive=r.keepalive;
|
|
|
|
|
|
|
|
if (r.status==0) {
|
|
|
|
if (r.versionmajor>=2)
|
|
|
|
ResponseStatus(&r,505);
|
|
|
|
else if (!RequestValidURI(&r))
|
|
|
|
ResponseStatus(&r,400);
|
|
|
|
else {
|
|
|
|
abyss_bool handled;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = c->server->handlers.size-1, handled = FALSE;
|
|
|
|
i >= 0 && !handled;
|
|
|
|
--i) {
|
|
|
|
URIHandler2 * const handlerP = handlers.item[i];
|
|
|
|
|
|
|
|
if (handlerP->handleReq2)
|
|
|
|
handlerP->handleReq2(handlerP, &r, &handled);
|
|
|
|
else if (handlerP->handleReq1)
|
|
|
|
handled = handlerP->handleReq1(&r);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!handled)
|
|
|
|
((URIHandler)(c->server->defaulthandler))(&r);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
HTTPWriteEnd(&r);
|
|
|
|
|
|
|
|
if (!r.done)
|
|
|
|
ResponseError(&r);
|
|
|
|
|
|
|
|
SessionLog(&r);
|
|
|
|
|
|
|
|
if (!(r.keepalive && r.cankeepalive))
|
|
|
|
break;
|
|
|
|
|
|
|
|
/**************** Must adjust the read buffer *****************/
|
|
|
|
ConnReadInit(c);
|
|
|
|
};
|
|
|
|
|
|
|
|
RequestFree(&r);
|
|
|
|
SocketClose(&(c->socket));
|
|
|
|
}
|
|
|
|
|
2007-04-25 19:57:14 +00:00
|
|
|
int ServerInit(TServer *srv)
|
2006-12-21 03:57:49 +00:00
|
|
|
{
|
|
|
|
/********* Must check errors from these functions *************/
|
2007-04-25 19:57:14 +00:00
|
|
|
if (!SocketInit()) {
|
|
|
|
TraceMsg("Can't initialize TCP sockets\n");
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!SocketCreate(&srv->listensock)) {
|
|
|
|
TraceMsg("Can't create a socket\n");
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!SocketBind(&srv->listensock,NULL,srv->port)) {
|
|
|
|
TraceMsg("Can't bind\n");
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!SocketListen(&srv->listensock,MAX_CONN)) {
|
|
|
|
TraceMsg("Can't listen\n");
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return TRUE;
|
2006-12-21 03:57:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* With pthread configuration, our connections run as threads of a
|
|
|
|
single address space, so we manage a pool of connection
|
|
|
|
descriptors. With fork configuration, our connections run as
|
|
|
|
separate processes with their own memory, so we don't have the
|
|
|
|
pool.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static abyss_bool const usingPthreadsForConnections =
|
|
|
|
#ifdef _THREAD
|
|
|
|
TRUE;
|
|
|
|
#else
|
|
|
|
FALSE;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
ServerRunThreaded(TServer *srv)
|
|
|
|
{
|
|
|
|
uint32_t i;
|
|
|
|
TSocket s,ns;
|
|
|
|
TIPAddr peerIpAddr;
|
|
|
|
TConn *c;
|
|
|
|
|
|
|
|
/* Connection array from Heap. Small systems might not
|
|
|
|
* have the "stack_size" required to have the array of
|
|
|
|
* connections right on it */
|
|
|
|
MALLOCARRAY_NOFAIL(c, MAX_CONN);
|
|
|
|
|
|
|
|
for (i=0;i<MAX_CONN;i++)
|
|
|
|
c[i].inUse = FALSE;
|
|
|
|
|
|
|
|
s=srv->listensock;
|
2007-11-30 22:56:01 +00:00
|
|
|
srv->running = 1;
|
|
|
|
while( srv->running )
|
2006-12-21 03:57:49 +00:00
|
|
|
{
|
|
|
|
/* collect all threads resources for closed connections */
|
|
|
|
for (i=0;i<MAX_CONN;i++)
|
|
|
|
{
|
|
|
|
if( c[i].inUse && ( c[i].connected == FALSE ) )
|
|
|
|
{
|
|
|
|
ConnClose( &c[i] );
|
|
|
|
c[i].inUse = FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i=0; i<MAX_CONN && c[i].inUse; ++i);
|
|
|
|
|
|
|
|
if (i==MAX_CONN)
|
|
|
|
{
|
|
|
|
/* Every connection descriptor was in use. */
|
|
|
|
ThreadWait(2000);
|
|
|
|
continue;
|
|
|
|
};
|
|
|
|
|
|
|
|
if (SocketAccept(&s,&ns,&peerIpAddr))
|
|
|
|
{
|
|
|
|
abyss_bool success;
|
|
|
|
c[i].inUse = TRUE;
|
|
|
|
success = ConnCreate2(&c[i], srv, ns, peerIpAddr, &ServerFunc,
|
|
|
|
ABYSS_BACKGROUND);
|
|
|
|
if (success)
|
|
|
|
{
|
|
|
|
ConnProcess(&c[i]);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
SocketClose(&ns);
|
|
|
|
c[i].inUse = FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
TraceMsg("Socket Error=%d\n", SocketError());
|
|
|
|
}
|
|
|
|
/* We never get here, but it's conceptually possible for someone to
|
|
|
|
terminate a server normally, so...
|
|
|
|
*/
|
|
|
|
free( c );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
ServerRunForked(TServer *srv)
|
|
|
|
{
|
|
|
|
TSocket s,ns;
|
|
|
|
TConn c;
|
|
|
|
TIPAddr ip;
|
|
|
|
|
|
|
|
s=srv->listensock;
|
|
|
|
|
|
|
|
while (1)
|
|
|
|
{
|
|
|
|
if (SocketAccept(&s,&ns,&ip))
|
|
|
|
{
|
|
|
|
abyss_bool success;
|
|
|
|
success = ConnCreate2(&c,
|
|
|
|
srv, ns, ip, ServerFunc, ABYSS_BACKGROUND);
|
|
|
|
|
|
|
|
/* ConnCreate2() forks. Child does not return. */
|
|
|
|
if (success)
|
|
|
|
ConnProcess(&c);
|
|
|
|
|
|
|
|
SocketClose(&ns); /* Close parent's copy of socket */
|
|
|
|
}
|
|
|
|
else
|
|
|
|
TraceMsg("Socket Error=%d\n", SocketError());
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
ServerRun(TServer * const serverP) {
|
|
|
|
if (usingPthreadsForConnections)
|
|
|
|
ServerRunThreaded(serverP);
|
|
|
|
else
|
|
|
|
ServerRunForked(serverP);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* ServerRunOnce() supplied by Brian Quinlan of ActiveState. */
|
|
|
|
|
|
|
|
/* Bryan Henderson found this to be completely wrong on 2001.11.29
|
|
|
|
and changed it so it does the same thing as ServerRun(), but only
|
|
|
|
once.
|
|
|
|
|
|
|
|
The biggest problem it had was that when it forked the child (via
|
|
|
|
ConnCreate(), both the parent and the child read the socket and
|
|
|
|
processed the request!
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
closeParentSocketCopy(TSocket * socketP) {
|
|
|
|
/*----------------------------------------------------------------------------
|
|
|
|
If we're doing forked connections, close the indicated socket because it
|
|
|
|
is the parent's copy and the parent doesn't need it. If we're doing
|
|
|
|
threaded connections, then there's no such thing as a parent's copy, so
|
|
|
|
nothing to close.
|
|
|
|
-----------------------------------------------------------------------------*/
|
|
|
|
#ifndef _THREAD
|
|
|
|
SocketClose(socketP);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ServerRunOnce2(TServer * const srv,
|
|
|
|
enum abyss_foreback const foregroundBackground)
|
|
|
|
{
|
|
|
|
TConn connection;
|
|
|
|
TSocket listenSocket;
|
|
|
|
TSocket connectedSocket;
|
|
|
|
TIPAddr remoteAddr;
|
|
|
|
abyss_bool succeeded;
|
|
|
|
|
|
|
|
srv->keepalivemaxconn = 1;
|
|
|
|
|
|
|
|
connection.connected = FALSE;
|
|
|
|
connection.inUse = FALSE;
|
|
|
|
|
|
|
|
listenSocket = srv->listensock;
|
|
|
|
|
|
|
|
succeeded = SocketAccept(&listenSocket, &connectedSocket, &remoteAddr);
|
|
|
|
if (succeeded) {
|
|
|
|
abyss_bool success;
|
|
|
|
success = ConnCreate2(&connection,
|
|
|
|
srv, connectedSocket, remoteAddr,
|
|
|
|
&ServerFunc,
|
|
|
|
foregroundBackground);
|
|
|
|
if (success)
|
|
|
|
ConnProcess(&connection);
|
|
|
|
closeParentSocketCopy(&connectedSocket);
|
|
|
|
} else
|
|
|
|
TraceMsg("Socket Error=%d\n", SocketError());
|
|
|
|
}
|
|
|
|
|
|
|
|
void ServerRunOnce(TServer *srv)
|
|
|
|
{
|
|
|
|
ServerRunOnce2(srv, ABYSS_BACKGROUND);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
ServerAddHandler2(TServer * const srvP,
|
|
|
|
URIHandler2 * const handlerArgP,
|
|
|
|
abyss_bool * const successP) {
|
|
|
|
|
|
|
|
URIHandler2 * handlerP;
|
|
|
|
|
|
|
|
MALLOCVAR(handlerP);
|
|
|
|
if (handlerP == NULL)
|
|
|
|
*successP = FALSE;
|
|
|
|
else {
|
|
|
|
*handlerP = *handlerArgP;
|
|
|
|
|
|
|
|
if (handlerP->init == NULL)
|
|
|
|
*successP = TRUE;
|
|
|
|
else
|
|
|
|
handlerP->init(handlerP, successP);
|
|
|
|
|
|
|
|
if (*successP) {
|
|
|
|
*successP = ListAdd(&srvP->handlers, handlerP);
|
|
|
|
|
|
|
|
if (!*successP) {
|
|
|
|
if (handlerP->term)
|
|
|
|
handlerP->term(handlerP);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!*successP)
|
|
|
|
free(handlerP);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
destroyHandler(URIHandler2 * const handlerP) {
|
|
|
|
|
|
|
|
free(handlerP);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static URIHandler2 *
|
|
|
|
createHandler(URIHandler const function) {
|
|
|
|
|
|
|
|
URIHandler2 * handlerP;
|
|
|
|
|
|
|
|
MALLOCVAR(handlerP);
|
|
|
|
if (handlerP != NULL) {
|
|
|
|
handlerP->init = NULL;
|
|
|
|
handlerP->term = destroyHandler;
|
|
|
|
handlerP->userdata = NULL;
|
|
|
|
handlerP->handleReq2 = NULL;
|
|
|
|
handlerP->handleReq1 = function;
|
|
|
|
}
|
|
|
|
return handlerP;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
abyss_bool
|
|
|
|
ServerAddHandler(TServer * const srvP,
|
|
|
|
URIHandler const function) {
|
|
|
|
|
|
|
|
URIHandler2 * handlerP;
|
|
|
|
abyss_bool success;
|
|
|
|
|
|
|
|
handlerP = createHandler(function);
|
|
|
|
|
|
|
|
if (handlerP == NULL)
|
|
|
|
success = FALSE;
|
|
|
|
else {
|
|
|
|
success = ListAdd(&srvP->handlers, handlerP);
|
|
|
|
|
|
|
|
if (!success)
|
|
|
|
free(handlerP);
|
|
|
|
}
|
|
|
|
return success;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
ServerDefaultHandler(TServer * const srvP,
|
|
|
|
URIHandler const handler) {
|
|
|
|
|
|
|
|
srvP->defaulthandler = handler;
|
|
|
|
}
|
|
|
|
|
|
|
|
abyss_bool LogOpen(TServer *srv, const char *filename) {
|
|
|
|
|
|
|
|
if (FileOpenCreate(&(srv->logfile),filename,O_WRONLY | O_APPEND)) {
|
|
|
|
if (MutexCreate(&(srv->logmutex)))
|
|
|
|
return TRUE;
|
|
|
|
else {
|
|
|
|
FileClose(&(srv->logfile));
|
|
|
|
srv->logfile=(-1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TraceMsg("Can't open log file '%s'",filename);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
void LogWrite(TServer *srv,char *c)
|
|
|
|
{
|
|
|
|
if ((srv->logfile)==(-1))
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (!MutexLock(&(srv->logmutex)))
|
|
|
|
return;
|
|
|
|
|
|
|
|
FileWrite(&(srv->logfile),c,strlen(c));
|
|
|
|
FileWrite(&(srv->logfile),LBR,strlen(LBR));
|
|
|
|
|
|
|
|
MutexUnlock(&(srv->logmutex));
|
|
|
|
}
|
|
|
|
|
|
|
|
void LogClose(TServer *srv)
|
|
|
|
{
|
|
|
|
if ((srv->logfile)==(-1))
|
|
|
|
return;
|
|
|
|
|
|
|
|
FileClose(&(srv->logfile));
|
|
|
|
MutexFree(&(srv->logmutex));
|
|
|
|
}
|
|
|
|
|
|
|
|
abyss_bool SessionLog(TSession *s)
|
|
|
|
{
|
|
|
|
char z[1024];
|
|
|
|
uint32_t n;
|
|
|
|
|
|
|
|
if (s->requestline == NULL)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
if (strlen(s->requestline)>1024-26-50)
|
|
|
|
s->requestline[1024-26-50]='\0';
|
|
|
|
|
|
|
|
n=sprintf(z,"%d.%d.%d.%d - %s - [",IPB1(s->conn->peerip),IPB2(s->conn->peerip),IPB3(s->conn->peerip),IPB4(s->conn->peerip),(s->user?s->user:""));
|
|
|
|
|
|
|
|
DateToLogString(&s->date,z+n);
|
|
|
|
|
2007-12-04 17:06:01 +00:00
|
|
|
sprintf(z+n+20,"] \"%s\" %d %d",s->requestline,s->status,s->conn->outbytes);
|
2006-12-21 03:57:49 +00:00
|
|
|
|
|
|
|
LogWrite(s->server,z);
|
|
|
|
return TRUE;
|
|
|
|
}
|