Newer
Older
}
session->req.prev_write=TRUE;
for(i=0; i<argc; i++) {
if((str=JS_ValueToString(cx, argv[i]))==NULL)
continue;
JSSTRING_TO_STRING(cx, str, cstr, NULL);
rc=JS_SUSPENDREQUEST(cx);
if(writeln)
js_writebuf(session, newline, 2);
JS_RESUMEREQUEST(cx, rc);
}
if(str==NULL)
else
return(JS_TRUE);
}
static JSBool
js_write(JSContext *cx, uintN argc, jsval *arglist)
return(JS_TRUE);
}
static JSBool
js_writeln(JSContext *cx, uintN argc, jsval *arglist)
return(JS_TRUE);
}
js_set_cookie(JSContext *cx, uintN argc, jsval *arglist)
jsval *argv=JS_ARGV(cx, arglist);
char header_buf[8192];
char *header;
char *p;
int32 i;
JS_SET_RVAL(cx, arglist, JSVAL_VOID);
if((session=(http_session_t*)JS_GetContextPrivate(cx))==NULL)
return(JS_FALSE);
if(argc<2)
return(JS_FALSE);
header=header_buf;
JSVALUE_TO_STRING(cx, argv[0], p, NULL);
if(!p)
return(JS_FALSE);
header+=sprintf(header,"Set-Cookie: %s=",p);
JSVALUE_TO_STRING(cx, argv[1], p, NULL);
if(!p)
return(JS_FALSE);
header+=sprintf(header,"%s",p);
if(argc>2) {
if(!JS_ValueToInt32(cx,argv[2],&i))
return JS_FALSE;
header += strftime(header,50,"; expires=%a, %d-%b-%Y %H:%M:%S GMT",&tm);
}
if(argc>3) {
JSVALUE_TO_STRING(cx, argv[3], p, NULL);
if(p!=NULL && *p)
header += sprintf(header,"; domain=%s",p);
}
if(argc>4) {
JSVALUE_TO_STRING(cx, argv[4], p, NULL);
if(p!=NULL && *p)
header += sprintf(header,"; path=%s",p);
}
if(argc>5) {
header += sprintf(header,"; secure");
}
strListPush(&session->req.dynamic_heads,header_buf);
return(JS_TRUE);
}
static JSBool
js_log(JSContext *cx, uintN argc, jsval *arglist)
jsval *argv=JS_ARGV(cx, arglist);
char str[512];
uintN i=0;
int32 level=LOG_INFO;
http_session_t* session;
JS_SET_RVAL(cx, arglist, JSVAL_VOID);
if((session=(http_session_t*)JS_GetContextPrivate(cx))==NULL)
return(JS_FALSE);
if(startup==NULL || startup->lputs==NULL)
return(JS_FALSE);
if(argc > 1 && JSVAL_IS_NUMBER(argv[i])) {
if(!JS_ValueToInt32(cx,argv[i++],&level))
return JS_FALSE;
}
str[0]=0;
for(;i<argc && strlen(str)<(sizeof(str)/2);i++) {
JSVALUE_TO_STRING(cx, argv[i], val, NULL);
return(JS_FALSE);
strcat(str," ");
}
rc=JS_SUSPENDREQUEST(cx);
lprintf(level,"%04d %s",session->socket,str);
JS_RESUMEREQUEST(cx, rc);
JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(JS_NewStringCopyZ(cx, str)));
return(JS_TRUE);
}
static JSBool
js_login(JSContext *cx, uintN argc, jsval *arglist)
jsval *argv=JS_ARGV(cx, arglist);
char* p;
JSBool inc_logons=JS_FALSE;
user_t user;
http_session_t* session;
JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(JS_FALSE));
if((session=(http_session_t*)JS_GetContextPrivate(cx))==NULL)
return(JS_FALSE);
/* User name */
JSVALUE_TO_STRING(cx, argv[0], p, NULL);
return(JS_FALSE);
rc=JS_SUSPENDREQUEST(cx);
memset(&user,0,sizeof(user));
if(isdigit(*p))
user.number=atoi(p);
else if(*p)
user.number=matchuser(&scfg,p,FALSE);
if(getuserdat(&scfg,&user)!=0) {
lprintf(LOG_NOTICE,"%04d !USER NOT FOUND: '%s'"
,session->socket,p);
JS_RESUMEREQUEST(cx, rc);
return(JS_TRUE);
}
if(user.misc&(DELETED|INACTIVE)) {
lprintf(LOG_WARNING,"%04d !DELETED OR INACTIVE USER #%d: %s"
,session->socket,user.number,p);
JS_RESUMEREQUEST(cx, rc);
return(JS_TRUE);
}
JS_RESUMEREQUEST(cx, rc);
/* Password */
if(user.pass[0]) {
JSVALUE_TO_STRING(cx, argv[1], p, NULL);
return(JS_FALSE);
if(stricmp(user.pass,p)) { /* Wrong password */
rc=JS_SUSPENDREQUEST(cx);
lprintf(LOG_WARNING,"%04d !INVALID PASSWORD ATTEMPT FOR USER: %s"
,session->socket,user.alias);
JS_RESUMEREQUEST(cx, rc);
return(JS_TRUE);
}
}
if(argc>2)
JS_ValueToBoolean(cx,argv[2],&inc_logons);
rc=JS_SUSPENDREQUEST(cx);
if(inc_logons) {
user.logons++;
user.ltoday++;
}
http_logon(session, &user);
JS_RESUMEREQUEST(cx, rc);
/* user-specific objects */
if(!js_CreateUserObjects(session->js_cx, session->js_glob, &scfg, &session->user, &session->client
,NULL /* ftp index file */, session->subscan /* subscan */)) {
lprintf(LOG_ERR,"%04d !JavaScript ERROR creating user objects",session->socket);
send_error(session,"500 Error initializing JavaScript User Objects");
return(FALSE);
}
JS_SET_RVAL(cx, arglist,BOOLEAN_TO_JSVAL(JS_TRUE));
return(JS_TRUE);
}
#if 0
static char *find_next_pair(char *buffer, size_t buflen, char find)
{
char *p;
char *search;
char *end;
size_t buflen2;
4234
4235
4236
4237
4238
4239
4240
4241
4242
4243
4244
4245
4246
4247
4248
4249
4250
4251
4252
4253
4254
4255
4256
4257
4258
4259
4260
4261
4262
4263
4264
4265
4266
4267
4268
4269
4270
4271
4272
4273
4274
4275
4276
4277
4278
4279
4280
4281
4282
4283
4284
4285
4286
4287
4288
4289
4290
4291
4292
4293
4294
4295
4296
4297
4298
4299
4300
4301
4302
4303
4304
4305
4306
4307
4308
4309
4310
4311
4312
4313
4314
4315
4316
4317
4318
4319
4320
4321
4322
4323
4324
4325
4326
4327
4328
4329
4330
4331
4332
4333
4334
4335
4336
4337
4338
4339
4340
4341
4342
end=buffer+buflen;
search=buffer;
buflen2=buflen;
for(;search<end;) {
p=memchr(search, chars[i], buflen2);
/* Can't even find one... there's definatly no pair */
if(p==NULL)
return(NULL);
if(*(p+1)==find)
return(p);
/* Next search pos is at the char after the match */
search=p+1;
buflen2=end-search;
}
}
static void js_write_escaped(JSContext *cx, JSObject *obj, char *pos, size_t len, char *name_end, char *repeat_section)
{
char *name=pos+2;
}
enum {
T_AT
,T_PERCENT
,T_CARET
,T_LT
};
static int js_write_template_part(JSContext *cx, JSObject *obj, char *template, size_t len, char *repeat_section)
{
size_t len2;
char *pos;
char *end;
char *p;
char *p2;
char *send_end;
int no_more[4];
char *next[4];
int i,j;
char chars[5]="@%^<";
end=template+len;
pos=template;
memset(&next,0,sizeof(next));
memset(&no_more,0,sizeof(no_more));
while(pos<end) {
send_end=NULL;
/* Find next seperator */
for(i=0; i<4; i++) {
if(!no_more[i]) {
if(next[i] < pos)
next[i]=NULL;
if(next[i] == NULL) {
if((next[i]=find_next_pair(pos, len, chars[i]))==NULL) {
no_more[i]=TRUE;
continue;
}
}
if(!send_end || next[i] < send_end)
send_end=next[i];
}
}
if(send_end==NULL) {
/* Nothing else matched... we're good now! */
js_writebuf(session, pos, len);
pos=end;
len=0;
continue;
}
if(send_end > pos) {
i=send_end-pos;
js_writebuf(session, pos, i);
pos+=i;
len-=i;
}
/*
* At this point, pos points to a matched introducer.
* If it's not a repeat section, we can just output it here.
*/
if(*pos != '<') {
/*
* If there is no corresponding terminator to this introdcer,
* force it to be output unchanged.
*/
if((p=find_next_pair(pos, len, *pos))==NULL) {
no_more[strchr(chars,*pos)-char]=TRUE;
continue;
}
js_write_escaped(cx, obj, pos, len, p, repeat_section);
continue;
}
/*
* Pos is the start of a repeat section now... this is where things
* start to get tricky. Set up RepeatObj object, then call self
* once for each repeat.
*/
}
}
static JSBool
js_write_template(JSContext *cx, uintN argc, jsval *arglist)
JSObject *obj=JS_THIS_OBJECT(cx, arglist);
jsval *argv=JS_ARGV(cx, arglist);
JSString* js_str;
char *filename;
char *template;
FILE *tfile;
size_t len;
http_session_t* session;
JS_SET_RVAL(cx, arglist, JSVAL_VOID);
if((session=(http_session_t*)JS_GetContextPrivate(cx))==NULL)
return(JS_FALSE);
if(session->req.fp==NULL)
return(JS_FALSE);
JSVALUE_TO_STRING(cx, argv[0], filename, NULL);
if(filename==NULL)
return(JS_FALSE);
if(!fexist(filename)) {
JS_ReportError(cx, "Template file %s does not exist.", filename);
return(JS_FALSE);
}
len=flength(filename);
if((tfile=fopen(filename,"r"))==NULL) {
JS_ReportError(cx, "Unable to open template %s for read.", filename);
return(JS_FALSE);
}
if((template=(char *)alloca(len))==NULL) {
4378
4379
4380
4381
4382
4383
4384
4385
4386
4387
4388
4389
4390
4391
4392
4393
4394
4395
4396
4397
4398
4399
4400
4401
4402
4403
4404
4405
4406
4407
4408
4409
4410
4411
4412
4413
4414
4415
JS_ReportError(cx, "Unable to allocate %u bytes for template.", len);
return(JS_FALSE);
}
if(fread(template, 1, len, tfile) != len) {
fclose(tfile);
JS_ReportError(cx, "Unable to read %u bytes from template %s.", len, filename);
return(JS_FALSE);
}
fclose(tfile);
if((!session->req.prev_write) && (!session->req.sent_headers)) {
if(session->http_ver>=HTTP_1_1 && session->req.keep_alive) {
if(!ssjs_send_headers(session,TRUE))
return(JS_FALSE);
}
else {
/* "Fast Mode" requested? */
jsval val;
JSObject* reply;
JS_GetProperty(cx, session->js_glob, "http_reply", &val);
reply=JSVAL_TO_OBJECT(val);
JS_GetProperty(cx, reply, "fast", &val);
if(JSVAL_IS_BOOLEAN(val) && JSVAL_TO_BOOLEAN(val)) {
session->req.keep_alive=FALSE;
if(!ssjs_send_headers(session,FALSE))
return(JS_FALSE);
}
}
}
session->req.prev_write=TRUE;
js_write_template_part(cx, obj, template, len, NULL);
return(JS_TRUE);
}
#endif
static JSFunctionSpec js_global_functions[] = {
{"write", js_write, 1}, /* write to HTML file */
{"writeln", js_writeln, 1}, /* write line to HTML file */
{"print", js_writeln, 1}, /* write line to HTML file (alias) */
{"log", js_log, 0}, /* Log a string */
{"login", js_login, 2}, /* log in as a different user */
{"set_cookie", js_set_cookie, 2}, /* Set a cookie */
{0}
};
static JSBool
js_OperationCallback(JSContext *cx)
JSBool ret;
http_session_t* session;
JS_SetOperationCallback(cx, NULL);
if((session=(http_session_t*)JS_GetContextPrivate(cx))==NULL) {
JS_SetOperationCallback(cx, js_OperationCallback);
return(JS_FALSE);
ret=js_CommonOperationCallback(cx,&session->js_callback);
JS_SetOperationCallback(cx, js_OperationCallback);
static JSContext*
js_initcx(http_session_t *session)
{
JSContext* js_cx;
lprintf(LOG_DEBUG,"%04d JavaScript: Initializing context (stack: %lu bytes)"
,session->socket,startup->js.cx_stack);
if((js_cx = JS_NewContext(session->js_runtime, startup->js.cx_stack))==NULL)
return(NULL);
JS_BEGINREQUEST(js_cx);
lprintf(LOG_DEBUG,"%04d JavaScript: Context created",session->socket);
JS_SetErrorReporter(js_cx, js_ErrorReporter);
JS_SetOperationCallback(js_cx, js_OperationCallback);
lprintf(LOG_DEBUG,"%04d JavaScript: Creating Global Objects and Classes",session->socket);
if(!js_CreateCommonObjects(js_cx, &scfg, NULL
,NULL /* global */
,uptime /* system */
,startup->host_name /* system */
,SOCKLIB_DESC /* system */
,&session->js_callback /* js */
,&session->client /* client */
,session->socket /* client */
,&js_server_props /* server */
,&session->js_glob
)
|| !JS_DefineFunctions(js_cx, session->js_glob, js_global_functions)) {
JS_RemoveObjectRoot(js_cx, &session->js_glob);
JS_ENDREQUEST(js_cx);
JS_DestroyContext(js_cx);
return(NULL);
}
return(js_cx);
}
static BOOL js_setup(http_session_t* session)
#ifndef ONE_JS_RUNTIME
if(session->js_runtime == NULL) {
lprintf(LOG_DEBUG,"%04d JavaScript: Creating runtime: %lu bytes"
,session->socket,startup->js.max_bytes);
if((session->js_runtime=jsrt_GetNew(startup->js.max_bytes, 5000, __FILE__, __LINE__))==NULL) {
lprintf(LOG_ERR,"%04d !ERROR creating JavaScript runtime",session->socket);
return(FALSE);
}
}
#endif
if(session->js_cx==NULL) { /* Context not yet created, create it now */
/* js_initcx() begins a context */
if(((session->js_cx=js_initcx(session))==NULL)) {
lprintf(LOG_ERR,"%04d !ERROR initializing JavaScript context",session->socket);
return(FALSE);
}
argv=JS_NewArrayObject(session->js_cx, 0, NULL);
JS_DefineProperty(session->js_cx, session->js_glob, "argv", OBJECT_TO_JSVAL(argv)
,NULL,NULL,JSPROP_READONLY|JSPROP_ENUMERATE);
JS_DefineProperty(session->js_cx, session->js_glob, "argc", INT_TO_JSVAL(0)
,NULL,NULL,JSPROP_READONLY|JSPROP_ENUMERATE);
JS_DefineProperty(session->js_cx, session->js_glob, "web_root_dir",
STRING_TO_JSVAL(JS_NewStringCopyZ(session->js_cx, root_dir))
,NULL,NULL,JSPROP_READONLY|JSPROP_ENUMERATE);
JS_DefineProperty(session->js_cx, session->js_glob, "web_error_dir",
STRING_TO_JSVAL(JS_NewStringCopyZ(session->js_cx, session->req.error_dir?session->req.error_dir:error_dir))
,NULL,NULL,JSPROP_READONLY|JSPROP_ENUMERATE);
JS_BEGINREQUEST(session->js_cx);
lprintf(LOG_DEBUG,"%04d JavaScript: Initializing HttpRequest object",session->socket);
if(js_CreateHttpRequestObject(session->js_cx, session->js_glob, session)==NULL) {
lprintf(LOG_ERR,"%04d !ERROR initializing JavaScript HttpRequest object",session->socket);
JS_ENDREQUEST(session->js_cx);
return(FALSE);
}
lprintf(LOG_DEBUG,"%04d JavaScript: Initializing HttpReply object",session->socket);
if(js_CreateHttpReplyObject(session->js_cx, session->js_glob, session)==NULL) {
lprintf(LOG_ERR,"%04d !ERROR initializing JavaScript HttpReply object",session->socket);
JS_ENDREQUEST(session->js_cx);
JS_SetContextPrivate(session->js_cx, session);
JS_ENDREQUEST(session->js_cx);
return(TRUE);
}
static BOOL ssjs_send_headers(http_session_t* session,int chunked)
jsval val;
JSObject* reply;
JSIdArray* heads;
JSObject* headers;
int i;
char str[MAX_REQUEST_LINE+1];
JS_BEGINREQUEST(session->js_cx);
JS_GetProperty(session->js_cx,session->js_glob,"http_reply",&val);
reply = JSVAL_TO_OBJECT(val);
JS_GetProperty(session->js_cx,reply,"status",&val);
JSVALUE_TO_STRING(session->js_cx, val, p, NULL);
JS_GetProperty(session->js_cx,reply,"header",&val);
headers = JSVAL_TO_OBJECT(val);
heads=JS_Enumerate(session->js_cx,headers);
if(heads != NULL) {
for(i=0;i<heads->length;i++) {
JS_IdToValue(session->js_cx,heads->vector[i],&val);
JSVALUE_TO_STRING(session->js_cx, val, p, NULL);
JS_GetProperty(session->js_cx,headers,p,&val);
JSVALUE_TO_STRING(session->js_cx, val, p2, NULL);
safe_snprintf(str,sizeof(str),"%s: %s",p,p2);
JS_ClearScope(session->js_cx, headers);
JS_ENDREQUEST(session->js_cx);
return(send_headers(session,session->req.status,chunked));
}
static BOOL exec_ssjs(http_session_t* session, char* script) {
JSObject* js_script;
jsval rval;
char path[MAX_PATH+1];
BOOL retval=TRUE;
long double start;
/* External JavaScript handler? */
if(script == session->req.physical_path && session->req.xjs_handler[0])
script = session->req.xjs_handler;
sprintf(path,"%sSBBS_SSJS.%u.%u.html",temp_dir,getpid(),session->socket);
if((session->req.fp=fopen(path,"wb"))==NULL) {
lprintf(LOG_ERR,"%04d !ERROR %d opening/creating %s", session->socket, errno, path);
return(FALSE);
}
if(session->req.cleanup_file[CLEANUP_SSJS_TMP_FILE]) {
if(!(startup->options&WEB_OPT_DEBUG_SSJS))
remove(session->req.cleanup_file[CLEANUP_SSJS_TMP_FILE]);
free(session->req.cleanup_file[CLEANUP_SSJS_TMP_FILE]);
}
/* FREE()d in close_request() */
session->req.cleanup_file[CLEANUP_SSJS_TMP_FILE]=strdup(path);
JS_BEGINREQUEST(session->js_cx);
js_add_request_prop(session,"real_path",session->req.physical_path);
js_add_request_prop(session,"virtual_path",session->req.virtual_path);
js_add_request_prop(session,"ars",session->req.ars);
js_add_request_prop(session,"request_string",session->req.request_line);
js_add_request_prop(session,"host",session->req.host);
js_add_request_prop(session,"vhost",session->req.vhost);
js_add_request_prop(session,"http_ver",http_vers[session->http_ver]);
js_add_request_prop(session,"remote_ip",session->host_ip);
js_add_request_prop(session,"remote_host",session->host_name);
if(session->req.query_str && session->req.query_str[0]) {
js_add_request_prop(session,"query_string",session->req.query_str);
js_parse_query(session,session->req.query_str);
}
if(session->req.post_data && session->req.post_data[0]) {
js_add_request_prop(session,"post_data",session->req.post_data);
js_parse_query(session,session->req.post_data);
}
do {
/* RUN SCRIPT */
JS_ClearPendingException(session->js_cx);
session->js_callback.counter=0;
lprintf(LOG_DEBUG,"%04d JavaScript: Compiling script: %s",session->socket,script);
if((js_script=JS_CompileFile(session->js_cx, session->js_glob
,script))==NULL) {
lprintf(LOG_ERR,"%04d !JavaScript FAILED to compile script (%s)"
,session->socket,script);
JS_RemoveObjectRoot(session->js_cx, &session->js_glob);
JS_ENDREQUEST(session->js_cx);
return(FALSE);
}
lprintf(LOG_DEBUG,"%04d JavaScript: Executing script: %s",session->socket,script);
start=xp_timer();
js_PrepareToExecute(session->js_cx, session->js_glob, script, /* startup_dir */NULL);
JS_ExecuteScript(session->js_cx, session->js_glob, js_script, &rval);
js_EvalOnExit(session->js_cx, session->js_glob, &session->js_callback);
JS_RemoveObjectRoot(session->js_cx, &session->js_glob);
lprintf(LOG_DEBUG,"%04d JavaScript: Done executing script: %s (%.2Lf seconds)"
,session->socket,script,xp_timer()-start);
} while(0);
SAFECOPY(session->req.physical_path, path);
if(session->req.fp!=NULL) {
fclose(session->req.fp);
session->req.fp=NULL;
}
/* Read http_reply object */
if(!session->req.sent_headers) {
retval=ssjs_send_headers(session,FALSE);
}
/* Free up temporary resources here */
session->req.dynamic=IS_SSJS;
JS_ENDREQUEST(session->js_cx);
return(retval);
}
static void respond(http_session_t * session)
{
BOOL send_file=TRUE;
if(session->req.method==HTTP_OPTIONS) {
send_headers(session,session->req.status,FALSE);
}
else {
if(session->req.dynamic==IS_CGI) {
if(!exec_cgi(session)) {
send_error(session,error_500);
return;
}
session->req.finished=TRUE;
if(session->req.dynamic==IS_SSJS) { /* Server-Side JavaScript */
if(!exec_ssjs(session,session->req.physical_path)) {
send_error(session,error_500);
return;
}
sprintf(session->req.physical_path
,"%sSBBS_SSJS.%u.%u.html",temp_dir,getpid(),session->socket);
}
else {
session->req.mime_type=get_mime_type(strrchr(session->req.physical_path,'.'));
send_file=send_headers(session,session->req.status,FALSE);
if(session->req.method==HTTP_HEAD || session->req.method==HTTP_OPTIONS)
if(send_file) {
lprintf(LOG_INFO,"%04d Sending file: %s (%"PRIuOFF" bytes)"
,session->socket, session->req.physical_path, flength(session->req.physical_path));
snt=sock_sendfile(session,session->req.physical_path,session->req.range_start,session->req.range_end);
if(session->req.ld!=NULL) {
if(snt<0)
snt=0;
session->req.ld->size=snt;
}
lprintf(LOG_INFO,"%04d Sent file: %s (%d bytes)"
,session->socket, session->req.physical_path, snt);
session->req.finished=TRUE;
int read_post_data(http_session_t * session)
{
unsigned i=0;
4722
4723
4724
4725
4726
4727
4728
4729
4730
4731
4732
4733
4734
4735
4736
4737
4738
4739
4740
4741
4742
4743
4744
4745
4746
4747
if(session->req.dynamic!=IS_CGI && (session->req.post_len || session->req.read_chunked)) {
if(session->req.read_chunked) {
char *p;
size_t ch_len=0;
int bytes_read=0;
char ch_lstr[12];
session->req.post_len=0;
while(1) {
/* Read chunk length */
if(sockreadline(session,ch_lstr,sizeof(ch_lstr)-1)>0) {
ch_len=strtol(ch_lstr,NULL,16);
}
else {
send_error(session,error_500);
return(FALSE);
}
if(ch_len==0)
break;
/* Check size */
i += ch_len;
if(i > MAX_POST_LEN) {
send_error(session,"413 Request entity too large");
return(FALSE);
}
/* realloc() to new size */
/* FREE()d in close_request */
p=realloc(session->req.post_data, i);
if(p==NULL) {
lprintf(LOG_CRIT,"%04d !ERROR Allocating %d bytes of memory",session->socket,session->req.post_len);
send_error(session,"413 Request entity too large");
return(FALSE);
}
session->req.post_data=p;
/* read new data */
bytes_read=recvbufsocket(&session->socket,session->req.post_data+session->req.post_len,ch_len);
if(!bytes_read) {
send_error(session,error_500);
return(FALSE);
}
session->req.post_len+=bytes_read;
/* Read chunk terminator */
if(sockreadline(session,ch_lstr,sizeof(ch_lstr)-1)>0)
send_error(session,error_500);
/* Read more headers! */
if(!get_request_headers(session))
return(FALSE);
if(!parse_headers(session))
return(FALSE);
else {
i = session->req.post_len;
/* FREE()d in close_request() */
if(i < (MAX_POST_LEN+1) && (session->req.post_data=malloc(i+1)) != NULL)
session->req.post_len=recvbufsocket(&session->socket,session->req.post_data,i);
else {
lprintf(LOG_CRIT,"%04d !ERROR Allocating %d bytes of memory",session->socket,i);
send_error(session,"413 Request entity too large");
return(FALSE);
}
}
if(session->req.post_len != i)
lprintf(LOG_DEBUG,"%04d !ERROR Browser said they sent %d bytes, but I got %d",session->socket,i,session->req.post_len);
if(session->req.post_len > i)
session->req.post_len = i;
session->req.post_data[session->req.post_len]=0;
}
return(TRUE);
}
void http_output_thread(void *arg)
{
http_session_t *session=(http_session_t *)arg;
RingBuf *obuf;
char buf[OUTBUF_LEN+12]; /* *MUST* be large enough to hold the buffer,
the size of the buffer in hex, and four extra bytes. */
char *bufdata;
int failed=0;
int len;
unsigned avail;
int chunked;
int i;
unsigned mss=OUTBUF_LEN;
SetThreadName("HTTP Output");
obuf=&(session->outbuf);
if((i=pthread_mutex_init(&session->outbuf_write,NULL))!=0) {
lprintf(LOG_DEBUG,"Error %d initializing outbuf mutex",i);
close_socket(&session->socket);
return;
}
session->outbuf_write_initialized=1;
#ifdef TCP_MAXSEG
/*
* Auto-tune the highwater mark to be the negotiated MSS for the
* socket (when possible)
*/
if(!obuf->highwater_mark) {
socklen_t sl;
sl=sizeof(i);
if(!getsockopt(session->socket, IPPROTO_TCP, TCP_MAXSEG, &i, &sl)) {
/* Check for sanity... */
if(i>100) {
lprintf(LOG_DEBUG,"%04d Autotuning outbuf highwater mark to %d based on MSS"
,session->socket,i);
mss=obuf->highwater_mark;
if(mss>OUTBUF_LEN) {
mss=OUTBUF_LEN;
lprintf(LOG_DEBUG,"%04d MSS (%d) is higher than OUTBUF_LEN (%d)"
,session->socket,i,OUTBUF_LEN);
}
}
}
}
#endif
thread_up(TRUE /* setuid */);
/*
* Do *not* exit on terminate_server... wait for session thread
* to close the socket and set it to INVALID_SOCKET
*/
while(session->socket!=INVALID_SOCKET) {
/* Wait for something to output in the RingBuffer */
if((avail=RingBufFull(obuf))==0) { /* empty */
if(sem_trywait_block(&obuf->sem,1000))
continue;
/* Check for spurious sem post... */
if((avail=RingBufFull(obuf))==0)
continue;
}
else
sem_trywait(&obuf->sem);
/* Wait for full buffer or drain timeout */
if(obuf->highwater_mark) {
if(avail<obuf->highwater_mark) {
sem_trywait_block(&obuf->highwater_sem,startup->outbuf_drain_timeout);
/* We (potentially) blocked, so get fill level again */
avail=RingBufFull(obuf);
} else
sem_trywait(&obuf->highwater_sem);
/*
* At this point, there's something to send and,
* if the highwater mark is set, the timeout has
* passed or we've hit highwater. Read ring buffer
* into linear buffer.
*/
/*
* Read the current value of write_chunked... since we wait until the
* ring buffer is empty before fiddling with it.
*/
chunked=session->req.write_chunked;
bufdata=buf;
if(chunked) {
i=sprintf(buf, "%X\r\n", avail);
bufdata+=i;
len+=i;
}
pthread_mutex_lock(&session->outbuf_write);
RingBufRead(obuf, bufdata, avail);
if(chunked) {
bufdata+=avail;
*(bufdata++)='\r';
*(bufdata++)='\n';
len+=2;
}
if(!failed)
sock_sendbuf(&session->socket, buf, len, &failed);
pthread_mutex_unlock(&session->outbuf_write);
}
thread_down();
pthread_mutex_lock(&session->outbuf_write);
session->outbuf_write_initialized=0;
pthread_mutex_unlock(&session->outbuf_write);
pthread_mutex_destroy(&session->outbuf_write);
sem_post(&session->output_thread_terminated);
void http_session_thread(void* arg)
{
char* host_name;
HOSTENT* host;
SOCKET socket;
char redir_req[MAX_REQUEST_LINE+1];
char *redirp;
int loop_count;
BOOL init_error;
int32_t clients_remain;
SetThreadName("HTTP Session");
pthread_mutex_lock(&((http_session_t*)arg)->struct_filled);
pthread_mutex_unlock(&((http_session_t*)arg)->struct_filled);
pthread_mutex_destroy(&((http_session_t*)arg)->struct_filled);
session=*(http_session_t*)arg; /* copies arg BEFORE it's freed */
socket=session.socket;
if(socket==INVALID_SOCKET) {
session_threads--;
return;
}
lprintf(LOG_DEBUG,"%04d Session thread started", session.socket);
if(startup->index_file_name==NULL || startup->cgi_ext==NULL)
lprintf(LOG_DEBUG,"%04d !!! DANGER WILL ROBINSON, DANGER !!!", session.socket);
#ifdef _WIN32
if(startup->answer_sound[0] && !(startup->options&BBS_OPT_MUTE))
PlaySound(startup->answer_sound, NULL, SND_ASYNC|SND_FILENAME);
#endif
thread_up(TRUE /* setuid */);
session.finished=FALSE;
/* Start up the output buffer */
/* FREE()d in this block (RingBufDispose before all returns) */
if(RingBufInit(&(session.outbuf), OUTBUF_LEN)) {
lprintf(LOG_ERR,"%04d Canot create output ringbuffer!", session.socket);
close_socket(&session.socket);
thread_down();
session_threads--;
return;
}
/* Destroyed in this block (before all returns) */
sem_init(&session.output_thread_terminated,0,0);
_beginthread(http_output_thread, 0, &session);
sbbs_srand(); /* Seed random number generator */
if(startup->options&BBS_OPT_NO_HOST_LOOKUP)
host=NULL;
else
host=gethostbyaddr ((char *)&session.addr.sin_addr
if(host!=NULL && host->h_name!=NULL)
host_name=host->h_name;
else
host_name=session.host_ip;
SAFECOPY(session.host_name,host_name);
if(!(startup->options&BBS_OPT_NO_HOST_LOOKUP)) {
lprintf(LOG_INFO,"%04d Hostname: %s", session.socket, session.host_name);
#if 0 /* gethostbyaddr() is apparently not (always) thread-safe
and getnameinfo() doesn't return alias information */
for(i=0;host!=NULL && host->h_aliases!=NULL
&& host->h_aliases[i]!=NULL;i++)
lprintf(LOG_INFO,"%04d HostAlias: %s", session.socket, host->h_aliases[i]);
#endif
if(trashcan(&scfg,session.host_name,"host")) {
lprintf(LOG_NOTICE,"%04d !CLIENT BLOCKED in host.can: %s", session.socket, session.host_name);
close_socket(&session.socket);
sem_wait(&session.output_thread_terminated);
RingBufDispose(&session.outbuf);
session_threads--;