Commits (3)
  • Rob Swindell's avatar
    Remove unused stub function. · efe3b357
    Rob Swindell authored
    efe3b357
  • Rob Swindell's avatar
    Overhaul statistics files · 908a31be
    Rob Swindell authored
    */dsts.dab (daily statistics and running totals) -> */dsts.ini
    */csts.dab (cumulative statistics / log) -> */csts.tab
    
    * dsts.ini now has both daily and total stats for all fields (not just timeon and logons).
    * dsts.ini is now an easily modifiable text file - no longer need dstsedit (here-by deprecated and soon to be deleted)
    * dsts.ini and csts.tab support 64-bit upload/download byte stats and are very extensible for future fields to be added or extended > 32-bit (this was the main inspiration for this overhaul, but it was overdue and already designed for v4, pretty much)
    * csts.tab is a tab-delimited fixed length record format suitable for easy import to a spreadsheet program or parsing with scripts. Each day is a 128-character LF-delimited record with tab-delimited fields of plain ASCII text.
    * All fields except timeon in dsts.ini files are updated immediately and by more non-terminal servers (e.g. post statistics from web UI scripts).
    * New user stats are tracked more than just for "today".
    
    The upgrade of these files is automatic and built-into SBBS.
    
    Still to do: overhaul the slog utility to support the new csts.tab file format.
    908a31be
  • Rob Swindell's avatar
