Commit 23a82123 authored by Rob Swindell's avatar Rob Swindell 💬
Browse files

Add dynamic file area/base indexing to web server

By setting sbbs.ini [web] FileIndexScript to an SSJS or XJS script filename, that script (by default, from your exec directory) will be executed when a file area/base listing has been http[s]-requested. File area/base requests are of the form <vpath_prefix> (for the list of libraries), <vpath_prefix>/<lib-name>/ (for list of directories of a library) or <vpath_prefix>/<lib-name>/<dir-code-suffix>/ (for a list of files in a directory). The new http_request "lib" and "dir" properties indicate that a library or directory listing was requested (if neither are defined, that's a request for the root / list of libs). The same configured script is executed to handle all 3 types of index/list requests.

A sample script (webfileindex.ssj) will be committed soon.

Authentication (via HTTP-AUTH) will be required if user #0 does not have access to all libraries or all directories within a required library.

file_area.lib[].link has been changed from "/<vdir>/" to just "<vdir>" (no slashes) and renamed to "vdir".

file_area.dir[].link has been changed from "/<vpath>/" to "<vpath>/" (no leading slash) and renamed to "vpath".

Added file_area.dir[].vdir property that contains just the directory's virtual directory name.

I don't think anyone was using these "link" properties since the dynamic FTP HTML index scripting feature is no longer supported.

Added can_user_access_lib() to insure that the user has access to at least one directory of a library before allowing access to the library (e.g. via JS). Something similar should be created for message groups.
parent cf5b2141
Pipeline #2907 failed with stage
in 8 minutes and 45 seconds
......@@ -1411,6 +1411,19 @@ static BOOL can_list(lib_t *lib, dir_t *dir, user_t *user, client_t *client)
return FALSE;
}
static int getdir_from_vpath(scfg_t* cfg, const char* vpath, user_t* user, client_t* client, BOOL include_upload_only)
{
int dir = -1;
int lib = -1;
char* filename = NULL;
enum parsed_vpath result = parse_vpath(cfg, vpath, user, client, include_upload_only, &lib, &dir, &filename);
if(result == PARSED_VPATH_DIR || result == PARSED_VPATH_FULL)
return dir;
return -1;
}
static BOOL ftpalias(char* fullalias, char* filename, user_t* user, client_t* client, int* curdir)
{
char* p;
......
......@@ -34,10 +34,10 @@ static char* file_area_prop_desc[] = {
static char* lib_prop_desc[] = {
"index into lib_list array (or -1 if not in array) <i>(introduced in v3.12)</i>"
,"unique number for this library"
,"library name"
,"library description"
,"library access requirements"
,"library virtual path (for FTP or HTTP access)"
,"name"
,"description"
,"access requirements"
,"virtual directory name (for FTP or HTTP access)"
,"user has sufficient access to this library's directories <i>(introduced in v3.18)</i>"
,"internal code prefix (for directories) <i>(introduced in v3.18c)</i>"
,NULL
......@@ -70,6 +70,7 @@ static char* dir_prop_desc[] = {
,"configured maximum age (in days) of files before expiration"
,"percent of file size awarded uploader in credits upon file upload"
,"percent of file size awarded uploader in credits upon subsequent downloads"
,"virtual directory name (for FTP or HTTP access)"
,"virtual path (for FTP or HTTP access)"
,"number of files currently in this directory <i>(introduced in v3.18c)</i>"
,"timestamp of file base index of this directory <i>(introduced in v3.19)</i>"
......@@ -285,7 +286,7 @@ JSBool js_file_area_resolve(JSContext* cx, JSObject* areaobj, jsid id)
val=OBJECT_TO_JSVAL(libobj);
lib_index=-1;
if(p->user==NULL || chk_ar(p->cfg,p->cfg->lib[l]->ar,p->user,p->client)) {
if(p->user==NULL || can_user_access_lib(p->cfg, l, p->user, p->client)) {
if(!JS_GetArrayLength(cx, lib_list, (jsuint*)&lib_index))
return JS_FALSE;
......@@ -325,11 +326,10 @@ JSBool js_file_area_resolve(JSContext* cx, JSObject* areaobj, jsid id)
if(!JS_SetProperty(cx, libobj, "ars", &val))
return JS_FALSE;
sprintf(vpath,"/%s/",p->cfg->lib[l]->vdir);
if((js_str=JS_NewStringCopyZ(cx, vpath))==NULL)
if((js_str=JS_NewStringCopyZ(cx, p->cfg->lib[l]->vdir))==NULL)
return JS_FALSE;
val=STRING_TO_JSVAL(js_str);
if(!JS_SetProperty(cx, libobj, "link", &val))
if(!JS_SetProperty(cx, libobj, "vdir", &val))
return JS_FALSE;
val = BOOLEAN_TO_JSVAL(lib_index >= 0);
......@@ -531,53 +531,21 @@ JSBool js_file_area_resolve(JSContext* cx, JSObject* areaobj, jsid id)
if(!JS_SetProperty(cx, dirobj, "download_credit_pct", &val))
return JS_FALSE;
sprintf(vpath,"/%s/%s/"
if((js_str=JS_NewStringCopyZ(cx, p->cfg->dir[d]->vdir))==NULL)
return JS_FALSE;
val=STRING_TO_JSVAL(js_str);
if(!JS_SetProperty(cx, dirobj, "vdir", &val))
return JS_FALSE;
SAFEPRINTF2(vpath,"%s/%s/"
,p->cfg->lib[l]->vdir
,p->cfg->dir[d]->vdir
);
if((js_str=JS_NewStringCopyZ(cx, vpath))==NULL)
return JS_FALSE;
val=STRING_TO_JSVAL(js_str);
if(!JS_SetProperty(cx, dirobj, "link", &val))
return JS_FALSE;
#if 0
if(p->user == NULL || is_user_dirop(p->cfg, d, p->user, p->client))
is_op=TRUE;
else
is_op=FALSE;
val = BOOLEAN_TO_JSVAL(dir_index >= 0 && lib_index >= 0);
if(!JS_SetProperty(cx, dirobj, "can_access", &val))
return JS_FALSE;
if(p->user == NULL || can_user_upload(p->cfg, d, p->user, p->client, /* reason: */NULL))
val=JSVAL_TRUE;
else
val=JSVAL_FALSE;
if(!JS_SetProperty(cx, dirobj, "can_upload", &val))
if(!JS_SetProperty(cx, dirobj, "vpath", &val))
return JS_FALSE;
if(p->user == NULL || can_user_download(p->cfg, d, p->user, p->client, /* reason: */NULL))
val=JSVAL_TRUE;
else
val=JSVAL_FALSE;
if(!JS_SetProperty(cx, dirobj, "can_download", &val))
return JS_FALSE;
if(is_download_free(p->cfg,d,p->user,p->client))
val=JSVAL_TRUE;
else
val=JSVAL_FALSE;
if(!JS_SetProperty(cx, dirobj, "is_exempt", &val))
return JS_FALSE;
if(is_op)
val=JSVAL_TRUE;
else
val=JSVAL_FALSE;
if(!JS_SetProperty(cx, dirobj, "is_operator", &val))
return JS_FALSE;
#endif
if(!JS_DefineProperties(cx, dirobj, js_dir_properties))
return JS_FALSE;
......
......@@ -66,6 +66,7 @@ static const char* strSemFileCheckFrequency ="SemFileCheckFrequency";
static const char* strIniFileName ="iniFileName";
static const char* strFileVPathPrefix ="FileVPathPrefix";
static const char* strFileVPathForVHosts ="FileVPathForVHosts";
static const char* strFileIndexScript ="FileIndexScript";
#define DEFAULT_LOG_LEVEL LOG_DEBUG
#define DEFAULT_BIND_RETRY_COUNT 2
......@@ -714,6 +715,8 @@ void sbbs_read_ini(
,iniGetString(list,section,"Authentication",WEB_DEFAULT_AUTH_LIST,value));
SAFECOPY(web->logfile_base
,iniGetString(list,section,"HttpLogFile",nulstr,value));
SAFECOPY(web->file_index_script
,iniGetString(list, section, strFileIndexScript, nulstr, value));
SAFECOPY(web->file_vpath_prefix
,iniGetString(list, section, strFileVPathPrefix, nulstr, value));
web->file_vpath_for_vhosts = iniGetBool(list, section, strFileVPathForVHosts, FALSE);
......@@ -1271,6 +1274,8 @@ BOOL sbbs_write_ini(
break;
if(!iniSetString(lp,section,"HttpLogFile",web->logfile_base,&style))
break;
if(!iniSetString(lp,section,strFileIndexScript, web->file_index_script, &style))
break;
if(!iniSetString(lp,section,strFileVPathPrefix, web->file_vpath_prefix, &style))
break;
if(!iniSetBool(lp,section,strFileVPathForVHosts, web->file_vpath_for_vhosts, &style))
......
......@@ -3021,6 +3021,52 @@ BOOL can_user_post(scfg_t* cfg, uint subnum, user_t* user, client_t* client, uin
return TRUE;
}
/****************************************************************************/
// Determine if the specified user can access one or more directories of lib
/****************************************************************************/
BOOL can_user_access_lib(scfg_t* cfg, uint libnum, user_t* user, client_t* client)
{
uint count = 0;
for(uint dirnum = 0; dirnum < cfg->total_dirs; dirnum++) {
if(cfg->dir[dirnum]->lib != libnum)
continue;
if(can_user_access_dir(cfg, dirnum, user, client)) // checks lib's AR already
count++;
}
return count >= 1; // User has access to one or more directories of library
}
/****************************************************************************/
// Determine if the specified user can access ALL file libraries
/****************************************************************************/
BOOL can_user_access_all_libs(scfg_t* cfg, user_t* user, client_t* client)
{
for(uint libnum = 0; libnum < cfg->total_libs; libnum++) {
if(!can_user_access_lib(cfg, libnum, user, client))
return FALSE;
}
return TRUE;
}
/****************************************************************************/
// Determine if the specified user can all dirs of a lib
/****************************************************************************/
BOOL can_user_access_all_dirs(scfg_t* cfg, uint libnum, user_t* user, client_t* client)
{
uint count = 0;
for(uint dirnum = 0; dirnum < cfg->total_dirs; dirnum++) {
if(cfg->dir[dirnum]->lib != libnum)
continue;
if(can_user_access_dir(cfg, dirnum, user, client)) // checks lib's AR already
count++;
else
return FALSE;
}
return count >= 1; // User has access to one or more directories of library
}
/****************************************************************************/
/* Determine if the specified user can or cannot access the specified dir */
/****************************************************************************/
......@@ -3846,15 +3892,18 @@ int lookup_user(scfg_t* cfg, link_list_t* list, const char *inname)
return 0;
}
/* Returns the directory index of a virtual lib/dir path (e.g. main/games/filename) */
int getdir_from_vpath(scfg_t* cfg, const char* p, user_t* user, client_t* client, BOOL include_upload_only)
/* Parse a virtual filebase path of the form "[/]lib[/dir][/filename]" (e.g. main/games/filename.ext) */
enum parsed_vpath parse_vpath(scfg_t* cfg, const char* vpath, user_t* user, client_t* client, BOOL include_upload_only
,int* lib, int* dir, char** filename)
{
char* p;
char* tp;
char path[MAX_PATH+1];
uint dir;
uint lib;
SAFECOPY(path,p);
*lib = -1;
*dir = -1;
SAFECOPY(path, vpath);
p=path;
if(*p=='/')
......@@ -3862,33 +3911,40 @@ int getdir_from_vpath(scfg_t* cfg, const char* p, user_t* user, client_t* client
if(!strncmp(p,"./",2))
p+=2;
if(*p == '\0')
return PARSED_VPATH_ROOT;
tp=strchr(p,'/');
if(tp) *tp=0;
for(lib=0;lib<cfg->total_libs;lib++) {
if(!chk_ar(cfg,cfg->lib[lib]->ar,user,client))
for(*lib = 0; *lib < cfg->total_libs; (*lib)++) {
if(!chk_ar(cfg,cfg->lib[*lib]->ar,user,client))
continue;
if(!stricmp(cfg->lib[lib]->vdir,p))
if(!stricmp(cfg->lib[*lib]->vdir,p))
break;
}
if(lib>=cfg->total_libs)
return(-1);
if(tp!=NULL)
p=tp+1;
if(*lib >= cfg->total_libs)
return PARSED_VPATH_NONE;
if(tp == NULL || *(tp + 1) == '\0')
return PARSED_VPATH_LIB;
p=tp+1;
tp=strchr(p,'/');
if(tp) *tp=0;
for(dir=0;dir<cfg->total_dirs;dir++) {
if(cfg->dir[dir]->lib!=lib)
if(tp) {
*tp=0;
if(*(tp + 1) != '\0')
*filename = getfname(vpath);
}
for(*dir = 0; *dir < cfg->total_dirs; (*dir)++) {
if(cfg->dir[*dir]->lib != *lib)
continue;
if((!include_upload_only || (dir!=cfg->sysop_dir && dir!=cfg->upload_dir))
&& !chk_ar(cfg,cfg->dir[dir]->ar,user,client))
if((!include_upload_only || (*dir != cfg->sysop_dir && *dir != cfg->upload_dir))
&& !chk_ar(cfg,cfg->dir[*dir]->ar,user,client))
continue;
if(!stricmp(cfg->dir[dir]->vdir,p))
if(!stricmp(cfg->dir[*dir]->vdir,p))
break;
}
if(dir>=cfg->total_dirs)
return(-1);
if(*dir >= cfg->total_dirs)
return PARSED_VPATH_NONE;
return(dir);
return *filename == NULL ? PARSED_VPATH_DIR : PARSED_VPATH_FULL;
}
......@@ -88,6 +88,9 @@ DLLEXPORT BOOL logoutuserdat(scfg_t*, user_t*, time_t now, time_t logontime);
DLLEXPORT void resetdailyuserdat(scfg_t*, user_t*, BOOL write);
DLLEXPORT void subtract_cdt(scfg_t*, user_t*, long amt);
DLLEXPORT int user_rec_len(int offset);
DLLEXPORT BOOL can_user_access_all_libs(scfg_t*, user_t*, client_t*);
DLLEXPORT BOOL can_user_access_all_dirs(scfg_t*, uint libnum, user_t*, client_t*);
DLLEXPORT BOOL can_user_access_lib(scfg_t*, uint libnum, user_t*, client_t*);
DLLEXPORT BOOL can_user_access_dir(scfg_t*, uint dirnum, user_t*, client_t* client);
DLLEXPORT BOOL can_user_access_sub(scfg_t*, uint subnum, user_t*, client_t* client);
DLLEXPORT BOOL can_user_read_sub(scfg_t*, uint subnum, user_t*, client_t* client);
......@@ -101,7 +104,15 @@ DLLEXPORT BOOL is_download_free(scfg_t*, uint dirnum, user_t*, client_t* client)
DLLEXPORT BOOL is_host_exempt(scfg_t*, const char* ip_addr, const char* host_name);
DLLEXPORT BOOL filter_ip(scfg_t*, const char* prot, const char* reason, const char* host
,const char* ip_addr, const char* username, const char* fname);
DLLEXPORT int getdir_from_vpath(scfg_t*, const char* p, user_t*, client_t*, BOOL include_upload_only);
enum parsed_vpath {
PARSED_VPATH_NONE,
PARSED_VPATH_ROOT,
PARSED_VPATH_LIB,
PARSED_VPATH_DIR,
PARSED_VPATH_FULL
};
DLLEXPORT enum parsed_vpath parse_vpath(scfg_t*, const char* vpath, user_t*, client_t*, BOOL include_upload_only
,int* libnum, int* dirnum, char** filename);
/* user .ini file access */
DLLEXPORT BOOL user_get_property(scfg_t*, unsigned user_number, const char* section, const char* key, char* value, size_t maxlen);
......
......@@ -254,7 +254,8 @@ typedef struct {
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 */
BOOL filebase_access;
enum parsed_vpath parsed_vpath; /* file area/base access */
int libnum;
file_t file;
user_t user;
int last_user_num;
......@@ -1894,14 +1895,24 @@ static BOOL check_ars(http_session_t * session)
}
if(!http_checkuser(session))
return(FALSE);
if((session->filebase_access
&& !can_user_download(&scfg, session->file.dir, &session->user, &session->client, NULL))
|| session->req.ars[0]) {
if(session->req.ars[0]) {
/* There *IS* an ARS string ie: Auth is required */
if(startup->options&WEB_OPT_DEBUG_RX)
lprintf(LOG_NOTICE,"%04d !No authentication information",session->socket);
return(FALSE);
}
if(session->user.number == 0) {
switch(session->parsed_vpath) {
case PARSED_VPATH_FULL:
return can_user_download(&scfg, session->file.dir, &session->user, &session->client, NULL);
case PARSED_VPATH_ROOT:
return can_user_access_all_libs(&scfg, &session->user, &session->client);
case PARSED_VPATH_LIB:
return can_user_access_all_dirs(&scfg, session->libnum, &session->user, &session->client);
case PARSED_VPATH_DIR:
return can_user_access_dir(&scfg, session->file.dir, &session->user, &session->client);
}
}
/* No auth required, allow */
return(TRUE);
}
......@@ -1991,7 +2002,7 @@ static BOOL check_ars(http_session_t * session)
session->req.ld->user=strdup(session->req.auth.username);
}
if(session->filebase_access) {
if(session->parsed_vpath == PARSED_VPATH_FULL) {
if(is_download_free(&scfg, session->file.dir, &session->user, &session->client)
|| session->user.cdt >= session->file.cost)
authorized = can_user_download(&scfg, session->file.dir, &session->user, &session->client, NULL);
......@@ -2343,21 +2354,26 @@ static void js_add_request_property(http_session_t * session, char *key, char *v
if(session->js_cx==NULL || session->js_request==NULL)
return;
if(key==NULL || value==NULL)
if(key==NULL)
return;
if(len)
js_str=JS_NewStringCopyN(session->js_cx, value, len);
else
js_str=JS_NewStringCopyZ(session->js_cx, value);
if(value == NULL) {
// Remove property
JS_DeleteProperty(session->js_cx, session->js_request, key);
} else {
if(len)
js_str=JS_NewStringCopyN(session->js_cx, value, len);
else
js_str=JS_NewStringCopyZ(session->js_cx, value);
if(js_str == NULL)
return;
if(js_str == NULL)
return;
uintN attrs = JSPROP_ENUMERATE;
if(!writeable)
attrs |= JSPROP_READONLY;
JS_DefineProperty(session->js_cx, session->js_request, key, STRING_TO_JSVAL(js_str)
,NULL,NULL,attrs);
uintN attrs = JSPROP_ENUMERATE;
if(!writeable)
attrs |= JSPROP_READONLY;
JS_DefineProperty(session->js_cx, session->js_request, key, STRING_TO_JSVAL(js_str)
,NULL,NULL,attrs);
}
}
static void js_add_request_prop_writeable(http_session_t * session, char *key, char *value)
......@@ -2887,7 +2903,7 @@ static int is_dynamic_req(http_session_t* session)
char fname[MAX_PATH+1];
char ext[MAX_PATH+1];
if(session->filebase_access)
if(session->parsed_vpath == PARSED_VPATH_FULL)
return IS_STATIC;
check_extra_path(session);
......@@ -3126,7 +3142,29 @@ static BOOL get_request_headers(http_session_t * session)
return TRUE;
}
static BOOL get_fullpath(http_session_t * session)
static enum parsed_vpath resolve_vpath(http_session_t* session, char* vpath)
{
char* filename = NULL;
enum parsed_vpath result = parse_vpath(&scfg, vpath + strlen(startup->file_vpath_prefix), &session->user, &session->client
,/* include_upload_only: */false, &session->libnum, &session->file.dir, &filename);
if(result != PARSED_VPATH_FULL)
return result;
char path[MAX_PATH + 1];
safe_snprintf(path, sizeof(path), "%s%s", scfg.dir[session->file.dir]->path, filename);
if(!fexistcase(path))
return PARSED_VPATH_NONE;
if(!loadfile(&scfg, session->file.dir, filename, &session->file, file_detail_index))
return PARSED_VPATH_NONE;
strlcpy(vpath, path, MAX_PATH);
return PARSED_VPATH_FULL;
}
enum get_fullpath {
FULLPATH_INVALID,
FULLPATH_VALID,
FULLPATH_NOEXIST
};
static enum get_fullpath get_fullpath(http_session_t * session)
{
char str[MAX_PATH+1];
bool vhost = false;
......@@ -3156,14 +3194,25 @@ static BOOL get_fullpath(http_session_t * session)
if(startup->file_vpath_prefix[0] && (vhost == false || startup->file_vpath_for_vhosts == true)
&& strncmp(session->req.physical_path, startup->file_vpath_prefix, strlen(startup->file_vpath_prefix)) == 0) {
session->filebase_access = TRUE;
return TRUE;
session->parsed_vpath = resolve_vpath(session, session->req.physical_path);
switch(session->parsed_vpath) {
case PARSED_VPATH_NONE:
return FULLPATH_NOEXIST;
case PARSED_VPATH_FULL:
return FULLPATH_VALID;
default:
if(getfname(startup->file_index_script) == startup->file_index_script) // no path specified
SAFEPRINTF2(str, "%s%s", scfg.exec_dir, startup->file_index_script);
else
SAFECOPY(str, startup->file_index_script);
break;
}
}
if(FULLPATH(session->req.physical_path,str,sizeof(session->req.physical_path))==NULL)
return(FALSE);
return FULLPATH_INVALID;
return(isabspath(session->req.physical_path));
return(isabspath(session->req.physical_path) ? FULLPATH_VALID : FULLPATH_INVALID);
}
static BOOL is_legal_host(const char *host, BOOL strip_port)
......@@ -3250,8 +3299,9 @@ static BOOL get_req(http_session_t * session, char *request_line)
send_error(session,__LINE__,"400 Bad Request");
return FALSE;
}
if(!get_fullpath(session)) {
send_error(session,__LINE__,error_500);
enum get_fullpath fullpath_valid = get_fullpath(session);
if(fullpath_valid != FULLPATH_VALID) {
send_error(session,__LINE__, fullpath_valid == FULLPATH_NOEXIST ? error_404 : error_500);
return(FALSE);
}
if(session->req.ld!=NULL && session->req.ld->vhost==NULL)
......@@ -3532,20 +3582,6 @@ static void read_webctrl_section(FILE *file, char *section, http_session_t *sess
iniFreeNamedStringList(values);
}
static bool resolve_filebase_path(http_session_t* session, char* path)
{
uint dir = getdir_from_vpath(&scfg, path + strlen(startup->file_vpath_prefix), &session->user, &session->client, false);
if(dir >= scfg.total_dirs)
return false;
char filename[MAX_PATH + 1];
SAFECOPY(filename, getfname(path));
session->file.dir = dir;
safe_snprintf(path, MAX_PATH, "%s%s", scfg.dir[dir]->path, filename);
if(!fexistcase(path))
return false;
return loadfile(&scfg, dir, filename, &session->file, file_detail_index);
}
static BOOL check_request(http_session_t * session)
{
char path[MAX_PATH+1];
......@@ -3574,14 +3610,6 @@ static BOOL check_request(http_session_t * session)
SAFECOPY(path,session->req.physical_path);
if(startup->options&WEB_OPT_DEBUG_TX)
lprintf(LOG_DEBUG,"%04d Path is: %s",session->socket,path);
if(session->filebase_access) {
if(!resolve_filebase_path(session, path)) {
send_error(session,__LINE__,error_404);
return FALSE;
}
}
if(isdir(path)) {
last_ch=*lastchar(path);
if(!IS_PATH_DELIM(last_ch)) {
......@@ -3632,7 +3660,7 @@ static BOOL check_request(http_session_t * session)
/* Set default ARS to a 0-length string */
session->req.ars[0]=0;
if(!session->filebase_access) {
if(session->parsed_vpath == PARSED_VPATH_NONE) {
if(strnicmp(path,root_dir,strlen(root_dir))) {
session->req.keep_alive=FALSE;
......@@ -5987,6 +6015,8 @@ static BOOL exec_ssjs(http_session_t* session, char* script) {
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);
js_add_request_prop(session, "lib", session->libnum >= 0 ? scfg.lib[session->libnum]->sname : NULL);
js_add_request_prop(session, "dir", session->file.dir >= 0 ? scfg.dir[session->file.dir]->code : NULL);
if(session->req.query_str[0]) {
js_add_request_prop(session,"query_string",session->req.query_str);
js_parse_query(session,session->req.query_str);
......@@ -6097,7 +6127,7 @@ static void respond(http_session_t * session)
e = 1;
lprintf(LOG_INFO, "%04d Sent file: %s (%"PRIdOFF" bytes, %ld cps)"
,session->socket, session->req.physical_path, snt, (long)(snt / e));
if(session->filebase_access)
if(session->parsed_vpath == PARSED_VPATH_FULL)
user_downloaded_file(&scfg, &session->user, &session->client, session->file.dir, session->file.name, snt);
}
}
......
......@@ -68,6 +68,7 @@ typedef struct {
char** index_file_name; /* Index filenames */
char logfile_base[INI_MAX_VALUE_LEN]; /* Logfile base name (date is appended) */
char ini_fname[INI_MAX_VALUE_LEN];
char file_index_script[INI_MAX_VALUE_LEN];
char file_vpath_prefix[INI_MAX_VALUE_LEN];
BOOL file_vpath_for_vhosts;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment