Fossil SCM

Add a setup option that automatically redirects from HTTP to HTTPS on the login page, to prevent passwords from being sent in the clear over the internet. Off by default, for the sake of installations that do not support HTTPS. Also off for the "fossil ui" and "fossil server" commands.

drh 2014-11-13 16:09 UTC trunk
Commit b85eb7db7917938b000d6f29a5f53877c2802ac8
+26 -1
--- src/login.c
+++ src/login.c
@@ -475,10 +475,20 @@
475475
char *zSha1Pw;
476476
const char *zIpAddr; /* IP address of requestor */
477477
const char *zReferer;
478478
479479
login_check_credentials();
480
+ if( login_wants_https_redirect() ){
481
+ const char *zQS = P("QUERY_STRING");
482
+ if( zQS==0 ){
483
+ zQS = "";
484
+ }else if( zQS[0]!=0 ){
485
+ zQS = mprintf("?%s", zQS);
486
+ }
487
+ cgi_redirectf("%s%s%s", g.zHttpsURL, P("PATH_INFO"), zQS);
488
+ return;
489
+ }
480490
sqlite3_create_function(g.db, "constant_time_cmp", 2, SQLITE_UTF8, 0,
481491
constant_time_cmp_function, 0, 0);
482492
zUsername = P("u");
483493
zPasswd = P("p");
484494
anonFlag = P("anon")!=0;
@@ -776,10 +786,25 @@
776786
" AND constant_time_cmp(cookie,%Q)=0",
777787
zLogin, zRemoteAddr, zCookie
778788
);
779789
return uid;
780790
}
791
+
792
+/*
793
+** Return true if it is appropriate to redirect login requests to HTTPS.
794
+**
795
+** Redirect to https is appropriate if all of the above are true:
796
+** (1) The redirect-to-https flag is set
797
+** (2) The current connection is http, not https or ssh
798
+** (3) The sslNotAvailable flag is clear
799
+*/
800
+int login_wants_https_redirect(void){
801
+ if( g.sslNotAvailable ) return 0;
802
+ if( db_get_boolean("redirect-to-https",0)==0 ) return 0;
803
+ if( P("HTTPS")!=0 ) return 0;
804
+ return 1;
805
+}
781806
782807
/*
783808
** This routine examines the login cookie to see if it exists and
784809
** is valid. If the login cookie checks out, it then sets global
785810
** variables appropriately.
@@ -812,11 +837,11 @@
812837
** This feature allows the "fossil ui" command to give the user
813838
** full access rights without having to log in.
814839
*/
815840
zRemoteAddr = ipPrefix(zIpAddr = PD("REMOTE_ADDR","nil"));
816841
if( ( fossil_strcmp(zIpAddr, "127.0.0.1")==0 ||
817
- g.fSshClient & CGI_SSH_CLIENT )
842
+ (g.fSshClient & CGI_SSH_CLIENT)!=0 )
818843
&& g.useLocalauth
819844
&& db_get_int("localauth",0)==0
820845
&& P("HTTPS")==0
821846
){
822847
if( g.localOpen ) zLogin = db_lget("default-user",0);
823848
--- src/login.c
+++ src/login.c
@@ -475,10 +475,20 @@
475 char *zSha1Pw;
476 const char *zIpAddr; /* IP address of requestor */
477 const char *zReferer;
478
479 login_check_credentials();
 
 
 
 
 
 
 
 
 
 
480 sqlite3_create_function(g.db, "constant_time_cmp", 2, SQLITE_UTF8, 0,
481 constant_time_cmp_function, 0, 0);
482 zUsername = P("u");
483 zPasswd = P("p");
484 anonFlag = P("anon")!=0;
@@ -776,10 +786,25 @@
776 " AND constant_time_cmp(cookie,%Q)=0",
777 zLogin, zRemoteAddr, zCookie
778 );
779 return uid;
780 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
781
782 /*
783 ** This routine examines the login cookie to see if it exists and
784 ** is valid. If the login cookie checks out, it then sets global
785 ** variables appropriately.
@@ -812,11 +837,11 @@
812 ** This feature allows the "fossil ui" command to give the user
813 ** full access rights without having to log in.
814 */
815 zRemoteAddr = ipPrefix(zIpAddr = PD("REMOTE_ADDR","nil"));
816 if( ( fossil_strcmp(zIpAddr, "127.0.0.1")==0 ||
817 g.fSshClient & CGI_SSH_CLIENT )
818 && g.useLocalauth
819 && db_get_int("localauth",0)==0
820 && P("HTTPS")==0
821 ){
822 if( g.localOpen ) zLogin = db_lget("default-user",0);
823
--- src/login.c
+++ src/login.c
@@ -475,10 +475,20 @@
475 char *zSha1Pw;
476 const char *zIpAddr; /* IP address of requestor */
477 const char *zReferer;
478
479 login_check_credentials();
480 if( login_wants_https_redirect() ){
481 const char *zQS = P("QUERY_STRING");
482 if( zQS==0 ){
483 zQS = "";
484 }else if( zQS[0]!=0 ){
485 zQS = mprintf("?%s", zQS);
486 }
487 cgi_redirectf("%s%s%s", g.zHttpsURL, P("PATH_INFO"), zQS);
488 return;
489 }
490 sqlite3_create_function(g.db, "constant_time_cmp", 2, SQLITE_UTF8, 0,
491 constant_time_cmp_function, 0, 0);
492 zUsername = P("u");
493 zPasswd = P("p");
494 anonFlag = P("anon")!=0;
@@ -776,10 +786,25 @@
786 " AND constant_time_cmp(cookie,%Q)=0",
787 zLogin, zRemoteAddr, zCookie
788 );
789 return uid;
790 }
791
792 /*
793 ** Return true if it is appropriate to redirect login requests to HTTPS.
794 **
795 ** Redirect to https is appropriate if all of the above are true:
796 ** (1) The redirect-to-https flag is set
797 ** (2) The current connection is http, not https or ssh
798 ** (3) The sslNotAvailable flag is clear
799 */
800 int login_wants_https_redirect(void){
801 if( g.sslNotAvailable ) return 0;
802 if( db_get_boolean("redirect-to-https",0)==0 ) return 0;
803 if( P("HTTPS")!=0 ) return 0;
804 return 1;
805 }
806
807 /*
808 ** This routine examines the login cookie to see if it exists and
809 ** is valid. If the login cookie checks out, it then sets global
810 ** variables appropriately.
@@ -812,11 +837,11 @@
837 ** This feature allows the "fossil ui" command to give the user
838 ** full access rights without having to log in.
839 */
840 zRemoteAddr = ipPrefix(zIpAddr = PD("REMOTE_ADDR","nil"));
841 if( ( fossil_strcmp(zIpAddr, "127.0.0.1")==0 ||
842 (g.fSshClient & CGI_SSH_CLIENT)!=0 )
843 && g.useLocalauth
844 && db_get_int("localauth",0)==0
845 && P("HTTPS")==0
846 ){
847 if( g.localOpen ) zLogin = db_lget("default-user",0);
848
+3
--- src/main.c
+++ src/main.c
@@ -150,10 +150,11 @@
150150
char *zSshCmd; /* SSH command string */
151151
int fNoSync; /* Do not do an autosync ever. --nosync */
152152
char *zPath; /* Name of webpage being served */
153153
char *zExtra; /* Extra path information past the webpage name */
154154
char *zBaseURL; /* Full text of the URL being served */
155
+ char *zHttpsURL; /* zBaseURL translated to https: */
155156
char *zTop; /* Parent directory of zPath */
156157
const char *zContentType; /* The content type of the input HTTP request */
157158
int iErrPriority; /* Priority of current error message */
158159
char *zErrMsg; /* Text of an error message */
159160
int sslNotAvailable; /* SSL is not available. Do not redirect to https: */
@@ -1289,13 +1290,15 @@
12891290
i = strlen(zCur);
12901291
while( i>0 && zCur[i-1]=='/' ) i--;
12911292
if( fossil_stricmp(zMode,"on")==0 ){
12921293
g.zBaseURL = mprintf("https://%s%.*s", zHost, i, zCur);
12931294
g.zTop = &g.zBaseURL[8+strlen(zHost)];
1295
+ g.zHttpsURL = g.zBaseURL;
12941296
}else{
12951297
g.zBaseURL = mprintf("http://%s%.*s", zHost, i, zCur);
12961298
g.zTop = &g.zBaseURL[7+strlen(zHost)];
1299
+ g.zHttpsURL = mprintf("https://%s%.*s", zHost, i, zCur);
12971300
}
12981301
}
12991302
if( db_is_writeable("repository") ){
13001303
if( !db_exists("SELECT 1 FROM config WHERE name='baseurl:%q'", g.zBaseURL)){
13011304
db_multi_exec("INSERT INTO config(name,value,mtime)"
13021305
--- src/main.c
+++ src/main.c
@@ -150,10 +150,11 @@
150 char *zSshCmd; /* SSH command string */
151 int fNoSync; /* Do not do an autosync ever. --nosync */
152 char *zPath; /* Name of webpage being served */
153 char *zExtra; /* Extra path information past the webpage name */
154 char *zBaseURL; /* Full text of the URL being served */
 
155 char *zTop; /* Parent directory of zPath */
156 const char *zContentType; /* The content type of the input HTTP request */
157 int iErrPriority; /* Priority of current error message */
158 char *zErrMsg; /* Text of an error message */
159 int sslNotAvailable; /* SSL is not available. Do not redirect to https: */
@@ -1289,13 +1290,15 @@
1289 i = strlen(zCur);
1290 while( i>0 && zCur[i-1]=='/' ) i--;
1291 if( fossil_stricmp(zMode,"on")==0 ){
1292 g.zBaseURL = mprintf("https://%s%.*s", zHost, i, zCur);
1293 g.zTop = &g.zBaseURL[8+strlen(zHost)];
 
1294 }else{
1295 g.zBaseURL = mprintf("http://%s%.*s", zHost, i, zCur);
1296 g.zTop = &g.zBaseURL[7+strlen(zHost)];
 
1297 }
1298 }
1299 if( db_is_writeable("repository") ){
1300 if( !db_exists("SELECT 1 FROM config WHERE name='baseurl:%q'", g.zBaseURL)){
1301 db_multi_exec("INSERT INTO config(name,value,mtime)"
1302
--- src/main.c
+++ src/main.c
@@ -150,10 +150,11 @@
150 char *zSshCmd; /* SSH command string */
151 int fNoSync; /* Do not do an autosync ever. --nosync */
152 char *zPath; /* Name of webpage being served */
153 char *zExtra; /* Extra path information past the webpage name */
154 char *zBaseURL; /* Full text of the URL being served */
155 char *zHttpsURL; /* zBaseURL translated to https: */
156 char *zTop; /* Parent directory of zPath */
157 const char *zContentType; /* The content type of the input HTTP request */
158 int iErrPriority; /* Priority of current error message */
159 char *zErrMsg; /* Text of an error message */
160 int sslNotAvailable; /* SSL is not available. Do not redirect to https: */
@@ -1289,13 +1290,15 @@
1290 i = strlen(zCur);
1291 while( i>0 && zCur[i-1]=='/' ) i--;
1292 if( fossil_stricmp(zMode,"on")==0 ){
1293 g.zBaseURL = mprintf("https://%s%.*s", zHost, i, zCur);
1294 g.zTop = &g.zBaseURL[8+strlen(zHost)];
1295 g.zHttpsURL = g.zBaseURL;
1296 }else{
1297 g.zBaseURL = mprintf("http://%s%.*s", zHost, i, zCur);
1298 g.zTop = &g.zBaseURL[7+strlen(zHost)];
1299 g.zHttpsURL = mprintf("https://%s%.*s", zHost, i, zCur);
1300 }
1301 }
1302 if( db_is_writeable("repository") ){
1303 if( !db_exists("SELECT 1 FROM config WHERE name='baseurl:%q'", g.zBaseURL)){
1304 db_multi_exec("INSERT INTO config(name,value,mtime)"
1305
+11 -3
--- src/setup.c
+++ src/setup.c
@@ -66,11 +66,11 @@
6666
6767
/* Make sure the header contains <base href="...">. Issue a warning
6868
** if it does not. */
6969
if( !cgi_header_contains("<base href=") ){
7070
@ <p class="generalError"><b>Configuration Error:</b> Please add
71
- @ <tt>&lt;base href="$baseurl/$current_page"&gt;</tt> after
71
+ @ <tt>&lt;base href="$secureurl/$current_page"&gt;</tt> after
7272
@ <tt>&lt;head&gt;</tt> in the <a href="setup_header">HTML header</a>!</p>
7373
}
7474
7575
@ <table border="0" cellspacing="7">
7676
setup_menu_entry("Users", "setup_ulist",
@@ -973,10 +973,18 @@
973973
style_header("Access Control Settings");
974974
db_begin_transaction();
975975
@ <form action="%s(g.zTop)/setup_access" method="post"><div>
976976
login_insert_csrf_secret();
977977
@ <hr />
978
+ onoff_attribute("Redirect to HTTPS on the Login page",
979
+ "redirect-to-https", "redirhttps", 0, 0);
980
+ @ <p>When selected, force the use of HTTPS for the Login page.
981
+ @ <p>Details: When enabled, this option causes the $secureurl TH1
982
+ @ variable is set to an "https:" variant of $baseurl. Otherwise,
983
+ @ $secureurl is just an alias for $baseurl. Also when enabled, the
984
+ @ Login page redirects to https if accessed via http.
985
+ @ <hr />
978986
onoff_attribute("Require password for local access",
979987
"localauth", "localauth", 0, 0);
980988
@ <p>When enabled, the password sign-in is always required for
981989
@ web access. When disabled, unrestricted web access from 127.0.0.1
982990
@ is allowed for the <a href="%s(g.zTop)/help/ui">fossil ui</a> command or
@@ -1551,11 +1559,11 @@
15511559
char *zHead = strstr(z, "<head>");
15521560
if( strstr(z, "<base href=")==0 && zHead!=0 ){
15531561
char *zNew;
15541562
char *zTail = &zHead[6];
15551563
while( fossil_isspace(zTail[0]) ) zTail++;
1556
- zNew = mprintf("%.*s\n<base href=\"$baseurl/$current_page\" />\n%s",
1564
+ zNew = mprintf("%.*s\n<base href=\"$secureurl/$current_page\" />\n%s",
15571565
zHead+6-z, z, zTail);
15581566
cgi_replace_parameter("header", zNew);
15591567
db_set("header", zNew, 0);
15601568
}
15611569
}
@@ -1565,11 +1573,11 @@
15651573
15661574
/* Make sure the header contains <base href="...">. Issue a warning
15671575
** if it does not. */
15681576
if( !cgi_header_contains("<base href=") ){
15691577
@ <p class="generalError">Please add
1570
- @ <tt>&lt;base href="$baseurl/$current_page"&gt;</tt> after
1578
+ @ <tt>&lt;base href="$secureurl/$current_page"&gt;</tt> after
15711579
@ <tt>&lt;head&gt;</tt> in the header!
15721580
@ <input type="submit" name="fixbase" value="Add &lt;base&gt; Now"></p>
15731581
}
15741582
15751583
login_insert_csrf_secret();
15761584
--- src/setup.c
+++ src/setup.c
@@ -66,11 +66,11 @@
66
67 /* Make sure the header contains <base href="...">. Issue a warning
68 ** if it does not. */
69 if( !cgi_header_contains("<base href=") ){
70 @ <p class="generalError"><b>Configuration Error:</b> Please add
71 @ <tt>&lt;base href="$baseurl/$current_page"&gt;</tt> after
72 @ <tt>&lt;head&gt;</tt> in the <a href="setup_header">HTML header</a>!</p>
73 }
74
75 @ <table border="0" cellspacing="7">
76 setup_menu_entry("Users", "setup_ulist",
@@ -973,10 +973,18 @@
973 style_header("Access Control Settings");
974 db_begin_transaction();
975 @ <form action="%s(g.zTop)/setup_access" method="post"><div>
976 login_insert_csrf_secret();
977 @ <hr />
 
 
 
 
 
 
 
 
978 onoff_attribute("Require password for local access",
979 "localauth", "localauth", 0, 0);
980 @ <p>When enabled, the password sign-in is always required for
981 @ web access. When disabled, unrestricted web access from 127.0.0.1
982 @ is allowed for the <a href="%s(g.zTop)/help/ui">fossil ui</a> command or
@@ -1551,11 +1559,11 @@
1551 char *zHead = strstr(z, "<head>");
1552 if( strstr(z, "<base href=")==0 && zHead!=0 ){
1553 char *zNew;
1554 char *zTail = &zHead[6];
1555 while( fossil_isspace(zTail[0]) ) zTail++;
1556 zNew = mprintf("%.*s\n<base href=\"$baseurl/$current_page\" />\n%s",
1557 zHead+6-z, z, zTail);
1558 cgi_replace_parameter("header", zNew);
1559 db_set("header", zNew, 0);
1560 }
1561 }
@@ -1565,11 +1573,11 @@
1565
1566 /* Make sure the header contains <base href="...">. Issue a warning
1567 ** if it does not. */
1568 if( !cgi_header_contains("<base href=") ){
1569 @ <p class="generalError">Please add
1570 @ <tt>&lt;base href="$baseurl/$current_page"&gt;</tt> after
1571 @ <tt>&lt;head&gt;</tt> in the header!
1572 @ <input type="submit" name="fixbase" value="Add &lt;base&gt; Now"></p>
1573 }
1574
1575 login_insert_csrf_secret();
1576
--- src/setup.c
+++ src/setup.c
@@ -66,11 +66,11 @@
66
67 /* Make sure the header contains <base href="...">. Issue a warning
68 ** if it does not. */
69 if( !cgi_header_contains("<base href=") ){
70 @ <p class="generalError"><b>Configuration Error:</b> Please add
71 @ <tt>&lt;base href="$secureurl/$current_page"&gt;</tt> after
72 @ <tt>&lt;head&gt;</tt> in the <a href="setup_header">HTML header</a>!</p>
73 }
74
75 @ <table border="0" cellspacing="7">
76 setup_menu_entry("Users", "setup_ulist",
@@ -973,10 +973,18 @@
973 style_header("Access Control Settings");
974 db_begin_transaction();
975 @ <form action="%s(g.zTop)/setup_access" method="post"><div>
976 login_insert_csrf_secret();
977 @ <hr />
978 onoff_attribute("Redirect to HTTPS on the Login page",
979 "redirect-to-https", "redirhttps", 0, 0);
980 @ <p>When selected, force the use of HTTPS for the Login page.
981 @ <p>Details: When enabled, this option causes the $secureurl TH1
982 @ variable is set to an "https:" variant of $baseurl. Otherwise,
983 @ $secureurl is just an alias for $baseurl. Also when enabled, the
984 @ Login page redirects to https if accessed via http.
985 @ <hr />
986 onoff_attribute("Require password for local access",
987 "localauth", "localauth", 0, 0);
988 @ <p>When enabled, the password sign-in is always required for
989 @ web access. When disabled, unrestricted web access from 127.0.0.1
990 @ is allowed for the <a href="%s(g.zTop)/help/ui">fossil ui</a> command or
@@ -1551,11 +1559,11 @@
1559 char *zHead = strstr(z, "<head>");
1560 if( strstr(z, "<base href=")==0 && zHead!=0 ){
1561 char *zNew;
1562 char *zTail = &zHead[6];
1563 while( fossil_isspace(zTail[0]) ) zTail++;
1564 zNew = mprintf("%.*s\n<base href=\"$secureurl/$current_page\" />\n%s",
1565 zHead+6-z, z, zTail);
1566 cgi_replace_parameter("header", zNew);
1567 db_set("header", zNew, 0);
1568 }
1569 }
@@ -1565,11 +1573,11 @@
1573
1574 /* Make sure the header contains <base href="...">. Issue a warning
1575 ** if it does not. */
1576 if( !cgi_header_contains("<base href=") ){
1577 @ <p class="generalError">Please add
1578 @ <tt>&lt;base href="$secureurl/$current_page"&gt;</tt> after
1579 @ <tt>&lt;head&gt;</tt> in the header!
1580 @ <input type="submit" name="fixbase" value="Add &lt;base&gt; Now"></p>
1581 }
1582
1583 login_insert_csrf_secret();
1584
--- src/style.c
+++ src/style.c
@@ -301,10 +301,15 @@
301301
302302
/* Generate the header up through the main menu */
303303
Th_Store("project_name", db_get("project-name","Unnamed Fossil Project"));
304304
Th_Store("title", zTitle);
305305
Th_Store("baseurl", g.zBaseURL);
306
+ if( login_wants_https_redirect() ){
307
+ Th_Store("secureurl", g.zHttpsURL);
308
+ }else{
309
+ Th_Store("secureurl", g.zBaseURL);
310
+ }
306311
Th_Store("home", g.zTop);
307312
Th_Store("index_page", db_get("index-page","/home"));
308313
if( local_zCurrentPage==0 ) style_set_current_page("%T", g.zPath);
309314
Th_Store("current_page", local_zCurrentPage);
310315
Th_Store("csrf_token", g.zCsrfToken);
@@ -1259,10 +1264,11 @@
12591264
12601265
/* Process through TH1 in order to give an opportunity to substitute
12611266
** variables such as $baseurl.
12621267
*/
12631268
Th_Store("baseurl", g.zBaseURL);
1269
+ Th_Store("httpsurl", g.zHttpsURL);
12641270
Th_Store("home", g.zTop);
12651271
image_url_var("logo");
12661272
image_url_var("background");
12671273
Th_Render(blob_str(&css));
12681274
@@ -1304,10 +1310,11 @@
13041310
}
13051311
#if !defined(_WIN32)
13061312
@ uid=%d(getuid()), gid=%d(getgid())<br />
13071313
#endif
13081314
@ g.zBaseURL = %h(g.zBaseURL)<br />
1315
+ @ g.zHttpsURL = %h(g.zHttpsURL)<br />
13091316
@ g.zTop = %h(g.zTop)<br />
13101317
for(i=0, c='a'; c<='z'; c++){
13111318
if( login_has_capability(&c, 1) ) zCap[i++] = c;
13121319
}
13131320
zCap[i] = 0;
13141321
--- src/style.c
+++ src/style.c
@@ -301,10 +301,15 @@
301
302 /* Generate the header up through the main menu */
303 Th_Store("project_name", db_get("project-name","Unnamed Fossil Project"));
304 Th_Store("title", zTitle);
305 Th_Store("baseurl", g.zBaseURL);
 
 
 
 
 
306 Th_Store("home", g.zTop);
307 Th_Store("index_page", db_get("index-page","/home"));
308 if( local_zCurrentPage==0 ) style_set_current_page("%T", g.zPath);
309 Th_Store("current_page", local_zCurrentPage);
310 Th_Store("csrf_token", g.zCsrfToken);
@@ -1259,10 +1264,11 @@
1259
1260 /* Process through TH1 in order to give an opportunity to substitute
1261 ** variables such as $baseurl.
1262 */
1263 Th_Store("baseurl", g.zBaseURL);
 
1264 Th_Store("home", g.zTop);
1265 image_url_var("logo");
1266 image_url_var("background");
1267 Th_Render(blob_str(&css));
1268
@@ -1304,10 +1310,11 @@
1304 }
1305 #if !defined(_WIN32)
1306 @ uid=%d(getuid()), gid=%d(getgid())<br />
1307 #endif
1308 @ g.zBaseURL = %h(g.zBaseURL)<br />
 
1309 @ g.zTop = %h(g.zTop)<br />
1310 for(i=0, c='a'; c<='z'; c++){
1311 if( login_has_capability(&c, 1) ) zCap[i++] = c;
1312 }
1313 zCap[i] = 0;
1314
--- src/style.c
+++ src/style.c
@@ -301,10 +301,15 @@
301
302 /* Generate the header up through the main menu */
303 Th_Store("project_name", db_get("project-name","Unnamed Fossil Project"));
304 Th_Store("title", zTitle);
305 Th_Store("baseurl", g.zBaseURL);
306 if( login_wants_https_redirect() ){
307 Th_Store("secureurl", g.zHttpsURL);
308 }else{
309 Th_Store("secureurl", g.zBaseURL);
310 }
311 Th_Store("home", g.zTop);
312 Th_Store("index_page", db_get("index-page","/home"));
313 if( local_zCurrentPage==0 ) style_set_current_page("%T", g.zPath);
314 Th_Store("current_page", local_zCurrentPage);
315 Th_Store("csrf_token", g.zCsrfToken);
@@ -1259,10 +1264,11 @@
1264
1265 /* Process through TH1 in order to give an opportunity to substitute
1266 ** variables such as $baseurl.
1267 */
1268 Th_Store("baseurl", g.zBaseURL);
1269 Th_Store("httpsurl", g.zHttpsURL);
1270 Th_Store("home", g.zTop);
1271 image_url_var("logo");
1272 image_url_var("background");
1273 Th_Render(blob_str(&css));
1274
@@ -1304,10 +1310,11 @@
1310 }
1311 #if !defined(_WIN32)
1312 @ uid=%d(getuid()), gid=%d(getgid())<br />
1313 #endif
1314 @ g.zBaseURL = %h(g.zBaseURL)<br />
1315 @ g.zHttpsURL = %h(g.zHttpsURL)<br />
1316 @ g.zTop = %h(g.zTop)<br />
1317 for(i=0, c='a'; c<='z'; c++){
1318 if( login_has_capability(&c, 1) ) zCap[i++] = c;
1319 }
1320 zCap[i] = 0;
1321

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button