From 4f49b16079b27d7ce633619e4cddfdf2bffbc952 Mon Sep 17 00:00:00 2001
From: cashmere This repository is Wildly INSECURE because
+ @ it grants administrator privileges to anonymous users. You
+ @ should take this repository private
+ @ immediately! Or, at least remove the Setup and Admin privileges
+ @ for users "anonymous" and "login" on the
+ @ User Configuration page.
+ }else if( hasAnyCap(zSelfCap,"as") && hasSelfReg ){
+ @ This repository is Wildly INSECURE because
+ @ it grants administrator privileges to self-registered users. You
+ @ should take this repository private
+ @ and/or disable self-registration
+ @ immediately! Or, at least remove the Setup and Admin privileges
+ @ from the default permissions for new users.
+ }else if( hasAnyCap(zAnonCap,"y") ){
+ @ This repository is INSECURE because
+ @ it allows anonymous users to push unversioned files.
+ @ Fix this by taking the repository private
+ @ or by removing the "y" permission from users "anonymous" and
+ @ "nobody" on the User Configuration page.
+ }else if( hasAnyCap(zSelfCap,"y") ){
+ @ This repository is INSECURE because
+ @ it allows self-registered users to push unversioned files.
+ @ Fix this by taking the repository private
+ @ or by removing the "y" permission from the default permissions or
+ @ by disabling self-registration.
+ }else if( hasAnyCap(zAnonCap,"goz") ){
+ @ This repository is PUBLIC. All
+ @ checked-in content can be accessed by anonymous users.
+ @ Take it private.
+ }else if( hasAnyCap(zSelfCap,"goz") && hasSelfReg ){
+ @ This repository is PUBLIC because all
+ @ checked-in content can be accessed by self-registered users.
+ @ This repostory would be private if you disabled self-registration. This repository is Completely PRIVATE.
+ @ A valid login and password is required to access any content.
+ bCompletelyPrivate = 1;
+ }else{
+ @ This repository is Mostly PRIVATE.
+ @ A valid login and password is usually required, however some
+ @ content can be accessed either anonymously or by self-registered
+ @ users:
+ @ Anoymous users are vested with capabilities "%h(zSelfCap)" on
+ @ public pages. See the "Public Pages" entry in the
+ @ "User capability summary" below.
+ }
+ @ Change GLOB patterns exceptions using the "Public pages" setting
+ @ on the Access Settings page. The canonical URL for this
+ @ repository is %h(zPublicUrl).
+ if( nOther==1 ){
+ @ This is also 1 other URL that has
+ @ been used to access this repository.
+ }else if( nOther>=2 ){
+ @ There are also
+ @ %d(nOther) other URLs that have
+ @ been used to access this repository.
+ }
+ }else{
+ int nUrl = db_int(0, "SELECT count(*) FROM config"
+ " WHERE name GLOB 'baseurl:*'");
+ @ This repository does not have a
+ @ canonical access URL.
+ if( nUrl==1 ){
+ @ There is
+ @ 1 non-canonical URL
+ @ that has been used to access this repository.
+ }else if( nUrl>=2 ){
+ @ There are
+ @ %d(nUrl) non-canonical URLs
+ @ that have been used to access this repository.
+ }
+ }
+
+ /* Make sure the HTTPS is required for login, at least, so that the
+ ** password does not go across the Internet in the clear.
+ */
+ if( db_get_int("redirect-to-https",0)==0 ){
+ @ WARNING:
+ @ Sensitive material such as login passwords can be sent over an
+ @ unencrypted connection.
+ @ Fix this by changing the "Redirect to HTTPS" setting on the
+ @ Access Control page. If you were using
+ @ the old "Redirect to HTTPS on Login Page" setting, switch to the
+ @ new setting: it has a more secure implementation.
+ }
+
+#ifdef FOSSIL_ENABLE_TH1_DOCS
+ /* The use of embedded TH1 is dangerous. Warn if it is possible.
+ */
+ if( !Th_AreDocsEnabled() ){
+ @
+ @ This server is compiled with -DFOSSIL_ENABLE_TH1_DOCS. TH1 docs
+ @ are disabled for this particular repository, so you are safe for
+ @ now. However, to prevent future problems caused by accidentally
+ @ enabling TH1 docs in the future, it is recommended that you
+ @ recompile Fossil without the -DFOSSIL_ENABLE_TH1_DOCS flag. DANGER:
+ @ This server is compiled with -DFOSSIL_ENABLE_TH1_DOCS and TH1 docs
+ @ are enabled for this repository. Anyone who can check-in or push
+ @ to this repository can create a malicious TH1 script and then cause
+ @ that script to be run on the server. This is a serious security concern.
+ @ TH1 docs should only be enabled for repositories with a very limited
+ @ number of trusted committers, and the repository should be monitored
+ @ closely to ensure no hostile content sneaks in. If a bad TH1 script
+ @ does make it into the repository, the only want to prevent it from
+ @ being run is to shun it. Disable TH1 docs by recompiling Fossil without the
+ @ -DFOSSIL_ENABLE_TH1_DOCS flag, and/or clear the th1-docs setting
+ @ and ensure that the TH1_ENABLE_DOCS environment variable does not
+ @ exist in the environment.
+ if( db_get_boolean("tcl",0) ){
+ #ifdef FOSSIL_ENABLE_TH1_DOCS
+ if( Th_AreDocsEnabled() ){
+ @ DANGER:
+ }else{
+ @ WARNING:
+ }
+ #else
+ @ WARNING:
+ #endif
+ @ This server is compiled with -DFOSSIL_ENABLE_TCL and Tcl integration
+ @ is enabled for this repository. Anyone who can execute malicious
+ @ TH1 script on that server can also execute arbitrary Tcl script
+ @ under the identity of the operating system process of that server.
+ @ This is a serious security concern. Disable Tcl integration by recompiling Fossil without the
+ @ -DFOSSIL_ENABLE_TCL flag, and/or clear the 'tcl' setting.
+ if( hasSelfReg ){
+ if( hasAnyCap(zAnonCap,"j") || hasAnyCap(zSelfCap,"j") ){
+ @
+ if( zPubPages && zPubPages[0] ){
+ @
+ for(i=0; i
+ @
WARNING: + @ Anonymous users can view email addresses and other personally + @ identifiable information on tickets. + @ Fix this by removing the "Email" privilege + @ (capability "e") from users + @ "anonymous" and "nobody" on the + @ User Configuration page. + } + + /* Anonymous users probably should not be allowed to push content + ** to the repository. + */ + if( hasAnyCap(zAnonCap, "i") ){ + @
WARNING: + @ Anonymous users can push new check-ins into the repository. + @ Fix this by removing the "Check-in" privilege + @ (capability "i") from users + @ "anonymous" and "nobody" on the + @ User Configuration page. + } + + /* Anonymous users probably should not be allowed act as moderators + ** for wiki or tickets. + */ + if( hasAnyCap(zAnonCap, "lq5") ){ + @
WARNING: + @ Anonymous users can act as moderators for wiki, tickets, or + @ forum posts. This defeats the whole purpose of moderation. + @ Fix this by removing the "Mod-Wiki", "Mod-Tkt", and "Mod-Forum" + @ privileges (capabilities "fq5") + @ from users "anonymous" and "nobody" + @ on the User Configuration page. + } + + /* Check to see if any TH1 scripts are configured to run on a sync + */ + if( db_exists("SELECT 1 FROM config WHERE name GLOB 'xfer-*-script'" + " AND length(value)>0") ){ + @
WARNING: + @ TH1 scripts might be configured to run on any sync, push, pull, or + @ clone operation. See the /xfersetup + @ page for more information. These TH1 scripts are a potential + @ security concern and so should be carefully audited by a human. + } + + /* The strict-manifest-syntax setting should be on. */ + if( db_get_boolean("strict-manifest-syntax",1)==0 ){ + @
WARNING: + @ The "strict-manifest-syntax" flag is off. This is a security + @ risk. Turn this setting on (its default) to protect the users + @ of this repository. + } + + zVulnReport = db_get("vuln-report","log"); + if( fossil_strcmp(zVulnReport,"block")!=0 + && fossil_strcmp(zVulnReport,"fatal")!=0 + ){ + @
WARNING: + @ The vuln-report setting + @ has a value of "%h(zVulnReport)". This disables defenses against + @ XSS or SQL-injection vulnerabilities caused by coding errors in + @ custom TH1 scripts. For the best security, change + @ the value of the vuln-report setting to "block" or "fatal". + } + + /* Obsolete: */ + if( hasAnyCap(zAnonCap, "d") || + hasAnyCap(zDevCap, "d") || + hasAnyCap(zReadCap, "d") ){ + @
WARNING: + @ One or more users has the obsolete + @ "d" capability. You should remove it using the + @ User Configuration page in case we + @ ever reuse the letter for another purpose. + } + + /* If anonymous users are allowed to create new Wiki, then + ** wiki moderation should be activated to prevent spam. + */ + if( hasAnyCap(zAnonCap, "fk") ){ + if( db_get_boolean("modreq-wiki",0)==0 ){ + @
WARNING: + @ Anonymous users can create or edit wiki without moderation. + @ This can result in robots inserting lots of wiki spam into + @ repository. + @ Fix this by removing the "New-Wiki" and "Write-Wiki" + @ privileges from users "anonymous" and "nobody" on the + @ User Configuration page or + @ by enabling wiki moderation on the + @ Moderation Setup page. + }else{ + @
+ @ Anonymous users can create or edit wiki, but moderator + @ approval is required before the edits become permanent. + } + } + + /* Anonymous users should not be able to create trusted forum + ** posts. + */ + if( hasAnyCap(zAnonCap, "456") ){ + @
WARNING: + @ Anonymous users can create forum posts that are + @ accepted into the permanent record without moderation. + @ This can result in robots generating spam on forum posts. + @ Fix this by removing the "WriteTrusted-Forum" privilege + @ (capabilities "456") from + @ users "anonymous" and "nobody" on the + @ User Configuration page or + } + + /* Anonymous users should not be able to send announcements. + */ + if( hasAnyCap(zAnonCap, "A") ){ + @
WARNING: + @ Anonymous users can send announcements to anybody who is signed + @ up to receive announcements. This can result in spam. + @ Fix this by removing the "Announce" privilege + @ (capability "A") from + @ users "anonymous" and "nobody" on the + @ User Configuration page or + } + + /* Administrative privilege should only be provided to + ** specific individuals, not to entire classes of people. + ** And not too many people should have administrator privilege. + */ + z = db_text(0, + "SELECT group_concat(" + "printf('%%s',uid,login)," + "' and ')" + " FROM user" + " WHERE cap GLOB '*[as]*'" + " AND login in ('anonymous','nobody','reader','developer')" + ); + if( z && z[0] ){ + @
WARNING: + @ Administrative privilege ('a' or 's') + @ is granted to an entire class of users: %s(z). + @ Administrative privilege should only be + @ granted to specific individuals. + } + n = db_int(0,"SELECT count(*) FROM user WHERE fullcap(cap) GLOB '*[as]*'"); + if( n==0 ){ + @
+ @ No users have administrator privilege. + }else{ + z = db_text(0, + "SELECT group_concat(" + "printf('%%s',uid,login)," + "', ')" + " FROM user" + " WHERE fullcap(cap) GLOB '*[as]*'" + ); + @
+ @ Users with administrator privilege are: %s(z) + fossil_free(z); + if( n>3 ){ + @
WARNING: + @ Administrator privilege is granted to + @ %d(n) users. + @ Ideally, administrator privilege ('s' or 'a') should only + @ be granted to one or two users. + } + } + + /* The push-unversioned privilege should only be provided to + ** specific individuals, not to entire classes of people. + ** And no too many people should have this privilege. + */ + z = db_text(0, + "SELECT group_concat(" + "printf('%%s',uid,login)," + "' and ')" + " FROM user" + " WHERE cap GLOB '*y*'" + " AND login in ('anonymous','nobody','reader','developer')" + ); + if( z && z[0] ){ + @
WARNING: + @ The "Write-Unver" privilege is granted to an entire class of users: %s(z). + @ The Write-Unver privilege should only be granted to specific individuals. + fossil_free(z); + } + n = db_int(0,"SELECT count(*) FROM user WHERE cap GLOB '*y*'"); + if( n>0 ){ + z = db_text(0, + "SELECT group_concat(" + "printf('%%s',uid,login),', ')" + " FROM user WHERE fullcap(cap) GLOB '*y*'" + ); + @
+ @ Users with "Write-Unver" privilege: %s(z) + fossil_free(z); + if( n>3 ){ + @
Caution: + @ The "Write-Unver" privilege ('y') is granted to an excessive + @ number of users (%d(n)). + @ Ideally, the Write-Unver privilege should only + @ be granted to one or two users. + } + } + + /* Providing hyperlink capability to user "nobody" can lead to robots + ** making excessive requests resulting in DoS + */ + if( db_exists("SELECT 1 FROM user WHERE login='nobody' AND cap GLOB '*h*'") ){ + int nobodyId = db_int(0,"SELECT uid FROM user WHERE login='nobody'"); + int anonId = db_int(0, + "SELECT uid FROM user WHERE login='anonymous' AND cap NOT GLOB '*h*'"); + @
+ @ User "nobody" has "Hyperlink" privilege ('h') which can lead to + @ robots walking a nearly endless progression of pages on public-facing + @ repositories, causing excessive server load and possible DoS. + @ Suggested remediation: + @
Caution: + @ This repository trusts that the REMOTE_USER environment variable set + @ up by the webserver contains the name of an authenticated user. + @ Fossil's built-in authentication mechanism is bypassed. + @ Fix this by deactivating the "Allow REMOTE_USER authentication" + @ checkbox on the Access Control page. + } + if( db_get_boolean("http_authentication_ok", 0) ){ + @
Caution: + @ This repository trusts that the HTTP_AUTHENTICATION environment + @ variable set up by the webserver contains the name of an + @ authenticated user. + @ Fossil's built-in authentication mechanism is bypassed. + @ Fix this by deactivating the "Allow HTTP_AUTHENTICATION authentication" + @ checkbox on the Access Control page. + } + + /* Logging should be turned on + */ + if( db_get_boolean("access-log",1)==0 ){ + @
+ @ The User Log is disabled. The user log + @ keeps a record of successful and unsuccessful login attempts and is + @ useful for security monitoring. + } + if( db_get_boolean("admin-log",1)==0 ){ + @
+ @ The Administrative Log is disabled. + @ The administrative log provides a record of configuration changes + @ and is useful for security monitoring. + } + +#if !defined(_WIN32) && !defined(FOSSIL_OMIT_LOAD_AVERAGE) + /* Make sure that the load-average limiter is armed and working */ + if( load_average()==0.0 ){ + @
+ @ Unable to get the system load average. This can prevent Fossil + @ from throttling expensive operations during peak demand. + @ If running in a chroot jail on Linux, verify that the /proc + @ filesystem is mounted within the jail, so that the load average + @ can be obtained from the /proc/loadavg file. + }else { + double r = atof(db_get("max-loadavg", "0.0")); + if( r<=0.0 ){ + @
+ @ Load average limiting is turned off. This can cause the server + @ to bog down if many requests for expensive services (such as + @ large diffs or tarballs) arrive at about the same time. + @ To fix this, set the + @ "Server Load Average Limit" on the + @ Access Control page to the approximate + @ the number of available cores on your server, or maybe just a little + @ less. + }else if( r>=8.0 ){ + @
+ @ The "Server Load Average Limit" on + @ the Access Control page is set to %g(r), + @ which seems high. Is this server really a %d((int)r)-core machine? + } + } +#endif + + if( g.zErrlog==0 || fossil_strcmp(g.zErrlog,"-")==0 ){ + @
+ @ The server error log is disabled. + @ To set up an error log, + if( fossil_strcmp(g.zCmdName, "cgi")==0 ){ + @ make an entry like "errorlog: FILENAME" in the + @ CGI script at %h(P("SCRIPT_FILENAME")). + }else{ + @ add the "--errorlog FILENAME" option to the + @ "%h(g.argv[0]) %h(g.zCmdName)" command that launched this server. + } + }else{ + FILE *pTest = fossil_fopen(g.zErrlog,"a"); + if( pTest==0 ){ + @
+ @ Error: + @ There is an error log at "%h(g.zErrlog)" but that file is not + @ writable and so no logging will occur. + }else{ + fclose(pTest); + @
+ @ The error log at "%h(g.zErrlog)" is + @ %,lld(file_size(g.zErrlog, ExtFILE)) bytes in size. + } + } + + if( g.zExtRoot ){ + int nFile; + int nCgi; + ext_files(); + nFile = db_int(0, "SELECT count(*) FROM sfile"); + nCgi = nFile==0 ? 0 : db_int(0,"SELECT count(*) FROM sfile WHERE isexe"); + @
CGI Extensions are enabled with a document root + @ at %h(g.zExtRoot) holding + @ %d(nCgi) CGIs and %d(nFile-nCgi) static content and data files. + } + + if( fileedit_glob()!=0 ){ + @
Online File Editing is enabled + @ for this repository. Clear the + @ "fileedit-glob" setting to + @ disable online editing.
+ } + + @User capability summary: + capability_summary(); + + + azCSP = parse_content_security_policy(); + if( azCSP==0 ){ + @
WARNING: No Content Security Policy (CSP) is specified in the + @ header. Though not required, a strong CSP is recommended. Fossil will + @ automatically insert an appropriate CSP if you let it generate the + @ HTML <head> element by omitting <body> + @ from the header configuration in your customized skin. + @ + }else{ + int ii; + @
Content Security Policy: + @
Email alert configuration summary:
+ if( !zListId || !zListId[0] ){
+ @
WARNING: email-listid is not set,
+ @ so notifications will not include unsubscribe links.
+ }
+ fossil_free(zListId);
+ @
Email alerts are disabled + } + + n = db_int(0,"SELECT count(*) FROM (" + "SELECT rid FROM phantom EXCEPT SELECT rid FROM private)"); + if( n>0 ){ + @
\ + @ There exists public phantom artifacts in this repository, shown below. + @ Phantom artifacts are artifacts whose hash name is referenced by some + @ other artifact but whose content is unknown. Some phantoms are marked + @ private and those are ignored. But public phantoms cause unnecessary + @ sync traffic and might represent malicious attempts to corrupt the + @ repository structure. + @
+ @ To suppress unnecessary sync traffic caused by phantoms, add the RID + @ of each phantom to the "private" table. Example: + @
+ @ + table_of_public_phantoms(); + @+ @ INSERT INTO private SELECT rid FROM blob WHERE content IS NULL; + @
Robot Defenses: + @
+ if( g.zCgiFile ){ + Blob fullname; + blob_init(&fullname, 0, 0); + file_canonical_name(g.zCgiFile, &fullname, 0); + @ The CGI control file for this page is "%h(blob_str(&fullname))". + } + @ The command that generated this page: + @
+ @ %h(blob_str(&cmd)) + @
Click the "Make It Private" button below to disable all + @ anonymous access to this repository. A valid login and password + @ will be required to access this repository after clicking that + @ button.
+ @ + @Click the "Cancel" button to leave things as they are.
+ @ + @ + + style_finish_page(); +} + +/* +** Output a message explaining that no error log is available. +*/ +static void no_error_log_available(void){ + @No error log is configured. + if( g.zCgiFile==0 ){ + @ To create an error log, add the "--errorlog FILENAME" + @ command-line option to the command that launches the Fossil server. + }else{ + Blob fullname; + blob_init(&fullname, 0, 0); + file_canonical_name(g.zCgiFile, &fullname, 0); + @ To create an error log, edit the CGI control file + @ named "%h(blob_str(&fullname))" to add a line like this: + @
+ blob_reset(&fullname); + } +} + +/* +** WEBPAGE: errorlog +** +** Show the content of the error log. Only the administrator can view +** this page. +** +** y=0x001 Show only hack attempts +** y=0x002 Show only panics and assertion faults +** y=0x004 Show hung backoffice processes +** y=0x008 Show POST requests from a different origin +** y=0x010 Show SQLITE_AUTH and similar +** y=0x020 Show SMTP error reports +** y=0x040 Show TH1 vulnerability reports +** y=0x080 Show SQL errors +** y=0x100 Show timeouts +** y=0x200 Show WAL recoveries +** y=0x8000 Show other uncategorized messages +** +** If y is omitted or is zero, a count of the various message types is +** shown. +*/ +void errorlog_page(void){ + i64 szFile; + FILE *in; + char *zLog; + const char *zType = P("y"); + static const int eAllTypes = 0x83ff; + long eType = 0; + int bOutput = 0; + int prevWasTime = 0; + int nHack = 0; + int nPanic = 0; + int nOther = 0; + int nHang = 0; + int nXPost = 0; + int nAuth = 0; + int nSmtp = 0; + int nVuln = 0; + int nSqlErr = 0; + int nTimeout = 0; + int nRecover = 0; + char z[10000]; + char zTime[10000]; + + login_check_credentials(); + if( !g.perm.Admin ){ + login_needed(0); + return; + } + if( zType ){ + eType = strtol(zType,0,0) & eAllTypes; + } + style_header("Server Error Log"); + style_submenu_element("Test", "%R/test-warning"); + style_submenu_element("Refresh", "%R/errorlog"); + style_submenu_element("Download", "%R/errorlog?download"); + style_submenu_element("Truncate", "%R/errorlog?truncate"); + style_submenu_element("Log-Menu", "%R/setup-logmenu"); + if( eType ){ + style_submenu_element("Summary", "%R/errorlog"); + } + + if( g.zErrlog==0 || fossil_strcmp(g.zErrlog,"-")==0 ){ + no_error_log_available(); + style_finish_page(); + return; + } + if( P("truncate1") && cgi_csrf_safe(2) ){ + fclose(fopen(g.zErrlog,"w")); + } + if( P("download") ){ + Blob log; + blob_read_from_file(&log, g.zErrlog, ExtFILE); + cgi_set_content_type("text/plain"); + cgi_set_content(&log); + return; + } + szFile = file_size(g.zErrlog, ExtFILE); + if( P("truncate") ){ + @ + style_finish_page(); + return; + } + zLog = file_canonical_name_dup(g.zErrlog); + @+ @ errorlog: FILENAME + @
The server error log at "%h(zLog)" is %,lld(szFile) bytes in size. + fossil_free(zLog); + in = fossil_fopen(g.zErrlog, "rb"); + if( in==0 ){ + @
Unable to open that file for reading!
+ style_finish_page(); + return; + } + if( eType==0 ){ + /* will do a summary */ + }else if( (eType&eAllTypes)!=eAllTypes ){ + @ Only the following types of messages displayed: + @
+ }
+ while( fgets(z, sizeof(z), in) ){
+ if( prevWasTime ){
+ if( strncmp(z,"possible hack attempt - 418 ", 27)==0 ){
+ bOutput = (eType & 0x01)!=0;
+ nHack++;
+ }else
+ if( strncmp(z,"panic: ", 7)==0 ){
+ if( strncmp(z+7,"Timeout",7)==0 ){
+ bOutput = (eType & 0x100)!=0;
+ nTimeout++;
+ }else{
+ bOutput = (eType & 0x02)!=0;
+ nPanic++;
+ }
+ }else
+ if( strstr(z,"assertion fault")!=0 ){
+ bOutput = (eType & 0x02)!=0;
+ nPanic++;
+ }else
+ if( strncmp(z,"SMTP:", 5)==0 ){
+ bOutput = (eType & 0x20)!=0;
+ nSmtp++;
+ }else
+ if( sqlite3_strglob("warning: SQLITE_NOTICE(283):*",z)==0 ){
+ bOutput = (eType & 0x200)!=0;
+ nRecover++;
+ }else
+ if( sqlite3_strglob("warning: backoffice process * still *",z)==0 ){
+ bOutput = (eType & 0x04)!=0;
+ nHang++;
+ }else
+ if( sqlite3_strglob("warning: POST from different origin*",z)==0 ){
+ bOutput = (eType & 0x08)!=0;
+ nXPost++;
+ }else
+ if( sqlite3_strglob("SECURITY: authorizer blocks*",z)==0
+ || sqlite3_strglob("warning: SQLITE_AUTH*",z)==0
+ ){
+ bOutput = (eType & 0x10)!=0;
+ nAuth++;
+ }else
+ if( strncmp(z,"possible", 8)==0 && strstr(z,"tainted")!=0 ){
+ bOutput = (eType & 0x40)!=0;
+ nVuln++;
+ }else
+ if( strstr(z,"statement aborts at ") ){
+ bOutput = (eType & 0x80)!=0;
+ nSqlErr++;
+ }else
+ {
+ bOutput = (eType & 0x8000)!=0;
+ nOther++;
+ }
+ if( bOutput ){
+ @ %h(zTime)\
+ }
+ }
+ if( strncmp(z, "--------", 8)==0 ){
+ size_t n = strlen(z);
+ memcpy(zTime, z, n+1);
+ prevWasTime = 1;
+ bOutput = 0;
+ }else{
+ prevWasTime = 0;
+ }
+ if( bOutput && eType ){
+ @ %h(z)\
+ }
+ }
+ fclose(in);
+ if( eType ){
+ @
+ }
+ if( eType==0 ){
+ int nNonHack = nPanic + nHang + nAuth + nSmtp + nVuln + nOther + nSqlErr;
+ int nTotal = nNonHack + nHack + nXPost;
+ @ | %d(nPanic) | + @Panics | + } + if( nVuln>0 ){ + @|
| %d(nVuln) | + @TH1 Vulnerabilities | + } + if( nHack>0 ){ + @|
| %d(nHack) | + @Hack Attempts | + } + if( nSqlErr>0 ){ + @|
| %d(nSqlErr) | + @SQL Errors | + } + if( nTimeout>0 ){ + @|
| %d(nTimeout) | + @Timeouts | + } + if( nRecover>0 ){ + @|
| %d(nRecover) | + @WAL recoveries | + } + if( nHang>0 ){ + @|
| %d(nHang) | + @Hung Backoffice | + } + if( nXPost>0 ){ + @|
| %d(nXPost) | + @POSTs from different origin | + } + if( nAuth>0 ){ + @|
| %d(nAuth) | + @SQLITE_AUTH and similar | + } + if( nSmtp>0 ){ + @|
| %d(nSmtp) | + @SMTP faults | + } + if( nOther>0 ){ + @|
| %d(nOther) | + @Other | + } + @|
| %d(nTotal) | + if( nTotal>0 ){ + @All Messages | + }else{ + @All Messages | + } + @