......@@ -26,6 +26,7 @@
#include "userdat.h"
#include "filedat.h"
#include "load_cfg.h"
#include "getstats.h"
#include "smblib.h"
#include "git_branch.h"
#include "git_hash.h"
......@@ -77,29 +78,11 @@ int lprintf(int level, const char *fmat, ...)
}
/****************************************************************************/
/* Updates dstst.dab file */
/* Updates dsts.ini file */
/****************************************************************************/
void updatestats(ulong size)
void updatestats(off_t size)
{
char str[MAX_PATH+1];
int file;
uint32_t l;
SAFEPRINTF(str,"%sdsts.dab",scfg.ctrl_dir);
if((file=nopen(str,O_RDWR|O_BINARY))==-1) {
printf("ERR_OPEN %s\n",str);
return;
}
lseek(file,20L,SEEK_SET); /* Skip timestamp, logons and logons today */
read(file,&l,4); /* Uploads today */
l++;
lseek(file,-4L,SEEK_CUR);
write(file,&l,4);
read(file,&l,4); /* Upload bytes today */
l+=size;
lseek(file,-4L,SEEK_CUR);
write(file,&l,4);
close(file);
inc_upload_stats(&scfg, 1, size);
}
bool reupload(smb_t* smb, file_t* f)
......@@ -184,9 +167,9 @@ void addlist(char *inpath, uint dirnum, const char* uploader, uint dskip, uint s
memset(ext, 0, sizeof(ext));
memset(&f, 0, sizeof(f));
char fdesc[LEN_FDESC + 1] = {0};
uint32_t cdt = (uint32_t)flength(filepath);
off_t cdt = flength(filepath);
time_t file_timestamp = fdate(filepath);
printf("%10"PRIu32" %s\n"
printf("%10"PRIuOFF" %s\n"
,cdt, unixtodstr(&scfg,(time32_t)file_timestamp,str));
exist = smb_findfile(&smb, fname, &f) == SMB_SUCCESS;
if(exist) {
......@@ -418,7 +401,7 @@ void addlist(char *inpath, uint dirnum, const char* uploader, uint dskip, uint s
, result, smb.last_error, smb.file);
if(mode&UL_STATS)
updatestats((ulong)l);
updatestats(l);
files++;
} while(!feof(stream) && !ferror(stream));
fclose(stream);
......
......@@ -176,6 +176,7 @@
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<ClCompile Include="getstats.c" />
<ClCompile Include="sauce.c" />
<ClCompile Include="userdat.c">
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
......
......@@ -2555,10 +2555,6 @@ void __fastcall TMainForm::HelpAboutMenuItemClick(TObject *Sender)
delete AboutBoxForm;
}
//---------------------------------------------------------------------------
BOOL MuteService(SC_HANDLE svc, SERVICE_STATUS* status, BOOL mute)
{
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::SoundToggleExecute(TObject *Sender)
{
SoundToggle->Checked=!SoundToggle->Checked;
......
......@@ -162,6 +162,7 @@
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<ClCompile Include="getstats.c" />
<ClCompile Include="sauce.c" />
<ClCompile Include="userdat.c" />
</ItemGroup>
......
......@@ -162,6 +162,7 @@
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<ClCompile Include="getstats.c" />
<ClCompile Include="sauce.c" />
<ClCompile Include="userdat.c" />
</ItemGroup>
......
......@@ -170,6 +170,7 @@
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<ClCompile Include="getstats.c" />
<ClCompile Include="sauce.c" />
<ClCompile Include="userdat.c" />
</ItemGroup>
......
......@@ -807,7 +807,7 @@ static void send_thread(void* arg)
}
}
if(!xfer.tmpfile && !xfer.delfile && !(scfg.dir[f.dir]->misc&DIR_NOSTAT))
inc_sys_download_stats(&scfg, 1, (ulong)total);
inc_download_stats(&scfg, 1, (ulong)total);
}
if(xfer.credits) {
......@@ -1078,7 +1078,7 @@ static void receive_thread(void* arg)
,cdt*(uint64_t)(scfg.dir[f.dir]->up_pct/100.0));
}
if(!(scfg.dir[f.dir]->misc&DIR_NOSTAT))
inc_sys_upload_stats(&scfg, 1, (ulong)total);
inc_upload_stats(&scfg, 1, (ulong)total);
}
/* Send ACK */
sockprintf(xfer.ctrl_sock,sess,"226 Upload complete (%lu cps).",cps);
......
/* Synchronet C-exported statistics retrieval functions */
/* Synchronet C-exported statistics retrieval and update functions */
/****************************************************************************
* @format.tab-size 4 (Plain Text/Source Code File Header) *
......@@ -22,25 +22,275 @@
#include "getstats.h"
#include "nopen.h"
#include "smblib.h"
#include "ini_file.h"
#include "xpendian.h"
#include "xpdatetime.h"
// dsts.ini (Daily Statistics) keys:
#define strStatsDate "Date"
#define strStatsTotal "Total"
#define strStatsToday "Today"
#define strStatsLogons "Logons"
#define strStatsTimeon "Timeon"
#define strStatsUploads "Uploads"
#define strStatsUploadBytes "UploadB"
#define strStatsDownloads "Dnloads"
#define strStatsDownloadBytes "DnloadB"
#define strStatsPosts "Posts"
#define strStatsEmail "Email"
#define strStatsFeedback "Feedback"
#define strStatsNewUsers "NewUsers"
/****************************************************************************/
/* Daily statistics (reset daily) */
/****************************************************************************/
char* dstats_fname(scfg_t* cfg, uint node, char* path, size_t size)
{
safe_snprintf(path, size, "%sdsts.ini", node > 0 && node <= cfg->sys_nodes ? cfg->node_path[node-1] : cfg->ctrl_dir);
return path;
}
/****************************************************************************/
/* Cumulative statistics (log) */
/****************************************************************************/
char* cstats_fname(scfg_t* cfg, uint node, char* path, size_t size)
{
safe_snprintf(path, size, "%scsts.tab", node > 0 && node <= cfg->sys_nodes ? cfg->node_path[node-1] : cfg->ctrl_dir);
return path;
}
/****************************************************************************/
/****************************************************************************/
FILE* fopen_dstats(scfg_t* cfg, uint node, BOOL for_write)
{
char path[MAX_PATH+1];
dstats_fname(cfg, node, path, sizeof(path));
return iniOpenFile(path, for_write);
}
/****************************************************************************/
/****************************************************************************/
FILE* fopen_cstats(scfg_t* cfg, uint node, BOOL for_write)
{
char path[MAX_PATH+1];
cstats_fname(cfg, node, path, sizeof(path));
return fnopen(NULL, path, for_write ? O_CREAT|O_WRONLY|O_APPEND : O_RDONLY);
}
/****************************************************************************/
/****************************************************************************/
BOOL fclose_dstats(FILE* fp)
{
return iniCloseFile(fp);
}
/****************************************************************************/
/* Reads data from dsts.dab into stats structure */
/* If node is zero, reads from ctrl\dsts.dab, otherwise from each node */
/****************************************************************************/
BOOL getstats(scfg_t* cfg, char node, stats_t* stats)
BOOL fclose_cstats(FILE* fp)
{
char str[MAX_PATH+1];
int file;
return fclose(fp) == 0;
}
static void gettotals(str_list_t ini, const char* section, totals_t* stats)
{
stats->logons = iniGetLongInt(ini, section, strStatsLogons, 0);
stats->timeon = iniGetLongInt(ini, section, strStatsTimeon, 0);
stats->uls = iniGetLongInt(ini, section, strStatsUploads, 0);
stats->ulb = iniGetBytes(ini, section, strStatsUploadBytes, /* unit: */1, 0);
stats->dls = iniGetLongInt(ini, section, strStatsDownloads, 0);
stats->dlb = iniGetBytes(ini, section, strStatsDownloadBytes, /* unit: */1, 0);
stats->posts = iniGetLongInt(ini, section, strStatsPosts, 0);
stats->email = iniGetLongInt(ini, section, strStatsEmail, 0);
stats->fbacks = iniGetLongInt(ini, section, strStatsFeedback, 0);
stats->nusers = iniGetLongInt(ini, section, strStatsNewUsers, 0);
}
/****************************************************************************/
/* Reads data from dsts.ini into stats structure */
/* If node is zero, reads from ctrl/dsts.ini, otherwise from each node */
/****************************************************************************/
BOOL fread_dstats(FILE* fp, stats_t* stats)
{
str_list_t ini;
if(fp == NULL)
return FALSE;
memset(stats, 0, sizeof(*stats));
ini = iniReadFile(fp);
stats->date = iniGetDateTime(ini, NULL, strStatsDate, 0);
gettotals(ini, strStatsToday, &stats->today);
gettotals(ini, strStatsTotal, &stats->total);
iniFreeStringList(ini);
return TRUE;
}
/****************************************************************************/
/* Reads data from dsts.ini into stats structure */
/* If node is zero, reads from ctrl/dsts.ini, otherwise from each node */
/****************************************************************************/
BOOL getstats(scfg_t* cfg, uint node, stats_t* stats)
{
char path[MAX_PATH+1];
BOOL result;
memset(stats, 0, sizeof(*stats));
dstats_fname(cfg, node, path, sizeof(path));
FILE* fp = fnopen(NULL, path, O_RDONLY);
if(fp == NULL) {
int file;
if(fexist(path))
return FALSE;
// Upgrading from v3.19?
struct { /* System/Node Statistics */
uint32_t date, /* When last rolled-over */
logons, /* Total Logons on System */
ltoday, /* Total Logons Today */
timeon, /* Total Time on System */
ttoday, /* Total Time Today */
uls, /* Total Uploads Today */
ulb, /* Total Upload Bytes Today */
dls, /* Total Downloads Today */
dlb, /* Total Download Bytes Today */
ptoday, /* Total Posts Today */
etoday, /* Total Emails Today */
ftoday; /* Total Feedbacks Today */
uint16_t nusers; /* Total New Users Today */
} legacy_stats;
SAFEPRINTF(path,"%sdsts.dab",node ? cfg->node_path[node-1] : cfg->ctrl_dir);
if((file=nopen(path,O_RDONLY))==-1) {
return(FALSE);
}
read(file, &legacy_stats, sizeof(legacy_stats));
close(file);
sprintf(str,"%sdsts.dab",node ? cfg->node_path[node-1] : cfg->ctrl_dir);
if((file=nopen(str,O_RDONLY))==-1) {
return(FALSE);
stats->date = LE_INT(legacy_stats.date);
stats->logons = LE_INT(legacy_stats.logons);
stats->ltoday = LE_INT(legacy_stats.ltoday);
stats->timeon = LE_INT(legacy_stats.timeon);
stats->ttoday = LE_INT(legacy_stats.ttoday);
stats->uls = LE_INT(legacy_stats.uls);
stats->ulb = LE_INT(legacy_stats.ulb);
stats->dls = LE_INT(legacy_stats.dls);
stats->dlb = LE_INT(legacy_stats.dlb);
stats->ptoday = LE_INT(legacy_stats.ptoday);
stats->etoday = LE_INT(legacy_stats.etoday);
stats->ftoday = LE_INT(legacy_stats.ftoday);
stats->nusers = LE_INT(legacy_stats.nusers);
return TRUE;
}
lseek(file,4L,SEEK_SET); /* Skip update time/date */
/* TODO: Direct read of unpacked struct */
read(file,stats,sizeof(stats_t));
close(file);
return(TRUE);
result = fread_dstats(fp, stats);
fclose(fp);
return result;
}
static void settotals(str_list_t* ini, const char* section, const totals_t* stats)
{
iniSetLongInt(ini, section, strStatsLogons, stats->logons, /* style: */NULL);
iniSetLongInt(ini, section, strStatsTimeon, stats->timeon, /* style: */NULL);
iniSetLongInt(ini, section, strStatsUploads, stats->uls, /* style: */NULL);
iniSetBytes(ini, section, strStatsUploadBytes, /* unit: */1, stats->ulb, /* style: */NULL);
iniSetLongInt(ini, section, strStatsDownloads, stats->dls, /* style: */NULL);
iniSetBytes(ini, section, strStatsDownloadBytes, /* unit: */1, stats->dlb, /* style: */NULL);
iniSetLongInt(ini, section, strStatsPosts, stats->posts, /* style: */NULL);
iniSetLongInt(ini, section, strStatsEmail, stats->email, /* style: */NULL);
iniSetLongInt(ini, section, strStatsFeedback, stats->fbacks, /* style: */NULL);
iniSetLongInt(ini, section, strStatsNewUsers, stats->nusers, /* style: */NULL);
}
/****************************************************************************/
/****************************************************************************/
BOOL fwrite_dstats(FILE* fp, const stats_t* stats)
{
BOOL result;
str_list_t ini;
if(fp == NULL)
return FALSE;
ini = iniReadFile(fp);
iniSetDateTime(&ini, NULL, strStatsDate, /* include_time: */FALSE, stats->date, /* style: */NULL);
settotals(&ini, strStatsToday, &stats->today);
settotals(&ini, strStatsTotal, &stats->total);
result = iniWriteFile(fp, ini);
iniFreeStringList(ini);
return result;
}
/****************************************************************************/
/* If node is zero, reads from ctrl/dsts.ini, otherwise from each node */
/****************************************************************************/
BOOL putstats(scfg_t* cfg, uint node, const stats_t* stats)
{
BOOL result;
FILE* fp = fopen_dstats(cfg, node, /* for_write: */TRUE);
if(fp == NULL)
return FALSE;
result = fwrite_dstats(fp, stats);
iniCloseFile(fp);
return result;
}
/****************************************************************************/
/****************************************************************************/
void rolloverstats(stats_t* stats)
{
stats->date = time(NULL);
memset(&stats->today, 0, sizeof(stats->today));
}
/****************************************************************************/
/****************************************************************************/
BOOL fwrite_cstats(FILE* fp, const stats_t* stats)
{
int len;
char pad[LEN_CSTATS_RECORD];
memset(pad, '\t', sizeof(pad) - 1);
TERMINATE(pad);
fseek(fp, 0, SEEK_END);
if(ftell(fp) == 0) {
len = fprintf(fp
,"%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t"
,strStatsDate
,strStatsLogons
,strStatsTimeon
,strStatsUploads
,strStatsUploadBytes
,strStatsDownloads
,strStatsDownloadBytes
,strStatsPosts
,strStatsEmail
,strStatsFeedback
,strStatsNewUsers
);
if(len >= sizeof(pad))
return FALSE;
if(fprintf(fp, "%.*s\n", sizeof(pad) - (len + 1), pad) <= 0)
return FALSE;
}
len = fprintf(fp
,"%" PRIu32 "\t%lu\t%lu\t%lu\t%" PRIu64 "\t%lu\t%" PRIu64 "\t%lu\t%lu\t%lu\t%lu\t"
,time_to_isoDate(stats->date)
,stats->ltoday
,stats->ttoday
,stats->uls
,stats->ulb
,stats->dls
,stats->dlb
,stats->ptoday
,stats->etoday
,stats->ftoday
,stats->nusers
);
if(len >= sizeof(pad))
return FALSE;
return fprintf(fp, "%.*s\n", sizeof(pad) - (len + 1), pad) > 0;
}
/****************************************************************************/
......@@ -86,48 +336,100 @@ ulong getposts(scfg_t* cfg, uint subnum)
return result;
}
BOOL inc_sys_upload_stats(scfg_t* cfg, ulong files, ulong bytes)
{
char str[MAX_PATH+1];
int file;
uint32_t val; // TODO: support > 4GB uploads in a day
SAFEPRINTF(str,"%sdsts.dab",cfg->ctrl_dir);
if((file=nopen(str,O_RDWR))==-1)
return(FALSE);
(void)lseek(file,20L,SEEK_SET); /* Skip timestamp, logons and logons today */
(void)read(file,&val,4); /* Uploads today */
val+=files;
(void)lseek(file,-4L,SEEK_CUR);
(void)write(file,&val,4);
(void)read(file,&val,4); /* Upload bytes today */
val+=bytes;
(void)lseek(file,-4L,SEEK_CUR);
(void)write(file,&val,4);
close(file);
return(TRUE);
}
BOOL inc_sys_download_stats(scfg_t* cfg, ulong files, ulong bytes)
{
char str[MAX_PATH+1];
int file;
uint32_t val; // TODO: support > 4GB downloads in a day
SAFEPRINTF(str,"%sdsts.dab",cfg->ctrl_dir);
if((file=nopen(str,O_RDWR))==-1)
return(FALSE);
(void)lseek(file,28L,SEEK_SET); /* Skip timestamp, logons and logons today */
(void)read(file,&val,4); /* Downloads today */
val+=files;
(void)lseek(file,-4L,SEEK_CUR);
(void)write(file,&val,4);
(void)read(file,&val,4); /* Download bytes today */
val+=bytes;
(void)lseek(file,-4L,SEEK_CUR);
(void)write(file,&val,4);
close(file);
return(TRUE);
static void inc_xfer_stat_keys(str_list_t* ini, const char* section, ulong files, uint64_t bytes, const char* files_key, const char* bytes_key)
{
iniSetLongInt(ini, section, files_key, iniGetLongInt(*ini, section, files_key, 0) + files, /* style: */NULL);
iniSetBytes(ini, section, bytes_key, /* unit: */1, iniGetBytes(*ini, section, bytes_key, /* unit: */1, 0) + bytes, /* style: */NULL);
}
static BOOL inc_xfer_stats(scfg_t* cfg, uint node, ulong files, uint64_t bytes, const char* files_key, const char* bytes_key)
{
FILE* fp;
str_list_t ini;
BOOL result = FALSE;
fp = fopen_dstats(cfg, node, /* for_write: */TRUE);
if(fp == NULL)
return FALSE;
ini = iniReadFile(fp);
inc_xfer_stat_keys(&ini, strStatsTotal, files, bytes, files_key, bytes_key);
inc_xfer_stat_keys(&ini, strStatsToday, files, bytes, files_key, bytes_key);
result = iniWriteFile(fp, ini) > 0;
fclose_dstats(fp);
iniFreeStringList(ini);
return result;
}
static BOOL inc_all_xfer_stats(scfg_t* cfg, ulong files, uint64_t bytes, const char* files_key, const char* bytes_key)
{
BOOL success = TRUE;
if(cfg->node_num)
success = inc_xfer_stats(cfg, cfg->node_num, files, bytes, files_key, bytes_key);
return inc_xfer_stats(cfg, /* system = node_num 0 */0, files, bytes, files_key, bytes_key) && success;
}
BOOL inc_upload_stats(scfg_t* cfg, ulong files, uint64_t bytes)
{
return inc_all_xfer_stats(cfg, files, bytes, strStatsUploads, strStatsUploadBytes);
}
BOOL inc_download_stats(scfg_t* cfg, ulong files, uint64_t bytes)
{
return inc_all_xfer_stats(cfg, files, bytes, strStatsDownloads, strStatsDownloadBytes);
}
static BOOL inc_post_stat(scfg_t* cfg, uint node, uint count)
{
FILE* fp;
str_list_t ini;
BOOL result = FALSE;
fp = fopen_dstats(cfg, node, /* for_write: */TRUE);
if(fp == NULL)
return FALSE;
ini = iniReadFile(fp);
iniSetLongInt(&ini, strStatsToday, strStatsPosts, iniGetLongInt(ini, strStatsToday, strStatsPosts, 0) + count, /* style: */NULL);
iniSetLongInt(&ini, strStatsTotal, strStatsPosts, iniGetLongInt(ini, strStatsTotal, strStatsPosts, 0) + count, /* style: */NULL);
result = iniWriteFile(fp, ini) > 0;
fclose_dstats(fp);
iniFreeStringList(ini);
return result;
}
BOOL inc_post_stats(scfg_t* cfg, uint count)
{
BOOL success = TRUE;
if(cfg->node_num)
success = inc_post_stat(cfg, cfg->node_num, count);
return inc_post_stat(cfg, /* system = node_num 0 */0, count) && success;
}
static BOOL inc_email_stat(scfg_t* cfg, uint node, uint count, BOOL feedback)
{
FILE* fp;
str_list_t ini;
BOOL result = FALSE;
const char* key = feedback ? strStatsFeedback : strStatsEmail;
fp = fopen_dstats(cfg, node, /* for_write: */TRUE);
if(fp == NULL)
return FALSE;
ini = iniReadFile(fp);
iniSetLongInt(&ini, strStatsToday, key, iniGetLongInt(ini, strStatsToday, key, 0) + count, /* style: */NULL);
iniSetLongInt(&ini, strStatsTotal, key, iniGetLongInt(ini, strStatsTotal, key, 0) + count, /* style: */NULL);
result = iniWriteFile(fp, ini) > 0;
fclose_dstats(fp);
iniFreeStringList(ini);
return result;
}
BOOL inc_email_stats(scfg_t* cfg, uint count, BOOL feedback)
{
BOOL success = TRUE;
if(cfg->node_num)
success = inc_email_stat(cfg, cfg->node_num, count, feedback);
return inc_email_stat(cfg, /* system = node_num 0 */0, count, feedback) && success;
}
......@@ -20,6 +20,7 @@
#ifndef _GETSTATS_H_
#define _GETSTATS_H_
#include <stdio.h> // FILE
#include "scfgdefs.h" // scfg_t
#include "dllexport.h"
......@@ -27,13 +28,26 @@
extern "C" {
#endif
DLLEXPORT BOOL getstats(scfg_t* cfg, char node, stats_t* stats);
DLLEXPORT ulong getposts(scfg_t* cfg, uint subnum);
DLLEXPORT long getfiles(scfg_t* cfg, uint dirnum);
DLLEXPORT BOOL inc_sys_upload_stats(scfg_t*, ulong files, ulong bytes);
DLLEXPORT BOOL inc_sys_download_stats(scfg_t*, ulong files, ulong bytes);
DLLEXPORT char* dstats_fname(scfg_t*, uint node, char* path, size_t);
DLLEXPORT char* cstats_fname(scfg_t*, uint node, char* path, size_t);
DLLEXPORT FILE* fopen_dstats(scfg_t*, uint node, BOOL for_write);
DLLEXPORT FILE* fopen_cstats(scfg_t*, uint node, BOOL for_write);
DLLEXPORT BOOL fclose_cstats(FILE*);
DLLEXPORT BOOL fclose_dstats(FILE*);
DLLEXPORT BOOL fread_dstats(FILE*, stats_t*);
DLLEXPORT BOOL fwrite_dstats(FILE*, const stats_t*);
DLLEXPORT BOOL fwrite_cstats(FILE*, const stats_t*);
DLLEXPORT BOOL getstats(scfg_t*, uint node, stats_t*);
DLLEXPORT BOOL putstats(scfg_t*, uint node, const stats_t*);
DLLEXPORT ulong getposts(scfg_t*, uint subnum);
DLLEXPORT long getfiles(scfg_t*, uint dirnum);
DLLEXPORT void rolloverstats(stats_t*);
DLLEXPORT BOOL inc_post_stats(scfg_t*, uint count);
DLLEXPORT BOOL inc_email_stats(scfg_t*, uint count, BOOL feedback);
DLLEXPORT BOOL inc_upload_stats(scfg_t*, ulong files, uint64_t bytes);
DLLEXPORT BOOL inc_download_stats(scfg_t*, ulong files, uint64_t bytes);
#ifdef __cplusplus
}
#endif
#endif /* Don't add anything after this line */
\ No newline at end of file
#endif /* Don't add anything after this line */
......@@ -641,13 +641,13 @@ static JSBool js_sysstats_get(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
*vp=UINT_TO_JSVAL(stats.uls);
break;
case SYSSTAT_PROP_ULB:
*vp=UINT_TO_JSVAL(stats.ulb);
*vp=DOUBLE_TO_JSVAL((double)stats.ulb);
break;
case SYSSTAT_PROP_DLS:
*vp=UINT_TO_JSVAL(stats.dls);
break;
case SYSSTAT_PROP_DLB:
*vp=UINT_TO_JSVAL(stats.dlb);
*vp=DOUBLE_TO_JSVAL((double)stats.dlb);
break;
case SYSSTAT_PROP_PTODAY:
*vp=UINT_TO_JSVAL(stats.ptoday);
......
......@@ -564,109 +564,68 @@ bool sbbs_t::logon()
}
/****************************************************************************/
/* Checks the system dsts.dab to see if it is a new day, if it is, all the */
/* nodes' and the system's csts.dab are added to, and the dsts.dab's daily */
/* stats are cleared. Also increments the logon values in dsts.dab if */
/* Checks the system dsts.ini to see if it is a new day, if it is, all the */
/* nodes' and the system's csts.tab are added to, and the dsts.ini's daily */
/* stats are cleared. Also increments the logon values in dsts.ini if */
/* applicable. */
/****************************************************************************/
ulong sbbs_t::logonstats()
{
char str[MAX_PATH+1];
int dsts,csts;
char msg[256];
char path[MAX_PATH+1];
FILE* csts;
FILE* dsts;
uint i;
time32_t update32_t=0;
time_t update_t=0;
time32_t now32;
stats_t stats;
node_t node;
struct tm tm, update_tm;
sys_status&=~SS_DAILY;
memset(&stats,0,sizeof(stats));
safe_snprintf(str, sizeof(str), "%sdsts.dab",cfg.ctrl_dir);
if((dsts=nopen(str,O_RDWR))==-1) {
errormsg(WHERE,ERR_OPEN,str,O_RDWR);
return(0L);
}
read(dsts,&update32_t,4); /* Last updated */
update_t=update32_t;
read(dsts,&stats.logons,4); /* Total number of logons on system */
close(dsts);
getstats(&cfg, 0, &stats);
now=time(NULL);
now32=(time32_t)now;
if(update_t>now+(24L*60L*60L)) /* More than a day in the future? */
errormsg(WHERE,ERR_CHK,"Daily stats time stamp",(ulong)update_t);
if(localtime_r(&update_t,&update_tm)==NULL)
if(stats.date > now+(24L*60L*60L)) /* More than a day in the future? */
errormsg(WHERE,ERR_CHK,"Daily stats date/time stamp", (ulong)stats.date);
if(localtime_r(&stats.date, &update_tm)==NULL)
return(0);
if(localtime_r(&now,&tm)==NULL)
return(0);
if((tm.tm_mday>update_tm.tm_mday && tm.tm_mon==update_tm.tm_mon)
|| tm.tm_mon>update_tm.tm_mon || tm.tm_year>update_tm.tm_year) {
safe_snprintf(str, sizeof(str), "New Day - Prev: %s ",timestr(update_t));
logentry("!=",str);
safe_snprintf(msg, sizeof(msg), "New Day - Prev: %s ",timestr(stats.date));
logline(LOG_NOTICE, "!=", msg);
sys_status|=SS_DAILY; /* New Day !!! */
safe_snprintf(str, sizeof(str), "%slogon.lst",cfg.data_dir); /* Truncate logon list */
if((dsts=nopen(str,O_TRUNC|O_CREAT|O_WRONLY))==-1) {
errormsg(WHERE,ERR_OPEN,str,O_TRUNC|O_CREAT|O_WRONLY);
safe_snprintf(path, sizeof(path), "%slogon.lst",cfg.data_dir); /* Truncate logon list (LEGACY) */
int file;
if((file=nopen(path,O_TRUNC|O_CREAT|O_WRONLY))==-1) {
errormsg(WHERE,ERR_OPEN,path,O_TRUNC|O_CREAT|O_WRONLY);
return(0L);
}
close(dsts);
close(file);
for(i=0;i<=cfg.sys_nodes;i++) {
if(i) { /* updating a node */
getnodedat(i,&node,1);
node.misc|=NODE_EVENT;
putnodedat(i,&node);
}
safe_snprintf(str, sizeof(str), "%sdsts.dab",i ? cfg.node_path[i-1] : cfg.ctrl_dir);
if((dsts=nopen(str,O_RDWR))==-1) /* node doesn't have stats yet */
if((dsts = fopen_dstats(&cfg, i, /* for_write: */TRUE)) == NULL) /* doesn't have stats yet */
continue;
if((csts = fopen_cstats(&cfg, i, /* for_write: */TRUE)) == NULL) {
fclose_dstats(dsts);
errormsg(WHERE, ERR_OPEN, "csts.tab", i);
continue;
safe_snprintf(str, sizeof(str), "%scsts.dab",i ? cfg.node_path[i-1] : cfg.ctrl_dir);
if((csts=nopen(str,O_WRONLY|O_APPEND|O_CREAT))==-1) {
close(dsts);
errormsg(WHERE,ERR_OPEN,str,O_WRONLY|O_APPEND|O_CREAT);
continue;
}
lseek(dsts,8L,SEEK_SET); /* Skip time and logons */
write(csts,&now32,4);
read(dsts,&stats.ltoday,4);
write(csts,&stats.ltoday,4);
lseek(dsts,4L,SEEK_CUR); /* Skip total time on */
read(dsts,&stats.ttoday,4);
write(csts,&stats.ttoday,4);
read(dsts,&stats.uls,4);
write(csts,&stats.uls,4);
read(dsts,&stats.ulb,4);
write(csts,&stats.ulb,4);
read(dsts,&stats.dls,4);
write(csts,&stats.dls,4);
read(dsts,&stats.dlb,4);
write(csts,&stats.dlb,4);
read(dsts,&stats.ptoday,4);
write(csts,&stats.ptoday,4);
read(dsts,&stats.etoday,4);
write(csts,&stats.etoday,4);
read(dsts,&stats.ftoday,4);
write(csts,&stats.ftoday,4);
close(csts);
lseek(dsts,0L,SEEK_SET); /* Go back to beginning */
write(dsts,&now32,4); /* Update time stamp */
lseek(dsts,4L,SEEK_CUR); /* Skip total logons */
stats.ltoday=0;
write(dsts,&stats.ltoday,4); /* Logons today to 0 */
lseek(dsts,4L,SEEK_CUR); /* Skip total time on */
stats.ttoday=0; /* Set all other today variables to 0 */
write(dsts,&stats.ttoday,4); /* Time on today to 0 */
write(dsts,&stats.ttoday,4); /* Uploads today to 0 */
write(dsts,&stats.ttoday,4); /* U/L Bytes today */
write(dsts,&stats.ttoday,4); /* Download today */
write(dsts,&stats.ttoday,4); /* Download bytes */
write(dsts,&stats.ttoday,4); /* Posts today */
write(dsts,&stats.ttoday,4); /* Emails today */
write(dsts,&stats.ttoday,4); /* Feedback today */
write(dsts,&stats.ttoday,2); /* New users Today */
close(dsts);
fread_dstats(dsts, &stats);
stats.date = time(NULL);
fwrite_cstats(csts, &stats);
fclose_cstats(csts);
rolloverstats(&stats);
fwrite_dstats(dsts, &stats);
fclose_dstats(dsts);
}
}
......@@ -680,22 +639,17 @@ ulong sbbs_t::logonstats()
return(0);
for(i=0;i<2;i++) {
safe_snprintf(str, sizeof(str), "%sdsts.dab",i ? cfg.ctrl_dir : cfg.node_dir);
if((dsts=nopen(str,O_RDWR))==-1) {
errormsg(WHERE,ERR_OPEN,str,O_RDWR);
FILE* fp = fopen_dstats(&cfg, i ? 0 : cfg.node_num, /* for_write: */TRUE);
if(fp == NULL) {
errormsg(WHERE, ERR_OPEN, "dsts.ini", i);
return(0L);
}
lseek(dsts,4L,SEEK_SET); /* Skip time stamp */
read(dsts,&stats.logons,4);
read(dsts,&stats.ltoday,4);
stats.logons++;
stats.ltoday++;
lseek(dsts,4L,SEEK_SET); /* Rewind back and overwrite */
write(dsts,&stats.logons,4);
write(dsts,&stats.ltoday,4);
close(dsts);
fread_dstats(fp, &stats);
stats.today.logons++;
stats.total.logons++;
fwrite_dstats(fp, &stats);
fclose_dstats(fp);
}
return(stats.logons);
}
......@@ -4228,42 +4228,34 @@ void sbbs_t::catsyslog(int crash)
void sbbs_t::logoffstats()
{
char str[MAX_PATH+1];
int i,file;
int i;
stats_t stats;
if(REALSYSOP && !(cfg.sys_misc&SM_SYSSTAT))
return;
if(useron.rest&FLAG('Q')) /* Don't count QWKnet nodes */
return;
now = time(NULL);
if(now <= logontime) {
lprintf(LOG_ERR, "Logoff time (%lu) <= logon time (%lu)", (ulong)now, (ulong)logontime);
return;
}
ulong minutes_used = (ulong)(now-logontime)/60;
if(minutes_used < 1)
return;
for(i=0;i<2;i++) {
SAFEPRINTF(str,"%sdsts.dab",i ? cfg.ctrl_dir : cfg.node_dir);
if((file=nopen(str,O_RDWR))==-1) {
errormsg(WHERE,ERR_OPEN,str,O_RDWR);
return;
}
memset(&stats,0,sizeof(stats));
lseek(file,4L,SEEK_SET); /* Skip timestamp, logons and logons today */
read(file,&stats,sizeof(stats));
if(!(useron.rest&FLAG('Q'))) { /* Don't count QWKnet nodes */
stats.timeon+=(uint32_t)(now-logontime)/60;
stats.ttoday+=(uint32_t)(now-logontime)/60;
stats.ptoday+=logon_posts;
FILE* fp = fopen_dstats(&cfg, i ? 0 : cfg.node_num, /* for_write: */TRUE);
if(fp == NULL)
continue;
if(fread_dstats(fp, &stats)) {
stats.total.timeon += minutes_used;
stats.today.timeon += minutes_used;
fwrite_dstats(fp, &stats);
}
stats.uls+=(uint32_t)logon_uls;
stats.ulb+=(uint32_t)logon_ulb;
// logon_dls and logons_dlb are now handled in user_downloaded_file()
stats.etoday+=logon_emails;
stats.ftoday+=logon_fbacks;
#if 0 // This is now handled in newuserdat()
if(sys_status&SS_NEWUSER)
stats.nusers++;
#endif
lseek(file,4L,SEEK_SET);
write(file,&stats,sizeof(stats));
close(file);
fclose_dstats(fp);
}
}
......@@ -4375,7 +4367,7 @@ void node_thread(void* arg)
node_socket[sbbs->cfg.node_num-1]=INVALID_SOCKET;
sbbs->logout();
sbbs->logoffstats(); /* Updates both system and node dsts.dab files */
sbbs->logoffstats(); /* Updates both system and node dsts.ini (daily statistics) files */
SAFEPRINTF(str, "%sclient.ini", sbbs->cfg.node_dir);
FILE* fp = fopen(str, "at");
......@@ -4787,7 +4779,6 @@ void bbs_thread(void* arg)
socklen_t client_addr_len;
SOCKET client_socket=INVALID_SOCKET;
int i;
int file;
int result;
time_t t;
time_t start;
......@@ -4961,7 +4952,7 @@ void bbs_thread(void* arg)
lprintf(LOG_INFO,"Verifying/creating data directories");
make_data_dirs(&scfg);
/* Create missing node directories and dsts.dab files */
/* Create missing node directories */
lprintf(LOG_INFO,"Verifying/creating node directories");
for(i=0;i<=scfg.sys_nodes;i++) {
if(i) {
......@@ -4972,17 +4963,69 @@ void bbs_thread(void* arg)
return;
}
}
SAFEPRINTF(str,"%sdsts.dab",i ? scfg.node_path[i-1] : scfg.ctrl_dir);
if(flength(str)<DSTSDABLEN) {
if((file=sopen(str,O_WRONLY|O_CREAT|O_APPEND, SH_DENYNO, DEFFILEMODE))==-1) {
lprintf(LOG_CRIT,"!ERROR %d (%s) creating %s",errno, strerror(errno), str);
cleanup(1);
return;
// Convert old dsts.dab -> dsts.ini
if(!fexist(dstats_fname(&scfg, i, str, sizeof(str)))) {
lprintf(LOG_NOTICE, "Auto-upgrading daily statistics data file: %s", str);
stats_t stats;
getstats(&scfg, i, &stats);
putstats(&scfg, i, &stats);
}
if(!fexist(cstats_fname(&scfg, i, str, sizeof(str)))) {
ulong record = 0;
lprintf(LOG_NOTICE, "Auto-upgrading cumulative statistics log file: %s", str);
stats_t stats;
FILE* out = fopen_cstats(&scfg, i, /* write: */TRUE);
if(out == NULL) {
lprintf(LOG_ERR, "!ERROR %d (%s) creating: %s", errno, strerror(errno), str);
continue;
}
while(filelength(file)<DSTSDABLEN)
if(write(file,"\0",1)!=1)
break; /* Create NULL system dsts.dab */
close(file);
SAFEPRINTF(str, "%scsts.dab", i ? scfg.node_path[i-1] : scfg.ctrl_dir);
FILE* in = fopen(str, "rb");
if(in == NULL)
lprintf(LOG_ERR, "!ERROR %d (%s) creating: %s", errno, strerror(errno), str);
else {
ZERO_VAR(stats);
while(!feof(in)) {
struct { /* System/Node Statistics */
uint32_t date, /* When last rolled-over */
logons, /* Total Logons on System */
timeon, /* Total Time on System */
uls, /* Total Uploads Today */
ulb, /* Total Upload Bytes Today */
dls, /* Total Downloads Today */
dlb, /* Total Download Bytes Today */
ptoday, /* Total Posts Today */
etoday, /* Total Emails Today */
ftoday; /* Total Feedbacks Today */
} legacy_stats;
if(fread(&legacy_stats, sizeof(legacy_stats), 1, in) != 1)
break;
record++;
if(legacy_stats.logons > 1000
|| legacy_stats.timeon > 10000) {
lprintf(LOG_WARNING, "Skipped corrupted record #%lu in %s", record, str);
continue;
}
ZERO_VAR(stats);
stats.date = legacy_stats.date;
stats.today.logons = legacy_stats.logons;
stats.today.timeon = legacy_stats.timeon;
stats.today.uls = legacy_stats.uls;
stats.today.ulb = legacy_stats.ulb;
stats.today.dls = legacy_stats.dls;
stats.today.dlb = legacy_stats.dlb;
stats.today.posts = legacy_stats.ptoday;
stats.today.email = legacy_stats.etoday;
stats.today.fbacks = legacy_stats.ftoday;
if(!fwrite_cstats(out, &stats)) {
lprintf(LOG_ERR, "!WRITE ERROR, %s line %d", __FILE__, __LINE__);
break;
}
}
fclose(in);
}
fclose_cstats(out);
lprintf(LOG_INFO, "Done (%lu daily-statistics records converted)", record);
}
}
......
......@@ -158,6 +158,7 @@
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<ClCompile Include="getstats.c" />
<ClCompile Include="makeuser.c">
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
......
......@@ -450,8 +450,6 @@ typedef enum { /* Values for xtrn_t.event */
#define EDIT_TABSIZE 4 /* Tab size for internal message/line editor */
#define DSTSDABLEN 50 /* Length of dsts.dab file */
/* Console I/O Bits (console) */
#define CON_R_ECHO (1<<0) /* Echo remotely */
#define CON_R_ECHOX (1<<1) /* Echo X's to remote user */
......@@ -1050,21 +1048,46 @@ typedef fidoaddr_t faddr_t; /* defined in smbdefs.h */
typedef smbfile_t file_t; /* defined in smbdefs.h */
typedef struct { /* System/Node Statistics */
uint32_t logons, /* Total Logons on System */
ltoday, /* Total Logons Today */
timeon, /* Total Time on System */
ttoday, /* Total Time Today */
uls, /* Total Uploads Today */
ulb, /* Total Upload Bytes Today */
dls, /* Total Downloads Today */
dlb, /* Total Download Bytes Today */
ptoday, /* Total Posts Today */
etoday, /* Total Emails Today */
ftoday; /* Total Feedbacks Today */
uint16_t nusers; /* Total New Users Today */
ulong logons;
ulong timeon;
ulong uls;
uint64_t ulb;
ulong dls;
uint64_t dlb;
ulong posts;
ulong email;
ulong fbacks;
ulong nusers;
} totals_t;
typedef struct { /* System/Node Statistics */
time_t date; /* When stats were last rolled-over */
union {
totals_t total;
struct { // legacy names
ulong logons;
ulong timeon;
};
};
union {
totals_t today;
struct { // legacy names
ulong ltoday;
ulong ttoday;
ulong uls;
uint64_t ulb;
ulong dls;
uint64_t dlb;
ulong ptoday;
ulong etoday;
ulong ftoday;
ulong nusers;
};
};
} stats_t;
#define LEN_CSTATS_RECORD 128
typedef struct { /* Sub-board scan information */
uint16_t cfg; /* User's configuration */
uint32_t ptr; /* New-message pointer */
......
......@@ -165,6 +165,7 @@
</ClCompile>
<ClCompile Include="filedat.c" />
<ClCompile Include="getmail.c" />
<ClCompile Include="getstats.c" />
<ClCompile Include="msg_id.c">
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
......
......@@ -164,6 +164,7 @@
<AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<ClCompile Include="..\getstats.c" />
<ClCompile Include="..\msgdate.c" />
<ClCompile Include="..\userdat.c" />
<ClCompile Include="scfg.c">
......
......@@ -993,9 +993,9 @@ void sbbs_t::node_stats(uint node_num)
bprintf(text[StatsLogonsToday],ultoac(stats.ltoday,tmp));
bprintf(text[StatsTotalTime],ultoac(stats.timeon,tmp));
bprintf(text[StatsTimeToday],ultoac(stats.ttoday,tmp));
bprintf(text[StatsUploadsToday],ultoac(stats.ulb,tmp)
bprintf(text[StatsUploadsToday],u64toac(stats.ulb,tmp)
,stats.uls);
bprintf(text[StatsDownloadsToday],ultoac(stats.dlb,tmp)
bprintf(text[StatsDownloadsToday],u64toac(stats.dlb,tmp)
,stats.dls);
bprintf(text[StatsPostsToday],ultoac(stats.ptoday,tmp));
bprintf(text[StatsEmailsToday],ultoac(stats.etoday,tmp));
......@@ -1013,9 +1013,9 @@ void sbbs_t::sys_stats(void)
bprintf(text[StatsLogonsToday],ultoac(stats.ltoday,tmp));
bprintf(text[StatsTotalTime],ultoac(stats.timeon,tmp));
bprintf(text[StatsTimeToday],ultoac(stats.ttoday,tmp));
bprintf(text[StatsUploadsToday],ultoac(stats.ulb,tmp)
bprintf(text[StatsUploadsToday],u64toac(stats.ulb,tmp)
,stats.uls);
bprintf(text[StatsDownloadsToday],ultoac(stats.dlb,tmp)
bprintf(text[StatsDownloadsToday],u64toac(stats.dlb,tmp)
,stats.dls);
bprintf(text[StatsPostsToday],ultoac(stats.ptoday,tmp));
bprintf(text[StatsEmailsToday],ultoac(stats.etoday,tmp));
......
......@@ -1021,7 +1021,7 @@ const char * const text_defaults[TOTAL_TEXT]={
,"\x01\x6e\x01\x68\x01\x62\x0d\x0a\x46\x69\x64\x6f\x4e\x65\x74\x20\x45\x63\x68\x6f\x4d\x61\x69\x6c\x20\x63\x6f\x6e\x66\x65\x72\x65"
"\x6e\x63\x65\x20\x75\x73\x69\x6e\x67\x20\x74\x68\x65\x20\x6f\x72\x69\x67\x69\x6e\x20\x6c\x69\x6e\x65\x3a\x01\x6e\x0d\x0a\x20\x2a"
"\x20\x4f\x72\x69\x67\x69\x6e\x3a\x20\x25\x73\x20\x28\x25\x73\x29\x0d\x0a" // 609 SubInfoFidoNet
,"\x56\x69\x65\x77\x20\x73\x75\x62\x2d\x62\x6f\x61\x72\x64\x20\x69\x6e\x66\x6f\x72\x6d\x61\x74\x69\x6f\x6e\x20\x66\x69\x6c\x65" // 610 SubInfoViewFileQ
,"" // 610 SubInfoViewFileQ
,"\x0d\x0a\x01\x67\x01\x68\x44\x69\x72\x65\x63\x74\x6f\x72\x79\x20\x49\x6e\x66\x6f\x72\x6d\x61\x74\x69\x6f\x6e\x3a\x0d\x0a\x0d\x0a"
"\x01\x6e" // 611 DirInfoHdr
,"\x01\x6e\x01\x68\x01\x62\x4c\x6f\x6e\x67\x20\x4e\x61\x6d\x65\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3a\x20"
......@@ -1032,8 +1032,7 @@ const char * const text_defaults[TOTAL_TEXT]={
"\x01\x63\x25\x73\x0d\x0a" // 614 DirInfoAllowedExts
,"\x01\x6e\x01\x68\x01\x62\x4d\x61\x78\x69\x6d\x75\x6d\x20\x46\x69\x6c\x65\x73\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x3a\x20"
"\x01\x63\x25\x75\x0d\x0a" // 615 DirInfoMaxFiles
,"\x01\x6e\x01\x68\x01\x62\x56\x69\x65\x77\x20\x64\x69\x72\x65\x63\x74\x6f\x72\x79\x20\x69\x6e\x66\x6f\x72\x6d\x61\x74\x69\x6f\x6e"
"\x20\x66\x69\x6c\x65" // 616 DirInfoViewFileQ
,"" // 616 DirInfoViewFileQ
,"\x01\x6e\x0d\x0a\x4e\x6f\x20\x4e\x65\x74\x4d\x61\x69\x6c\x20\x61\x6c\x6c\x6f\x77\x65\x64\x2e\x0d\x0a" // 617 NoNetMailAllowed
,"\x0d\x0a\x49\x74\x20\x77\x69\x6c\x6c\x20\x63\x6f\x73\x74\x20\x79\x6f\x75\x20\x25\x75\x20\x63\x72\x65\x64\x69\x74\x73\x20\x74\x6f"
"\x20\x73\x65\x6e\x64\x20\x4e\x65\x74\x4d\x61\x69\x6c\x2e\x20\x43\x6f\x6e\x74\x69\x6e\x75\x65" // 618 NetMailCostContinueQ
......
......@@ -96,6 +96,7 @@
<ItemGroup>
<ClCompile Include="dat_rec.c" />
<ClCompile Include="filedat.c" />
<ClCompile Include="getstats.c" />
<ClCompile Include="sauce.c" />
<ClCompile Include="upgrade_to_v319.c" />
<ClCompile Include="userdat.c" />
......
......@@ -167,6 +167,7 @@ bool sbbs_t::uploadfile(file_t* f)
if(!(cfg.dir[f->dir]->misc&DIR_NOSTAT)) {
logon_ulb+=length; /* Update 'this call' stats */
logon_uls++;
inc_upload_stats(&cfg, 1, length);
}
if(cfg.dir[f->dir]->misc&DIR_AONLY) /* Forced anonymous */
f->hdr.attr |= MSG_ANONYMOUS;
......