Newer
Older
/* Synchronet Web Server */
/****************************************************************************
* @format.tab-size 4 (Plain Text/Source Code File Header) *
* @format.use-tabs true (see http://www.synchro.net/ptsc_hdr.html) *
* *
* Copyright Rob Swindell - http://www.synchro.net/copyright.html *
* *
* 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. *
* See the GNU General Public License for more details: gpl.txt or *
* http://www.fsf.org/copyleft/gpl.html *
* *
* For Synchronet coding style and modification guidelines, see *
* http://www.synchro.net/source.html *
* *
* Note: If this box doesn't appear square, then you need to fix your tabs. *
****************************************************************************/
/*
* General notes: (ToDo stuff)
*
* Support the ident protocol... the standard log format supports it.
* Add in support to pass connections through to a different webserver...
* probobly in access.ars... with like a simplified mod_rewrite.
* This would allow people to run apache and Synchronet as the same site.
* Add support for multipart/form-data
*
* Add support for UNIX-domain sockets for FastCGI
*
* Improved Win32 support for POST data... currently will read past Content-Length
#if defined(__unix__)
#include <sys/wait.h> /* waitpid() */
#include <sys/types.h>
#include <signal.h> /* kill() */
#define JAVASCRIPT
#undef SBBS /* this shouldn't be defined unless building sbbs.dll/libsbbs.so */
#include "filedat.h"
#include "sockwrap.h" /* sendfilesocket() */
#include "threadwrap.h"
#include "semwrap.h"
#include "websrvr.h"
#include "hex.h"
#include "js_rtpool.h"
#include "js_request.h"
#include "xpmap.h"
#include "git_branch.h"
#include "git_hash.h"
static const char* server_name="Synchronet Web Server";
static const char* server_abbrev = "web";
static const char* newline="\r\n";
static const char* http_scheme="http://";
static const size_t http_scheme_len=7;
static const char* https_scheme="https://";
static const size_t https_scheme_len=8;
static const char* error_301="301 Moved Permanently";
static const char* error_302="302 Moved Temporarily";
static const char* error_307="307 Temporary Redirect";
static const char* error_404="404 Not Found";
static const char* error_416="416 Requested Range Not Satisfiable";
static const char* error_500="500 Internal Server Error";
static const char* error_503="503 Service Unavailable\r\nConnection: close\r\nContent-Length: 0\r\n\r\n";
static const char* unknown=STR_UNKNOWN_USER;
static char* text[TOTAL_TEXT];
#define MAX_REQUEST_LINE 1024 /* NOT including terminator */
#define MAX_HEADERS_SIZE 16384 /* Maximum total size of all headers
#define MAX_REDIR_LOOPS 20 /* Max. times to follow internal redirects for a single request */
#define MAX_POST_LEN (4*1048576) /* Max size of body for POSTS */
#define OUTBUF_LEN (256*1024) /* Size of output thread ring buffer */
enum {
CLEANUP_SSJS_TMP_FILE
,CLEANUP_POST_DATA
,MAX_CLEANUPS
static scfg_t scfg;
static volatile BOOL http_logging_thread_running=FALSE;
static protected_uint32_t active_clients;
static protected_uint32_t thread_count;

Rob Swindell
committed
static volatile ulong client_highwater=0;
static volatile BOOL terminate_server=FALSE;
static volatile BOOL terminate_http_logging_thread=FALSE;
static char root_dir[MAX_PATH+1];
static char error_dir[MAX_PATH+1];
static char cgi_env_ini[MAX_PATH+1];
static char default_auth_list[MAX_PATH+1];
static volatile time_t uptime=0;
static volatile ulong served=0;
static web_startup_t* startup=NULL;
static js_server_props_t js_server_props;
static str_list_t recycle_semfiles;
static str_list_t shutdown_semfiles;
static str_list_t cgi_env;
static named_string_t** mime_types;
static named_string_t** cgi_handlers;
static named_string_t** xjs_handlers;
static named_string_t** alias_list; // request path aliases
/* Logging stuff */
link_list_t log_list;
struct log_data {
char *hostname;
char *ident;
char *user;
char *request;
char *referrer;
char *agent;
char *vhost;
int status;

rswindell
committed
off_t size;
struct tm completed;
};
enum auth_type {
AUTHENTICATION_UNKNOWN
,AUTHENTICATION_BASIC
,AUTHENTICATION_DIGEST
"Unknown"
,"Basic"
,"Digest"
enum algorithm {
ALGORITHM_UNKNOWN
,ALGORITHM_MD5
,ALGORITHM_MD5_SESS
};
enum qop_option {
QOP_NONE
,QOP_AUTH
,QOP_AUTH_INT
,QOP_UNKNOWN
};
typedef struct {
enum auth_type type;
char username[(LEN_ALIAS > LEN_NAME ? LEN_ALIAS : LEN_NAME)+1];
char password[LEN_PASS+1];
char *digest_uri;
char *realm;
char *nonce;
enum algorithm algorithm;
enum qop_option qop_value;
char *cnonce;
char *nonce_count;
unsigned char digest[16]; /* MD5 digest */
BOOL stale;
typedef struct {
char virtual_path[MAX_PATH+1];
char physical_path[MAX_PATH+1];
BOOL expect_go_ahead;
time_t if_modified_since;
BOOL keep_alive;
char ars[256];
char host[128]; /* The requested host. (as used for self-referencing URLs) */
char vhost[128]; /* The requested host. (virtual host) */
int send_location;
BOOL upgrading;
char* location_to_send;
char* vary_list;
const char* mime_type;
str_list_t headers;
char status[MAX_REQUEST_LINE+1];
char * post_data;
struct xpmapping *post_map;
size_t post_len;
int dynamic;
char xjs_handler[MAX_PATH+1];
struct log_data *ld;
char request_line[MAX_REQUEST_LINE+1];
char orig_request_line[MAX_REQUEST_LINE+1];
BOOL finished; /* Done processing request. */
BOOL read_chunked;
BOOL write_chunked;
off_t range_start;
off_t range_end;
BOOL path_info_index;
/* CGI parameters */
char query_str[MAX_REQUEST_LINE+1];
char extra_path_info[MAX_REQUEST_LINE+1];
str_list_t cgi_env;
str_list_t dynamic_heads;
BOOL got_extra_path;
/* Dynamically (sever-side JS) generated HTML parameters */
FILE* fp;
BOOL sent_headers;
BOOL prev_write;
BOOL manual_length;
/* webctrl.ini overrides */
char *error_dir;
char *cgi_dir;
char *realm;
} http_request_t;
typedef struct {
SOCKET socket;
http_request_t req;
char host_name[128]; /* Resolved remote host */
int http_ver; /* Request HTTP version. 0 = HTTP/0.9, 1=HTTP/1.0, 2=HTTP/1.1 */
BOOL finished; /* Do not accept any more imput from client */
enum parsed_vpath parsed_vpath; /* file area/base access */
int libnum;
user_t user;
int last_user_num;
time_t logon_time;
int last_js_user_num;
char redir_req[MAX_REQUEST_LINE+1];
/* JavaScript parameters */
JSRuntime* js_runtime;
JSContext* js_cx;
JSObject* js_glob;
JSObject* js_query;
JSObject* js_header;
JSObject* js_cookie;
JSObject* js_request;
js_callback_t js_callback;
/* Ring Buffer Stuff */
RingBuf outbuf;
sem_t output_thread_terminated;
int outbuf_write_initialized;
pthread_mutex_t outbuf_write;
/* Client info */
client_t client;
/* Synchronization stuff */
pthread_mutex_t struct_filled;
/* TLS Stuff */
BOOL is_tls;
CRYPT_SESSION tls_sess;
BOOL tls_pending;
BOOL peeked_valid;
char peeked;
} http_session_t;
HTTP_0_9
,HTTP_1_0
,HTTP_1_1
};
static char* http_vers[] = {
""
,"HTTP/1.0"
,"HTTP/1.1"
static char* response_http_vers[] = {
""
,"HTTP/1.1"
,"HTTP/1.1"
,NULL /* terminator */
};
enum {
HTTP_HEAD
,HTTP_GET
,HTTP_POST
,HTTP_OPTIONS
IS_STATIC
,IS_CGI
,IS_SSJS
HEAD_DATE
,HEAD_HOST
,HEAD_IFMODIFIED
,HEAD_LENGTH
,HEAD_TYPE
,HEAD_AUTH
,HEAD_CONNECTION
,HEAD_WWWAUTH
,HEAD_STATUS
,HEAD_ALLOW
,HEAD_EXPIRES
,HEAD_LASTMODIFIED
,HEAD_LOCATION
,HEAD_PRAGMA
,HEAD_SERVER
,HEAD_REFERER
,HEAD_AGENT
,HEAD_TRANSFER_ENCODING
,HEAD_ACCEPT_RANGES
,HEAD_CONTENT_RANGE
,HEAD_RANGE
,HEAD_COOKIE
,HEAD_STS
,HEAD_UPGRADEINSECURE
,HEAD_VARY
,HEAD_CSP
};
static struct {
int id;
char* text;
} headers[] = {
{ HEAD_DATE, "Date" },
{ HEAD_HOST, "Host" },
{ HEAD_IFMODIFIED, "If-Modified-Since" },
{ HEAD_LENGTH, "Content-Length" },
{ HEAD_TYPE, "Content-Type" },
{ HEAD_AUTH, "Authorization" },
{ HEAD_CONNECTION, "Connection" },
{ HEAD_WWWAUTH, "WWW-Authenticate" },
{ HEAD_STATUS, "Status" },
{ HEAD_ALLOW, "Allow" },
{ HEAD_EXPIRES, "Expires" },
{ HEAD_LASTMODIFIED, "Last-Modified" },
{ HEAD_LOCATION, "Location" },
{ HEAD_PRAGMA, "Pragma" },
{ HEAD_SERVER, "Server" },
{ HEAD_REFERER, "Referer" },
{ HEAD_AGENT, "User-Agent" },
{ HEAD_TRANSFER_ENCODING, "Transfer-Encoding" },
{ HEAD_ACCEPT_RANGES, "Accept-Ranges" },
{ HEAD_CONTENT_RANGE, "Content-Range" },
{ HEAD_RANGE, "Range" },
{ HEAD_COOKIE, "Cookie" },
{ HEAD_STS, "Strict-Transport-Security" },
{ HEAD_UPGRADEINSECURE, "Upgrade-Insecure-Requests" },
{ HEAD_VARY, "Vary" },
{ HEAD_CSP, "Content-Security-Policy" },
/* Everything MOVED_TEMP and everything after is a magical internal redirect */
,MOVED_STAT
#define GCES(status, sess, action) do { \
char *GCES_estr; \
int GCES_level; \
get_crypt_error_string(status, sess->tls_sess, &GCES_estr, action, &GCES_level);\
if (GCES_estr) { \
if(GCES_level < startup->tls_error_level) \
GCES_level = startup->tls_error_level; \
lprintf(GCES_level, "%04d TLS %s", sess->socket, GCES_estr); \
free_crypt_attrstr(GCES_estr); \
static char *days[]={"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
static char *months[]={"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};
static void respond(http_session_t * session);
static BOOL js_setup_cx(http_session_t* session);
static BOOL js_setup(http_session_t* session);
static char *find_last_slash(char *str);
static BOOL check_extra_path(http_session_t * session);
static BOOL exec_ssjs(http_session_t* session, char* script);
static BOOL ssjs_send_headers(http_session_t* session, int chunked);
static int sess_recv(http_session_t *session, char *buf, size_t length, int flags);
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
static time_t
sub_mkgmt(struct tm *tm)
{
int y, nleapdays;
time_t t;
/* days before the month */
static const unsigned short moff[12] = {
0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
};
/*
* XXX: This code assumes the given time to be normalized.
* Normalizing here is impossible in case the given time is a leap
* second but the local time library is ignorant of leap seconds.
*/
/* minimal sanity checking not to access outside of the array */
if ((unsigned) tm->tm_mon >= 12)
return (time_t) -1;
if (tm->tm_year < 1970 - 1900)
return (time_t) -1;
y = tm->tm_year + 1900 - (tm->tm_mon < 2);
nleapdays = y / 4 - y / 100 + y / 400 -
((1970-1) / 4 - (1970-1) / 100 + (1970-1) / 400);
t = ((((time_t) (tm->tm_year - (1970 - 1900)) * 365 +
moff[tm->tm_mon] + tm->tm_mday - 1 + nleapdays) * 24 +
tm->tm_hour) * 60 + tm->tm_min) * 60 + tm->tm_sec;
return (t < 0 ? (time_t) -1 : t);
}
time_t
time_gm(struct tm *tm)
{
time_t t, t2;
struct tm *tm2;
int sec;
/* Do the first guess. */
if ((t = sub_mkgmt(tm)) == (time_t) -1)
return (time_t) -1;
/* save value in case *tm is overwritten by gmtime() */
sec = tm->tm_sec;
tm2 = gmtime(&t); /* why not use gmtime_r instead? */
if (tm2 == NULL || (t2 = sub_mkgmt(tm2)) == (time_t) -1)
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
return (time_t) -1;
if (t2 < t || tm2->tm_sec != sec) {
/*
* Adjust for leap seconds.
*
* real time_t time
* |
* tm
* / ... (a) first sub_mkgmt() conversion
* t
* |
* tm2
* / ... (b) second sub_mkgmt() conversion
* t2
* --->time
*/
/*
* Do the second guess, assuming (a) and (b) are almost equal.
*/
t += t - t2;
tm2 = gmtime(&t);
/*
* Either (a) or (b), may include one or two extra
* leap seconds. Try t, t + 2, t - 2, t + 1, and t - 1.
*/
if (tm2->tm_sec == sec
|| (t += 2, tm2 = gmtime(&t), tm2->tm_sec == sec)
|| (t -= 4, tm2 = gmtime(&t), tm2->tm_sec == sec)
|| (t += 3, tm2 = gmtime(&t), tm2->tm_sec == sec)
|| (t -= 2, tm2 = gmtime(&t), tm2->tm_sec == sec))
; /* found */
else {
/*
* Not found.
*/
if (sec >= 60)
/*
* The given time is a leap second
* (sec 60 or 61), but the time library
* is ignorant of the leap second.
*/
; /* treat sec 60 as 59,
sec 61 as 0 of the next minute */
else
/* The given time may not be normalized. */
t++; /* restore t */
}
}
return (t < 0 ? (time_t) -1 : t);
}
static int lputs(int level, const char* str)
{
mqtt_lputs(&mqtt, TOPIC_SERVER, level, str);
if(level <= LOG_ERR) {
char errmsg[1024];
SAFEPRINTF2(errmsg, "%-4s %s", server_abbrev, str);
errorlog(&scfg, &mqtt, level, startup == NULL ? NULL : startup->host_name, errmsg);
if(startup != NULL && startup->errormsg != NULL)
startup->errormsg(startup->cbdata, level, errmsg);
}
if(startup == NULL || startup->lputs == NULL || str == NULL || level > startup->log_level)
return 0;
#if defined(_WIN32)
if(IsBadCodePtr((FARPROC)startup->lputs))
return 0;
#endif
return startup->lputs(startup->cbdata,level,str);
}
#if defined(__GNUC__) // Catch printf-format errors with lprintf
static int lprintf(int level, const char *fmt, ...) __attribute__ ((format (printf, 2, 3)));
#endif
static int lprintf(int level, const char *fmt, ...)
{
va_list argptr;
char sbuf[1024];
va_start(argptr,fmt);
vsnprintf(sbuf,sizeof(sbuf),fmt,argptr);
sbuf[sizeof(sbuf)-1]=0;
va_end(argptr);
static int writebuf(http_session_t *session, const char *buf, size_t len)
{
size_t sent=0;
size_t avail;
if (session->req.sent_headers && session->req.send_content == FALSE)
return (0);
while(sent < len) {
avail=RingBufFree(&session->outbuf);
(void)WaitForEvent(session->outbuf.empty_event, 1);
if(avail > len-sent)
avail=len-sent;
sent+=RingBufWrite(&(session->outbuf), ((const BYTE *)buf)+sent, avail);
}
return(sent);
}
#define HANDLE_CRYPT_CALL(status, session, action) handle_crypt_call_except2(status, session, action, CRYPT_OK, CRYPT_OK, __FILE__, __LINE__)
#define HANDLE_CRYPT_CALL_EXCEPT(status, session, action, ignore) handle_crypt_call_except2(status, session, action, ignore, ignore, __FILE__, __LINE__)
#define HANDLE_CRYPT_CALL_EXCEPT2(status, session, action, ignore, ignore2) handle_crypt_call_except2(status, session, action, ignore, ignore2, __FILE__, __LINE__)
static BOOL handle_crypt_call_except2(int status, http_session_t *session, const char *action, int ignore, int ignore2, const char *file, int line)
if (status == ignore)
return FALSE;
if (status == ignore2)
return FALSE;
return FALSE;
}
static BOOL session_check(http_session_t *session, BOOL *rd, BOOL *wr, unsigned timeout)
{
BOOL ret = FALSE;
BOOL lcl_rd;
BOOL *rd_ptr = rd?rd:&lcl_rd;
if (session->is_tls) {
if(wr)
*wr=1;
return TRUE;
}
}
ret = socket_check(session->socket, rd_ptr, wr, timeout);
if (ret && *rd_ptr) {
session->tls_pending = TRUE;
return TRUE;
}
return ret;
}
return socket_check(session->socket, rd, wr, timeout);
}
static int sess_sendbuf(http_session_t *session, const char *buf, size_t len, BOOL *failed)
if (failed == NULL)
failed = &local_failed;
while(sent<len && session->socket!=INVALID_SOCKET && *failed == FALSE) {
if (socket_writable(session->socket, startup->max_inactivity * 1000)) {
if (session->is_tls) {
/*
* Limit as per js_socket.c.
* Sure, this is TLS, not SSH, but we see weird stuff here in sz file transfers.
*/
size_t sendbytes = len-sent;
if (sendbytes > 0x2000)
sendbytes = 0x2000;
status = cryptPushData(session->tls_sess, buf+sent, sendbytes, &tls_sent);
GCES(status, session, "pushing data");
if (status == CRYPT_ERROR_TIMEOUT) {
tls_sent = 0;
if(!cryptStatusOK(status=cryptPopData(session->tls_sess, "", 0, &status))) {
if (status != CRYPT_ERROR_TIMEOUT && status != CRYPT_ERROR_PARAM2)
GCES(status, session, "popping data after timeout");
if(cryptStatusOK(status)) {
HANDLE_CRYPT_CALL_EXCEPT(status = cryptFlushData(session->tls_sess), session, "flushing data", CRYPT_ERROR_COMPLETE);
if (cryptStatusError(status))
else
*failed=TRUE;
result = tls_sent;
}
else {
result=sendsocket(session->socket,buf+sent,len-sent);
if(result==SOCKET_ERROR) {
if(ERROR_VALUE==ECONNRESET)
lprintf(LOG_NOTICE,"%04d Connection reset by peer on send",session->socket);
else if(ERROR_VALUE==ECONNABORTED)
lprintf(LOG_NOTICE,"%04d Connection aborted by peer on send",session->socket);
#ifdef EPIPE
else if(ERROR_VALUE==EPIPE)
lprintf(LOG_NOTICE,"%04d Unable to send to peer",session->socket);
#endif
else
lprintf(LOG_WARNING,"%04d !ERROR %d sending on socket",session->socket,ERROR_VALUE);
*failed=TRUE;
return(sent);
}
}
}
else {
lprintf(LOG_WARNING,"%04d Timeout waiting for socket to become writable",session->socket);
*failed=TRUE;
return(sent);
HANDLE_CRYPT_CALL(cryptFlushData(session->tls_sess), session, "flushing data");
#ifdef _WINSOCKAPI_
static WSADATA WSAData;
#define SOCKLIB_DESC WSAData.szDescription
static BOOL WSAInitialized=FALSE;
static BOOL winsock_startup(void)
{
int status; /* Status Code */
if((status = WSAStartup(MAKEWORD(1,1), &WSAData))==0) {
lprintf(LOG_DEBUG,"%s %s",WSAData.szDescription, WSAData.szSystemStatus);
WSAInitialized=TRUE;
return (TRUE);
}
lprintf(LOG_CRIT,"!WinSock startup ERROR %d", status);
return (FALSE);
}
#else /* No WINSOCK */
#define winsock_startup() (TRUE)
#define SOCKLIB_DESC NULL
static char* server_host_name(void)
{
return startup->host_name[0] ? startup->host_name : scfg.sys_inetaddr;
}
static void set_state(enum server_state state)
if(startup != NULL) {
if(startup->set_state != NULL)
startup->set_state(startup->cbdata, state);
}
static void update_clients(void)
{
if(startup != NULL) {
uint32_t count = protected_uint32_value(active_clients);
if(startup->clients!=NULL)
startup->clients(startup->cbdata, count);
}
}
static void client_on(SOCKET sock, client_t* client, BOOL update)
{
if(startup!=NULL && startup->client_on!=NULL)
startup->client_on(startup->cbdata,TRUE,sock,client,update);
mqtt_client_on(&mqtt, TRUE, sock, client, update);
}
static void client_off(SOCKET sock)
{
if(startup!=NULL && startup->client_on!=NULL)
startup->client_on(startup->cbdata,FALSE,sock,NULL,FALSE);
mqtt_client_on(&mqtt, FALSE, sock, NULL, FALSE);
}
static void thread_up(BOOL setuid)
{
if(startup!=NULL && startup->thread_up!=NULL)
startup->thread_up(startup->cbdata,TRUE, setuid);
}
static void thread_down(void)
{
(void)protected_uint32_adjust(&thread_count,-1);
if(startup!=NULL && startup->thread_up!=NULL)
startup->thread_up(startup->cbdata,FALSE, FALSE);
/*********************************************************************/
/* Adds an environment variable to the sessions cgi_env linked list */
/*********************************************************************/
static void add_env(http_session_t *session, const char *name,const char *value) {
char newname[129];
lprintf(LOG_WARNING,"%04d Attempt to set NULL env variable", session->socket);
return;
}
SAFECOPY(newname,name);
for(p=newname;*p;p++) {
*p=toupper(*p);
if(*p=='-')
*p='_';
}
if(p==NULL) {
lprintf(LOG_WARNING,"%04d Cannot allocate memory for string", session->socket);
return;
}
strListPush(&session->req.cgi_env,p);
/***************************************/
/* Initializes default CGI envirnoment */
/***************************************/
static void init_enviro(http_session_t *session) {
char str[128];
union xp_sockaddr sockaddr;
socklen_t socklen = sizeof(sockaddr);
add_env(session,"SERVER_SOFTWARE",VERSION_NOTICE);
getsockname(session->socket, &sockaddr.addr, &socklen);
sprintf(str,"%d",inet_addrport(&sockaddr));
add_env(session,"SERVER_PORT",str);
add_env(session,"GATEWAY_INTERFACE","CGI/1.1");
if(!strcmp(session->host_name,session->host_ip))
add_env(session,"REMOTE_HOST",session->host_name);
add_env(session,"REMOTE_ADDR",session->host_ip);
add_env(session,"REQUEST_URI",session->req.orig_request_line);
* Sends string str to socket sock... returns number of bytes written, or 0 on an error
* Can not close the socket since it can not set it to INVALID_SOCKET
*/
static int bufprint(http_session_t *session, const char *str)
return(writebuf(session,str,len));
/**********************************************************/
/* Converts a month name/abbr to the 0-based month number */
/* ToDo: This probobly exists somewhere else already */
/**********************************************************/
static int getmonth(char *mon)
{
int i;
for(i=0;i<12;i++)
if(!stricmp(mon,months[i]))
return(i);
return 0;
}
/*******************************************************************/
/* Converts a date string in any of the common formats to a time_t */
/*******************************************************************/
static time_t decode_date(char *date)
{
struct tm ti;
ti.tm_sec=0; /* seconds (0 - 60) */
ti.tm_min=0; /* minutes (0 - 59) */
ti.tm_hour=0; /* hours (0 - 23) */
ti.tm_mday=1; /* day of month (1 - 31) */
ti.tm_mon=0; /* month of year (0 - 11) */
ti.tm_year=0; /* year - 1900 */
ti.tm_isdst=0; /* is summer time in effect? */
token=strtok_r(date,",",&last);
if(token==NULL)
return(0);
/* This probobly only needs to be 9, but the extra one is for luck. */
if(strlen(date)>15) {
token=strtok_r(date," ",&last);
if(token==NULL)
return(0);
token=strtok_r(NULL," ",&last);
if(token==NULL)
return(0);
ti.tm_mon=getmonth(token);
token=strtok_r(NULL," ",&last);
if(token==NULL)
return(0);
ti.tm_mday=atoi(token);
token=strtok_r(NULL,":",&last);
if(token==NULL)
return(0);
ti.tm_hour=atoi(token);
token=strtok_r(NULL,":",&last);
if(token==NULL)
return(0);
ti.tm_min=atoi(token);
token=strtok_r(NULL," ",&last);
if(token==NULL)
return(0);
ti.tm_sec=atoi(token);
token=strtok_r(NULL,"",&last);
if(token==NULL)
return(0);
ti.tm_year=atoi(token)-1900;
}
else {
/* RFC 1123 or RFC 850 */
token=strtok_r(NULL," -",&last);
if(token==NULL)
return(0);
ti.tm_mday=atoi(token);
token=strtok_r(NULL," -",&last);
if(token==NULL)
return(0);
ti.tm_mon=getmonth(token);
token=strtok_r(NULL," ",&last);
if(token==NULL)
return(0);
ti.tm_year=atoi(token);
token=strtok_r(NULL,":",&last);
if(token==NULL)
return(0);
ti.tm_hour=atoi(token);
token=strtok_r(NULL,":",&last);
if(token==NULL)
return(0);
ti.tm_min=atoi(token);
token=strtok_r(NULL," ",&last);
if(token==NULL)
return(0);
ti.tm_sec=atoi(token);
if(ti.tm_year>1900)
ti.tm_year -= 1900;
}
t=time_gm(&ti);
{
char error[256];
#ifdef SO_ACCEPTFILTER
struct accept_filter_arg afa;
#endif
if(startup!=NULL && startup->socket_open!=NULL)
startup->socket_open(startup->cbdata,TRUE);
if (cbdata != NULL && !strcmp(cbdata, "TLS")) {
if(set_socket_options(&scfg, sock, "web|http|tls", error, sizeof(error)))
lprintf(LOG_ERR,"%04d !ERROR %s",sock,error);
}
else {
if(set_socket_options(&scfg, sock, "web|http", error, sizeof(error)))
lprintf(LOG_ERR,"%04d !ERROR %s",sock,error);
#ifdef SO_ACCEPTFILTER
memset(&afa, 0, sizeof(afa));
strcpy(afa.af_name, "httpready");
setsockopt(sock, SOL_SOCKET, SO_ACCEPTFILTER, &afa, sizeof(afa));
#endif
}
static void close_socket_cb(SOCKET sock, void *cbdata)
{
if(startup!=NULL && startup->socket_open!=NULL)
startup->socket_open(startup->cbdata,FALSE);
static int close_socket(SOCKET *sock)
char ch;
time_t end = time(NULL) + startup->max_inactivity;
if(sock==NULL || *sock==INVALID_SOCKET)
/* required to ensure all data is sent */
shutdown(*sock,SHUT_WR);
while(socket_readable(*sock, startup->max_inactivity * 1000)) {
if (recv(*sock,&ch,1,0) <= 0)
break;
if (time(NULL) >= end)
break;
}
result=closesocket(*sock);
*sock=INVALID_SOCKET;
if(startup!=NULL && startup->socket_open!=NULL) {
startup->socket_open(startup->cbdata,FALSE);
}
if(result!=0) {
if(ERROR_VALUE!=ENOTSOCK)