Fossil SCM

Merged in trunk changes

wyoung 2019-08-29 00:31 caps-doc merge
Commit 493254b2e7753546c4a4c0996c16a9f53cf1dee9246884380694be578229919a
--- skins/default/header.txt
+++ skins/default/header.txt
@@ -17,11 +17,11 @@
1717
html "<a href='$home$url' class='active $cls'>$name</a>\n"
1818
} else {
1919
html "<a href='$home$url' class='$cls'>$name</a>\n"
2020
}
2121
}
22
-html "<a id='hbbtn' href='#'>&#9776;</a>"
22
+html "<a id='hbbtn' href='/sitemap'>&#9776;</a>"
2323
menulink $index_page Home {}
2424
if {[anycap jor]} {
2525
menulink /timeline Timeline {}
2626
}
2727
if {[hascap oh]} {
2828
--- skins/default/header.txt
+++ skins/default/header.txt
@@ -17,11 +17,11 @@
17 html "<a href='$home$url' class='active $cls'>$name</a>\n"
18 } else {
19 html "<a href='$home$url' class='$cls'>$name</a>\n"
20 }
21 }
22 html "<a id='hbbtn' href='#'>&#9776;</a>"
23 menulink $index_page Home {}
24 if {[anycap jor]} {
25 menulink /timeline Timeline {}
26 }
27 if {[hascap oh]} {
28
--- skins/default/header.txt
+++ skins/default/header.txt
@@ -17,11 +17,11 @@
17 html "<a href='$home$url' class='active $cls'>$name</a>\n"
18 } else {
19 html "<a href='$home$url' class='$cls'>$name</a>\n"
20 }
21 }
22 html "<a id='hbbtn' href='/sitemap'>&#9776;</a>"
23 menulink $index_page Home {}
24 if {[anycap jor]} {
25 menulink /timeline Timeline {}
26 }
27 if {[hascap oh]} {
28
--- src/checkin.c
+++ src/checkin.c
@@ -1939,10 +1939,11 @@
19391939
/*
19401940
** COMMAND: ci*
19411941
** COMMAND: commit
19421942
**
19431943
** Usage: %fossil commit ?OPTIONS? ?FILE...?
1944
+** or: %fossil ci ?OPTIONS? ?FILE...?
19441945
**
19451946
** Create a new version containing all of the changes in the current
19461947
** checkout. You will be prompted to enter a check-in comment unless
19471948
** the comment has been specified on the command-line using "-m" or a
19481949
** file containing the comment using -M. The editor defined in the
19491950
--- src/checkin.c
+++ src/checkin.c
@@ -1939,10 +1939,11 @@
1939 /*
1940 ** COMMAND: ci*
1941 ** COMMAND: commit
1942 **
1943 ** Usage: %fossil commit ?OPTIONS? ?FILE...?
 
1944 **
1945 ** Create a new version containing all of the changes in the current
1946 ** checkout. You will be prompted to enter a check-in comment unless
1947 ** the comment has been specified on the command-line using "-m" or a
1948 ** file containing the comment using -M. The editor defined in the
1949
--- src/checkin.c
+++ src/checkin.c
@@ -1939,10 +1939,11 @@
1939 /*
1940 ** COMMAND: ci*
1941 ** COMMAND: commit
1942 **
1943 ** Usage: %fossil commit ?OPTIONS? ?FILE...?
1944 ** or: %fossil ci ?OPTIONS? ?FILE...?
1945 **
1946 ** Create a new version containing all of the changes in the current
1947 ** checkout. You will be prompted to enter a check-in comment unless
1948 ** the comment has been specified on the command-line using "-m" or a
1949 ** file containing the comment using -M. The editor defined in the
1950
+21 -7
--- src/db.c
+++ src/db.c
@@ -117,10 +117,11 @@
117117
/*
118118
** Arrange for the given file to be deleted on a failure.
119119
*/
120120
void db_delete_on_failure(const char *zFilename){
121121
assert( db.nDeleteOnFail<count(db.azDeleteOnFail) );
122
+ if( zFilename==0 ) return;
122123
db.azDeleteOnFail[db.nDeleteOnFail++] = fossil_strdup(zFilename);
123124
}
124125
125126
/*
126127
** Return the transaction nesting depth. 0 means we are currently
@@ -799,10 +800,13 @@
799800
}
800801
801802
/*
802803
** Initialize a new database file with the given schema. If anything
803804
** goes wrong, call db_err() to exit.
805
+**
806
+** If zFilename is NULL, then create an empty repository in an in-memory
807
+** database.
804808
*/
805809
void db_init_database(
806810
const char *zFileName, /* Name of database file to create */
807811
const char *zSchema, /* First part of schema */
808812
... /* Additional SQL to run. Terminate with NULL. */
@@ -810,11 +814,11 @@
810814
sqlite3 *db;
811815
int rc;
812816
const char *zSql;
813817
va_list ap;
814818
815
- db = db_open(zFileName);
819
+ db = db_open(zFileName ? zFileName : ":memory:");
816820
sqlite3_exec(db, "BEGIN EXCLUSIVE", 0, 0, 0);
817821
rc = sqlite3_exec(db, zSchema, 0, 0, 0);
818822
if( rc!=SQLITE_OK ){
819823
db_err("%s", sqlite3_errmsg(db));
820824
}
@@ -825,11 +829,15 @@
825829
db_err("%s", sqlite3_errmsg(db));
826830
}
827831
}
828832
va_end(ap);
829833
sqlite3_exec(db, "COMMIT", 0, 0, 0);
830
- sqlite3_close(db);
834
+ if( zFileName || g.db!=0 ){
835
+ sqlite3_close(db);
836
+ }else{
837
+ g.db = db;
838
+ }
831839
}
832840
833841
/*
834842
** Function to return the number of seconds since 1970. This is
835843
** the same as strftime('%s','now') but is more compact.
@@ -1768,12 +1776,13 @@
17681776
17691777
/*
17701778
** Flags for the db_find_and_open_repository() function.
17711779
*/
17721780
#if INTERFACE
1773
-#define OPEN_OK_NOT_FOUND 0x001 /* Do not error out if not found */
1774
-#define OPEN_ANY_SCHEMA 0x002 /* Do not error if schema is wrong */
1781
+#define OPEN_OK_NOT_FOUND 0x001 /* Do not error out if not found */
1782
+#define OPEN_ANY_SCHEMA 0x002 /* Do not error if schema is wrong */
1783
+#define OPEN_SUBSTITUTE 0x004 /* Fake in-memory repo if not found */
17751784
#endif
17761785
17771786
/*
17781787
** Try to find the repository and open it. Use the -R or --repository
17791788
** option to locate the repository. If no such option is available, then
@@ -1802,11 +1811,16 @@
18021811
if( g.repositoryOpen ){
18031812
if( (bFlags & OPEN_ANY_SCHEMA)==0 ) db_verify_schema();
18041813
return;
18051814
}
18061815
rep_not_found:
1807
- if( (bFlags & OPEN_OK_NOT_FOUND)==0 ){
1816
+ if( bFlags & OPEN_OK_NOT_FOUND ){
1817
+ /* No errors if the database is not found */
1818
+ if( bFlags & OPEN_SUBSTITUTE ){
1819
+ db_create_repository(0);
1820
+ }
1821
+ }else{
18081822
#ifdef FOSSIL_ENABLE_JSON
18091823
g.json.resultCode = FSL_JSON_E_DB_NOT_FOUND;
18101824
#endif
18111825
if( nArgUsed==0 ){
18121826
fossil_fatal("use --repository or -R to specify the repository database");
@@ -2032,12 +2046,12 @@
20322046
}
20332047
db_multi_exec(
20342048
"INSERT OR IGNORE INTO user(login, info) VALUES(%Q,'')", zUser
20352049
);
20362050
db_multi_exec(
2037
- "UPDATE user SET cap='s', pw=lower(hex(randomblob(3)))"
2038
- " WHERE login=%Q", zUser
2051
+ "UPDATE user SET cap='s', pw=%Q"
2052
+ " WHERE login=%Q", fossil_random_password(10), zUser
20392053
);
20402054
if( !setupUserOnly ){
20412055
db_multi_exec(
20422056
"INSERT OR IGNORE INTO user(login,pw,cap,info)"
20432057
" VALUES('anonymous',hex(randomblob(8)),'hmnc','Anon');"
20442058
--- src/db.c
+++ src/db.c
@@ -117,10 +117,11 @@
117 /*
118 ** Arrange for the given file to be deleted on a failure.
119 */
120 void db_delete_on_failure(const char *zFilename){
121 assert( db.nDeleteOnFail<count(db.azDeleteOnFail) );
 
122 db.azDeleteOnFail[db.nDeleteOnFail++] = fossil_strdup(zFilename);
123 }
124
125 /*
126 ** Return the transaction nesting depth. 0 means we are currently
@@ -799,10 +800,13 @@
799 }
800
801 /*
802 ** Initialize a new database file with the given schema. If anything
803 ** goes wrong, call db_err() to exit.
 
 
 
804 */
805 void db_init_database(
806 const char *zFileName, /* Name of database file to create */
807 const char *zSchema, /* First part of schema */
808 ... /* Additional SQL to run. Terminate with NULL. */
@@ -810,11 +814,11 @@
810 sqlite3 *db;
811 int rc;
812 const char *zSql;
813 va_list ap;
814
815 db = db_open(zFileName);
816 sqlite3_exec(db, "BEGIN EXCLUSIVE", 0, 0, 0);
817 rc = sqlite3_exec(db, zSchema, 0, 0, 0);
818 if( rc!=SQLITE_OK ){
819 db_err("%s", sqlite3_errmsg(db));
820 }
@@ -825,11 +829,15 @@
825 db_err("%s", sqlite3_errmsg(db));
826 }
827 }
828 va_end(ap);
829 sqlite3_exec(db, "COMMIT", 0, 0, 0);
830 sqlite3_close(db);
 
 
 
 
831 }
832
833 /*
834 ** Function to return the number of seconds since 1970. This is
835 ** the same as strftime('%s','now') but is more compact.
@@ -1768,12 +1776,13 @@
1768
1769 /*
1770 ** Flags for the db_find_and_open_repository() function.
1771 */
1772 #if INTERFACE
1773 #define OPEN_OK_NOT_FOUND 0x001 /* Do not error out if not found */
1774 #define OPEN_ANY_SCHEMA 0x002 /* Do not error if schema is wrong */
 
1775 #endif
1776
1777 /*
1778 ** Try to find the repository and open it. Use the -R or --repository
1779 ** option to locate the repository. If no such option is available, then
@@ -1802,11 +1811,16 @@
1802 if( g.repositoryOpen ){
1803 if( (bFlags & OPEN_ANY_SCHEMA)==0 ) db_verify_schema();
1804 return;
1805 }
1806 rep_not_found:
1807 if( (bFlags & OPEN_OK_NOT_FOUND)==0 ){
 
 
 
 
 
1808 #ifdef FOSSIL_ENABLE_JSON
1809 g.json.resultCode = FSL_JSON_E_DB_NOT_FOUND;
1810 #endif
1811 if( nArgUsed==0 ){
1812 fossil_fatal("use --repository or -R to specify the repository database");
@@ -2032,12 +2046,12 @@
2032 }
2033 db_multi_exec(
2034 "INSERT OR IGNORE INTO user(login, info) VALUES(%Q,'')", zUser
2035 );
2036 db_multi_exec(
2037 "UPDATE user SET cap='s', pw=lower(hex(randomblob(3)))"
2038 " WHERE login=%Q", zUser
2039 );
2040 if( !setupUserOnly ){
2041 db_multi_exec(
2042 "INSERT OR IGNORE INTO user(login,pw,cap,info)"
2043 " VALUES('anonymous',hex(randomblob(8)),'hmnc','Anon');"
2044
--- src/db.c
+++ src/db.c
@@ -117,10 +117,11 @@
117 /*
118 ** Arrange for the given file to be deleted on a failure.
119 */
120 void db_delete_on_failure(const char *zFilename){
121 assert( db.nDeleteOnFail<count(db.azDeleteOnFail) );
122 if( zFilename==0 ) return;
123 db.azDeleteOnFail[db.nDeleteOnFail++] = fossil_strdup(zFilename);
124 }
125
126 /*
127 ** Return the transaction nesting depth. 0 means we are currently
@@ -799,10 +800,13 @@
800 }
801
802 /*
803 ** Initialize a new database file with the given schema. If anything
804 ** goes wrong, call db_err() to exit.
805 **
806 ** If zFilename is NULL, then create an empty repository in an in-memory
807 ** database.
808 */
809 void db_init_database(
810 const char *zFileName, /* Name of database file to create */
811 const char *zSchema, /* First part of schema */
812 ... /* Additional SQL to run. Terminate with NULL. */
@@ -810,11 +814,11 @@
814 sqlite3 *db;
815 int rc;
816 const char *zSql;
817 va_list ap;
818
819 db = db_open(zFileName ? zFileName : ":memory:");
820 sqlite3_exec(db, "BEGIN EXCLUSIVE", 0, 0, 0);
821 rc = sqlite3_exec(db, zSchema, 0, 0, 0);
822 if( rc!=SQLITE_OK ){
823 db_err("%s", sqlite3_errmsg(db));
824 }
@@ -825,11 +829,15 @@
829 db_err("%s", sqlite3_errmsg(db));
830 }
831 }
832 va_end(ap);
833 sqlite3_exec(db, "COMMIT", 0, 0, 0);
834 if( zFileName || g.db!=0 ){
835 sqlite3_close(db);
836 }else{
837 g.db = db;
838 }
839 }
840
841 /*
842 ** Function to return the number of seconds since 1970. This is
843 ** the same as strftime('%s','now') but is more compact.
@@ -1768,12 +1776,13 @@
1776
1777 /*
1778 ** Flags for the db_find_and_open_repository() function.
1779 */
1780 #if INTERFACE
1781 #define OPEN_OK_NOT_FOUND 0x001 /* Do not error out if not found */
1782 #define OPEN_ANY_SCHEMA 0x002 /* Do not error if schema is wrong */
1783 #define OPEN_SUBSTITUTE 0x004 /* Fake in-memory repo if not found */
1784 #endif
1785
1786 /*
1787 ** Try to find the repository and open it. Use the -R or --repository
1788 ** option to locate the repository. If no such option is available, then
@@ -1802,11 +1811,16 @@
1811 if( g.repositoryOpen ){
1812 if( (bFlags & OPEN_ANY_SCHEMA)==0 ) db_verify_schema();
1813 return;
1814 }
1815 rep_not_found:
1816 if( bFlags & OPEN_OK_NOT_FOUND ){
1817 /* No errors if the database is not found */
1818 if( bFlags & OPEN_SUBSTITUTE ){
1819 db_create_repository(0);
1820 }
1821 }else{
1822 #ifdef FOSSIL_ENABLE_JSON
1823 g.json.resultCode = FSL_JSON_E_DB_NOT_FOUND;
1824 #endif
1825 if( nArgUsed==0 ){
1826 fossil_fatal("use --repository or -R to specify the repository database");
@@ -2032,12 +2046,12 @@
2046 }
2047 db_multi_exec(
2048 "INSERT OR IGNORE INTO user(login, info) VALUES(%Q,'')", zUser
2049 );
2050 db_multi_exec(
2051 "UPDATE user SET cap='s', pw=%Q"
2052 " WHERE login=%Q", fossil_random_password(10), zUser
2053 );
2054 if( !setupUserOnly ){
2055 db_multi_exec(
2056 "INSERT OR IGNORE INTO user(login,pw,cap,info)"
2057 " VALUES('anonymous',hex(randomblob(8)),'hmnc','Anon');"
2058
+2 -2
--- src/login.c
+++ src/login.c
@@ -2018,11 +2018,11 @@
20182018
const char *zNewName = find_option("name",0,1);
20192019
const char *zOther;
20202020
char *zErr = 0;
20212021
verify_all_options();
20222022
if( g.argc!=4 ){
2023
- fossil_fatal("unknown extra arguments to \"login-group add\"");
2023
+ fossil_fatal("unknown extra arguments to \"login-group join\"");
20242024
}
20252025
zOther = g.argv[3];
20262026
login_group_leave(&zErr);
20272027
sqlite3_free(zErr);
20282028
zErr = 0;
@@ -2042,11 +2042,11 @@
20422042
login_group_leave(&zErr);
20432043
if( zErr ) fossil_fatal("Oops: %s", zErr);
20442044
return;
20452045
}
20462046
}else{
2047
- fossil_fatal("unknown command \"%s\" - should be \"add\" or \"leave\"",
2047
+ fossil_fatal("unknown command \"%s\" - should be \"join\" or \"leave\"",
20482048
zCmd);
20492049
}
20502050
}
20512051
/* Show the current login group information */
20522052
zLGName = login_group_name();
20532053
--- src/login.c
+++ src/login.c
@@ -2018,11 +2018,11 @@
2018 const char *zNewName = find_option("name",0,1);
2019 const char *zOther;
2020 char *zErr = 0;
2021 verify_all_options();
2022 if( g.argc!=4 ){
2023 fossil_fatal("unknown extra arguments to \"login-group add\"");
2024 }
2025 zOther = g.argv[3];
2026 login_group_leave(&zErr);
2027 sqlite3_free(zErr);
2028 zErr = 0;
@@ -2042,11 +2042,11 @@
2042 login_group_leave(&zErr);
2043 if( zErr ) fossil_fatal("Oops: %s", zErr);
2044 return;
2045 }
2046 }else{
2047 fossil_fatal("unknown command \"%s\" - should be \"add\" or \"leave\"",
2048 zCmd);
2049 }
2050 }
2051 /* Show the current login group information */
2052 zLGName = login_group_name();
2053
--- src/login.c
+++ src/login.c
@@ -2018,11 +2018,11 @@
2018 const char *zNewName = find_option("name",0,1);
2019 const char *zOther;
2020 char *zErr = 0;
2021 verify_all_options();
2022 if( g.argc!=4 ){
2023 fossil_fatal("unknown extra arguments to \"login-group join\"");
2024 }
2025 zOther = g.argv[3];
2026 login_group_leave(&zErr);
2027 sqlite3_free(zErr);
2028 zErr = 0;
@@ -2042,11 +2042,11 @@
2042 login_group_leave(&zErr);
2043 if( zErr ) fossil_fatal("Oops: %s", zErr);
2044 return;
2045 }
2046 }else{
2047 fossil_fatal("unknown command \"%s\" - should be \"join\" or \"leave\"",
2048 zCmd);
2049 }
2050 }
2051 /* Show the current login group information */
2052 zLGName = login_group_name();
2053
--- src/markdown_html.c
+++ src/markdown_html.c
@@ -433,11 +433,15 @@
433433
char zClose[20];
434434
435435
if( zLink==0 || zLink[0]==0 ){
436436
zClose[0] = 0;
437437
}else{
438
- wiki_resolve_hyperlink(ob, 0, zLink, zClose, sizeof(zClose), 0, zTitle);
438
+ static const int flags =
439
+ WIKI_NOBADLINKS |
440
+ WIKI_MARKDOWNLINKS
441
+ ;
442
+ wiki_resolve_hyperlink(ob, flags, zLink, zClose, sizeof(zClose), 0, zTitle);
439443
}
440444
if( blob_size(content)==0 ){
441445
if( link ) BLOB_APPEND_BLOB(ob, link);
442446
}else{
443447
BLOB_APPEND_BLOB(ob, content);
444448
--- src/markdown_html.c
+++ src/markdown_html.c
@@ -433,11 +433,15 @@
433 char zClose[20];
434
435 if( zLink==0 || zLink[0]==0 ){
436 zClose[0] = 0;
437 }else{
438 wiki_resolve_hyperlink(ob, 0, zLink, zClose, sizeof(zClose), 0, zTitle);
 
 
 
 
439 }
440 if( blob_size(content)==0 ){
441 if( link ) BLOB_APPEND_BLOB(ob, link);
442 }else{
443 BLOB_APPEND_BLOB(ob, content);
444
--- src/markdown_html.c
+++ src/markdown_html.c
@@ -433,11 +433,15 @@
433 char zClose[20];
434
435 if( zLink==0 || zLink[0]==0 ){
436 zClose[0] = 0;
437 }else{
438 static const int flags =
439 WIKI_NOBADLINKS |
440 WIKI_MARKDOWNLINKS
441 ;
442 wiki_resolve_hyperlink(ob, flags, zLink, zClose, sizeof(zClose), 0, zTitle);
443 }
444 if( blob_size(content)==0 ){
445 if( link ) BLOB_APPEND_BLOB(ob, link);
446 }else{
447 BLOB_APPEND_BLOB(ob, content);
448
+53
--- src/util.c
+++ src/util.c
@@ -525,5 +525,58 @@
525525
fossil_panic("pledge(\"%s\",NULL) fails with errno=%d",
526526
promises, (int)errno);
527527
}
528528
}
529529
#endif /* defined(HAVE_PLEDGE) */
530
+
531
+/*
532
+** Construct a random password and return it as a string. N is the
533
+** recommended number of characters for the password.
534
+**
535
+** Space to hold the returned string is obtained from fossil_malloc()
536
+** and should be freed by the caller.
537
+*/
538
+char *fossil_random_password(int N){
539
+ char zSrc[60];
540
+ int nSrc;
541
+ int i;
542
+ char z[60];
543
+
544
+ /* Source characters for the password. Omit characters like "0", "O",
545
+ ** "1" and "I" that might be easily confused */
546
+ static const char zAlphabet[] =
547
+ /* 0 1 2 3 4 5 */
548
+ /* 123456789 123456789 123456789 123456789 123456789 123456 */
549
+ "23456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ";
550
+
551
+ if( N<8 ) N = 8;
552
+ else if( N>sizeof(zAlphabet)-2 ) N = sizeof(zAlphabet)-2;
553
+ nSrc = sizeof(zAlphabet) - 1;
554
+ memcpy(zSrc, zAlphabet, nSrc);
555
+
556
+ for(i=0; i<N; i++){
557
+ unsigned r;
558
+ sqlite3_randomness(sizeof(r), &r);
559
+ r %= nSrc;
560
+ z[i] = zSrc[r];
561
+ zSrc[r] = zSrc[--nSrc];
562
+ }
563
+ z[i] = 0;
564
+ return fossil_strdup(z);
565
+}
566
+
567
+/*
568
+** COMMAND: test-random-password
569
+**
570
+** Usage: %fossil test-random-password ?N?
571
+**
572
+** Generate a random password string of approximately N characters in length.
573
+** If N is omitted, use 10. Values of N less than 8 are changed to 8
574
+** and greater than 55 and changed to 55.
575
+*/
576
+void test_random_password(void){
577
+ int N = 10;
578
+ if( g.argc>=3 ){
579
+ N = atoi(g.argv[2]);
580
+ }
581
+ fossil_print("%s\n", fossil_random_password(N));
582
+}
530583
--- src/util.c
+++ src/util.c
@@ -525,5 +525,58 @@
525 fossil_panic("pledge(\"%s\",NULL) fails with errno=%d",
526 promises, (int)errno);
527 }
528 }
529 #endif /* defined(HAVE_PLEDGE) */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
530
--- src/util.c
+++ src/util.c
@@ -525,5 +525,58 @@
525 fossil_panic("pledge(\"%s\",NULL) fails with errno=%d",
526 promises, (int)errno);
527 }
528 }
529 #endif /* defined(HAVE_PLEDGE) */
530
531 /*
532 ** Construct a random password and return it as a string. N is the
533 ** recommended number of characters for the password.
534 **
535 ** Space to hold the returned string is obtained from fossil_malloc()
536 ** and should be freed by the caller.
537 */
538 char *fossil_random_password(int N){
539 char zSrc[60];
540 int nSrc;
541 int i;
542 char z[60];
543
544 /* Source characters for the password. Omit characters like "0", "O",
545 ** "1" and "I" that might be easily confused */
546 static const char zAlphabet[] =
547 /* 0 1 2 3 4 5 */
548 /* 123456789 123456789 123456789 123456789 123456789 123456 */
549 "23456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ";
550
551 if( N<8 ) N = 8;
552 else if( N>sizeof(zAlphabet)-2 ) N = sizeof(zAlphabet)-2;
553 nSrc = sizeof(zAlphabet) - 1;
554 memcpy(zSrc, zAlphabet, nSrc);
555
556 for(i=0; i<N; i++){
557 unsigned r;
558 sqlite3_randomness(sizeof(r), &r);
559 r %= nSrc;
560 z[i] = zSrc[r];
561 zSrc[r] = zSrc[--nSrc];
562 }
563 z[i] = 0;
564 return fossil_strdup(z);
565 }
566
567 /*
568 ** COMMAND: test-random-password
569 **
570 ** Usage: %fossil test-random-password ?N?
571 **
572 ** Generate a random password string of approximately N characters in length.
573 ** If N is omitted, use 10. Values of N less than 8 are changed to 8
574 ** and greater than 55 and changed to 55.
575 */
576 void test_random_password(void){
577 int N = 10;
578 if( g.argc>=3 ){
579 N = atoi(g.argv[2]);
580 }
581 fossil_print("%s\n", fossil_random_password(N));
582 }
583
+1
--- src/wiki.c
+++ src/wiki.c
@@ -1629,10 +1629,11 @@
16291629
** Render markdown wiki from FILE to stdout.
16301630
**
16311631
*/
16321632
void test_markdown_render(void){
16331633
Blob in, out;
1634
+ db_find_and_open_repository(OPEN_OK_NOT_FOUND|OPEN_SUBSTITUTE,0);
16341635
verify_all_options();
16351636
if( g.argc!=3 ) usage("FILE");
16361637
blob_zero(&out);
16371638
blob_read_from_file(&in, g.argv[2], ExtFILE);
16381639
markdown_to_html(&in, 0, &out);
16391640
--- src/wiki.c
+++ src/wiki.c
@@ -1629,10 +1629,11 @@
1629 ** Render markdown wiki from FILE to stdout.
1630 **
1631 */
1632 void test_markdown_render(void){
1633 Blob in, out;
 
1634 verify_all_options();
1635 if( g.argc!=3 ) usage("FILE");
1636 blob_zero(&out);
1637 blob_read_from_file(&in, g.argv[2], ExtFILE);
1638 markdown_to_html(&in, 0, &out);
1639
--- src/wiki.c
+++ src/wiki.c
@@ -1629,10 +1629,11 @@
1629 ** Render markdown wiki from FILE to stdout.
1630 **
1631 */
1632 void test_markdown_render(void){
1633 Blob in, out;
1634 db_find_and_open_repository(OPEN_OK_NOT_FOUND|OPEN_SUBSTITUTE,0);
1635 verify_all_options();
1636 if( g.argc!=3 ) usage("FILE");
1637 blob_zero(&out);
1638 blob_read_from_file(&in, g.argv[2], ExtFILE);
1639 markdown_to_html(&in, 0, &out);
1640
+24 -19
--- src/wikiformat.c
+++ src/wikiformat.c
@@ -30,10 +30,11 @@
3030
#define WIKI_NOBLOCK 0x004 /* No block markup of any kind */
3131
#define WIKI_BUTTONS 0x008 /* Allow sub-menu buttons */
3232
#define WIKI_NOBADLINKS 0x010 /* Ignore broken hyperlinks */
3333
#define WIKI_LINKSONLY 0x020 /* No markup. Only decorate links */
3434
#define WIKI_NEWLINE 0x040 /* Honor \n - break lines at each \n */
35
+#define WIKI_MARKDOWNLINKS 0x080 /* Resolve hyperlinks as in markdown */
3536
#endif
3637
3738
3839
/*
3940
** These are the only markup attributes allowed.
@@ -1211,20 +1212,22 @@
12111212
** [http://www.fossil-scm.org/]
12121213
** [https://www.fossil-scm.org/]
12131214
** [ftp://www.fossil-scm.org/]
12141215
** [mailto:[email protected]]
12151216
**
1216
-** [/path]
1217
+** [/path] -> Refers to the root of the Fossil hierarchy, not
1218
+** the root of the URI domain
12171219
**
12181220
** [./relpath]
1221
+** [../relpath]
12191222
**
1220
-** [WikiPageName]
1221
-** [wiki:WikiPageName]
1223
+** [#fragment]
12221224
**
12231225
** [0123456789abcdef]
12241226
**
1225
-** [#fragment]
1227
+** [WikiPageName]
1228
+** [wiki:WikiPageName]
12261229
**
12271230
** [2010-02-27 07:13]
12281231
*/
12291232
void wiki_resolve_hyperlink(
12301233
Blob *pOut, /* Write the HTML output here */
@@ -1296,24 +1299,35 @@
12961299
blob_appendf(pOut, "%z[",xhref(zExtraNS, "%R/info/%s", zTarget));
12971300
zTerm = "]</a>";
12981301
}else{
12991302
zTerm = "";
13001303
}
1301
- }else if( strlen(zTarget)>=10 && fossil_isdigit(zTarget[0]) && zTarget[4]=='-'
1302
- && db_int(0, "SELECT datetime(%Q) NOT NULL", zTarget) ){
1303
- blob_appendf(pOut, "<a href=\"%R/timeline?c=%T\"%s>", zTarget, zExtra);
13041304
}else if( (z = validWikiPageName(mFlags, zTarget))!=0 ){
1305
+ /* The link is to a valid wiki page name */
13051306
const char *zOverride = wiki_is_overridden(zTarget);
13061307
if( zOverride ){
13071308
blob_appendf(pOut, "<a href=\"%R/info/%S\"%s>", zOverride, zExtra);
13081309
}else{
13091310
blob_appendf(pOut, "<a href=\"%R/wiki?name=%T\"%s>", z, zExtra);
13101311
}
1311
- }else if( zOrig && zTarget>=&zOrig[2] && !fossil_isspace(zTarget[-2]) ){
1312
- /* Probably an array subscript in code */
1312
+ }else if( strlen(zTarget)>=10 && fossil_isdigit(zTarget[0]) && zTarget[4]=='-'
1313
+ && db_int(0, "SELECT datetime(%Q) NOT NULL", zTarget) ){
1314
+ /* Dates or date-and-times in ISO8610 resolve to a link to the
1315
+ ** timeline for that date */
1316
+ blob_appendf(pOut, "<a href=\"%R/timeline?c=%T\"%s>", zTarget, zExtra);
1317
+ }else if( mFlags & WIKI_MARKDOWNLINKS ){
1318
+ /* If none of the above, and if rendering links for markdown, then
1319
+ ** create a link to the literal text of the target */
1320
+ blob_appendf(pOut, "<a href=\"%h\"%s>", zTarget, zExtra);
1321
+ }else if( zOrig && zTarget>=&zOrig[2]
1322
+ && zTarget[-1]=='[' && !fossil_isspace(zTarget[-2]) ){
1323
+ /* If the hyperlink markup is not preceded by whitespace, then it
1324
+ ** is probably a C-language subscript or similar, not really a
1325
+ ** hyperlink. Just ignore it. */
13131326
zTerm = "";
13141327
}else if( (mFlags & (WIKI_NOBADLINKS|WIKI_LINKSONLY))!=0 ){
1328
+ /* Also ignore the link if various flags are set */
13151329
zTerm = "";
13161330
}else{
13171331
blob_appendf(pOut, "<span class=\"brokenlink\">[%h]", zTarget);
13181332
zTerm = "</span>";
13191333
}
@@ -1758,20 +1772,10 @@
17581772
}
17591773
blob_append(renderer.pOut, "\n", 1);
17601774
free(renderer.aStack);
17611775
}
17621776
1763
-/*
1764
-** Send a string as wiki to CGI output.
1765
-*/
1766
-void wiki_write(const char *zIn, int flags){
1767
- Blob in;
1768
- blob_init(&in, zIn, -1);
1769
- wiki_convert(&in, 0, flags);
1770
- blob_reset(&in);
1771
-}
1772
-
17731777
/*
17741778
** COMMAND: test-wiki-render
17751779
**
17761780
** Usage: %fossil test-wiki-render FILE [OPTIONS]
17771781
**
@@ -1790,10 +1794,11 @@
17901794
if( find_option("htmlonly",0,0)!=0 ) flags |= WIKI_HTMLONLY;
17911795
if( find_option("linksonly",0,0)!=0 ) flags |= WIKI_LINKSONLY;
17921796
if( find_option("nobadlinks",0,0)!=0 ) flags |= WIKI_NOBADLINKS;
17931797
if( find_option("inline",0,0)!=0 ) flags |= WIKI_INLINE;
17941798
if( find_option("noblock",0,0)!=0 ) flags |= WIKI_NOBLOCK;
1799
+ db_find_and_open_repository(OPEN_OK_NOT_FOUND|OPEN_SUBSTITUTE,0);
17951800
verify_all_options();
17961801
if( g.argc!=3 ) usage("FILE");
17971802
blob_zero(&out);
17981803
blob_read_from_file(&in, g.argv[2], ExtFILE);
17991804
wiki_convert(&in, &out, flags);
18001805
--- src/wikiformat.c
+++ src/wikiformat.c
@@ -30,10 +30,11 @@
30 #define WIKI_NOBLOCK 0x004 /* No block markup of any kind */
31 #define WIKI_BUTTONS 0x008 /* Allow sub-menu buttons */
32 #define WIKI_NOBADLINKS 0x010 /* Ignore broken hyperlinks */
33 #define WIKI_LINKSONLY 0x020 /* No markup. Only decorate links */
34 #define WIKI_NEWLINE 0x040 /* Honor \n - break lines at each \n */
 
35 #endif
36
37
38 /*
39 ** These are the only markup attributes allowed.
@@ -1211,20 +1212,22 @@
1211 ** [http://www.fossil-scm.org/]
1212 ** [https://www.fossil-scm.org/]
1213 ** [ftp://www.fossil-scm.org/]
1214 ** [mailto:[email protected]]
1215 **
1216 ** [/path]
 
1217 **
1218 ** [./relpath]
 
1219 **
1220 ** [WikiPageName]
1221 ** [wiki:WikiPageName]
1222 **
1223 ** [0123456789abcdef]
1224 **
1225 ** [#fragment]
 
1226 **
1227 ** [2010-02-27 07:13]
1228 */
1229 void wiki_resolve_hyperlink(
1230 Blob *pOut, /* Write the HTML output here */
@@ -1296,24 +1299,35 @@
1296 blob_appendf(pOut, "%z[",xhref(zExtraNS, "%R/info/%s", zTarget));
1297 zTerm = "]</a>";
1298 }else{
1299 zTerm = "";
1300 }
1301 }else if( strlen(zTarget)>=10 && fossil_isdigit(zTarget[0]) && zTarget[4]=='-'
1302 && db_int(0, "SELECT datetime(%Q) NOT NULL", zTarget) ){
1303 blob_appendf(pOut, "<a href=\"%R/timeline?c=%T\"%s>", zTarget, zExtra);
1304 }else if( (z = validWikiPageName(mFlags, zTarget))!=0 ){
 
1305 const char *zOverride = wiki_is_overridden(zTarget);
1306 if( zOverride ){
1307 blob_appendf(pOut, "<a href=\"%R/info/%S\"%s>", zOverride, zExtra);
1308 }else{
1309 blob_appendf(pOut, "<a href=\"%R/wiki?name=%T\"%s>", z, zExtra);
1310 }
1311 }else if( zOrig && zTarget>=&zOrig[2] && !fossil_isspace(zTarget[-2]) ){
1312 /* Probably an array subscript in code */
 
 
 
 
 
 
 
 
 
 
 
 
1313 zTerm = "";
1314 }else if( (mFlags & (WIKI_NOBADLINKS|WIKI_LINKSONLY))!=0 ){
 
1315 zTerm = "";
1316 }else{
1317 blob_appendf(pOut, "<span class=\"brokenlink\">[%h]", zTarget);
1318 zTerm = "</span>";
1319 }
@@ -1758,20 +1772,10 @@
1758 }
1759 blob_append(renderer.pOut, "\n", 1);
1760 free(renderer.aStack);
1761 }
1762
1763 /*
1764 ** Send a string as wiki to CGI output.
1765 */
1766 void wiki_write(const char *zIn, int flags){
1767 Blob in;
1768 blob_init(&in, zIn, -1);
1769 wiki_convert(&in, 0, flags);
1770 blob_reset(&in);
1771 }
1772
1773 /*
1774 ** COMMAND: test-wiki-render
1775 **
1776 ** Usage: %fossil test-wiki-render FILE [OPTIONS]
1777 **
@@ -1790,10 +1794,11 @@
1790 if( find_option("htmlonly",0,0)!=0 ) flags |= WIKI_HTMLONLY;
1791 if( find_option("linksonly",0,0)!=0 ) flags |= WIKI_LINKSONLY;
1792 if( find_option("nobadlinks",0,0)!=0 ) flags |= WIKI_NOBADLINKS;
1793 if( find_option("inline",0,0)!=0 ) flags |= WIKI_INLINE;
1794 if( find_option("noblock",0,0)!=0 ) flags |= WIKI_NOBLOCK;
 
1795 verify_all_options();
1796 if( g.argc!=3 ) usage("FILE");
1797 blob_zero(&out);
1798 blob_read_from_file(&in, g.argv[2], ExtFILE);
1799 wiki_convert(&in, &out, flags);
1800
--- src/wikiformat.c
+++ src/wikiformat.c
@@ -30,10 +30,11 @@
30 #define WIKI_NOBLOCK 0x004 /* No block markup of any kind */
31 #define WIKI_BUTTONS 0x008 /* Allow sub-menu buttons */
32 #define WIKI_NOBADLINKS 0x010 /* Ignore broken hyperlinks */
33 #define WIKI_LINKSONLY 0x020 /* No markup. Only decorate links */
34 #define WIKI_NEWLINE 0x040 /* Honor \n - break lines at each \n */
35 #define WIKI_MARKDOWNLINKS 0x080 /* Resolve hyperlinks as in markdown */
36 #endif
37
38
39 /*
40 ** These are the only markup attributes allowed.
@@ -1211,20 +1212,22 @@
1212 ** [http://www.fossil-scm.org/]
1213 ** [https://www.fossil-scm.org/]
1214 ** [ftp://www.fossil-scm.org/]
1215 ** [mailto:[email protected]]
1216 **
1217 ** [/path] -> Refers to the root of the Fossil hierarchy, not
1218 ** the root of the URI domain
1219 **
1220 ** [./relpath]
1221 ** [../relpath]
1222 **
1223 ** [#fragment]
 
1224 **
1225 ** [0123456789abcdef]
1226 **
1227 ** [WikiPageName]
1228 ** [wiki:WikiPageName]
1229 **
1230 ** [2010-02-27 07:13]
1231 */
1232 void wiki_resolve_hyperlink(
1233 Blob *pOut, /* Write the HTML output here */
@@ -1296,24 +1299,35 @@
1299 blob_appendf(pOut, "%z[",xhref(zExtraNS, "%R/info/%s", zTarget));
1300 zTerm = "]</a>";
1301 }else{
1302 zTerm = "";
1303 }
 
 
 
1304 }else if( (z = validWikiPageName(mFlags, zTarget))!=0 ){
1305 /* The link is to a valid wiki page name */
1306 const char *zOverride = wiki_is_overridden(zTarget);
1307 if( zOverride ){
1308 blob_appendf(pOut, "<a href=\"%R/info/%S\"%s>", zOverride, zExtra);
1309 }else{
1310 blob_appendf(pOut, "<a href=\"%R/wiki?name=%T\"%s>", z, zExtra);
1311 }
1312 }else if( strlen(zTarget)>=10 && fossil_isdigit(zTarget[0]) && zTarget[4]=='-'
1313 && db_int(0, "SELECT datetime(%Q) NOT NULL", zTarget) ){
1314 /* Dates or date-and-times in ISO8610 resolve to a link to the
1315 ** timeline for that date */
1316 blob_appendf(pOut, "<a href=\"%R/timeline?c=%T\"%s>", zTarget, zExtra);
1317 }else if( mFlags & WIKI_MARKDOWNLINKS ){
1318 /* If none of the above, and if rendering links for markdown, then
1319 ** create a link to the literal text of the target */
1320 blob_appendf(pOut, "<a href=\"%h\"%s>", zTarget, zExtra);
1321 }else if( zOrig && zTarget>=&zOrig[2]
1322 && zTarget[-1]=='[' && !fossil_isspace(zTarget[-2]) ){
1323 /* If the hyperlink markup is not preceded by whitespace, then it
1324 ** is probably a C-language subscript or similar, not really a
1325 ** hyperlink. Just ignore it. */
1326 zTerm = "";
1327 }else if( (mFlags & (WIKI_NOBADLINKS|WIKI_LINKSONLY))!=0 ){
1328 /* Also ignore the link if various flags are set */
1329 zTerm = "";
1330 }else{
1331 blob_appendf(pOut, "<span class=\"brokenlink\">[%h]", zTarget);
1332 zTerm = "</span>";
1333 }
@@ -1758,20 +1772,10 @@
1772 }
1773 blob_append(renderer.pOut, "\n", 1);
1774 free(renderer.aStack);
1775 }
1776
 
 
 
 
 
 
 
 
 
 
1777 /*
1778 ** COMMAND: test-wiki-render
1779 **
1780 ** Usage: %fossil test-wiki-render FILE [OPTIONS]
1781 **
@@ -1790,10 +1794,11 @@
1794 if( find_option("htmlonly",0,0)!=0 ) flags |= WIKI_HTMLONLY;
1795 if( find_option("linksonly",0,0)!=0 ) flags |= WIKI_LINKSONLY;
1796 if( find_option("nobadlinks",0,0)!=0 ) flags |= WIKI_NOBADLINKS;
1797 if( find_option("inline",0,0)!=0 ) flags |= WIKI_INLINE;
1798 if( find_option("noblock",0,0)!=0 ) flags |= WIKI_NOBLOCK;
1799 db_find_and_open_repository(OPEN_OK_NOT_FOUND|OPEN_SUBSTITUTE,0);
1800 verify_all_options();
1801 if( g.argc!=3 ) usage("FILE");
1802 blob_zero(&out);
1803 blob_read_from_file(&in, g.argv[2], ExtFILE);
1804 wiki_convert(&in, &out, flags);
1805
--- www/server/any/scgi.md
+++ www/server/any/scgi.md
@@ -9,20 +9,30 @@
99
This can be used with a web server such as [nginx](http://nginx.org)
1010
which does not support [Fossil’s CGI mode](./cgi.md).
1111
1212
A basic nginx configuration to support SCGI with Fossil looks like this:
1313
14
- location /example/ {
14
+ location /code/ {
1515
include scgi_params;
16
+ scgi_param SCRIPT_NAME "/code";
1617
scgi_pass localhost:9000;
17
- scgi_param SCRIPT_NAME "/example";
18
- scgi_param HTTPS "on";
1918
}
2019
21
-Start Fossil so that it will respond to nginx’s SCGI calls like this:
20
+The `scgi_params` file comes with nginx, and it simply translates nginx
21
+internal variables to `scgi_param` directives to create SCGI environment
22
+variables for the proxied program; in this case, Fossil. Our explicit
23
+`scgi_param` call to define `SCRIPT_NAME` adds one more variable to this
24
+set, which is necessary for this configuration to work properly, because
25
+our repo isn’t at the root of the URL hierarchy. Without it, when Fossil
26
+generates absolute URLs, they’ll be missing the `/code` part at the
27
+start, which will typically cause [404 errors][404].
28
+
29
+The final directive simply tells nginx to proxy all calls to URLs under
30
+`/code` down to an SCGI program on TCP port 9000. We can temporarily
31
+set Fossil up as a server on that port like so:
2232
23
- fossil server /path/to/repo.fossil --scgi --localhost --port 9000
33
+ $ fossil server /path/to/repo.fossil --scgi --localhost --port 9000 &
2434
2535
The `--scgi` option switches Fossil into SCGI mode from its default,
2636
which is [stand-alone HTTP server mode](./none.md). All of the other
2737
options discussed in that linked document — such as the ability to serve
2838
a directory full of Fossil repositories rather than just a single
@@ -34,23 +44,27 @@
3444
3545
Giving an explicit non-default TCP port number via `--port` is a good
3646
idea to avoid conflicts with use of Fossil’s default TCP service port,
3747
8080, which may conflict with local uses of `fossil ui` and such.
3848
39
-Fossil requires the `SCRIPT_NAME` environment variable in order to
40
-function properly, but nginx does not provide this variable by default,
41
-so it is necessary to provide it in the configuration. Failure to do
42
-this will cause Fossil to return an error.
43
-
44
-The [example `fslsrv` script](/file/tools/fslsrv) shows off these same
45
-concepts in a more complicated setting. You might want to mine that
46
-script for ideas.
47
-
48
-You might want to next read one of the platform-specific versions of this
49
-document, which goes into more detail:
50
-
51
-* [Debian/Ubuntu](../debian/nginx.md)
52
-
53
-There is a [separate article](../../tls-nginx.md) showing how to add TLS
54
-encryption to this basic SCGI + nginx setup.
49
+We characterized the SCGI service start command above as “temporary”
50
+because running Fossil in the background like that means it won’t start
51
+back up on a reboot of the server. A simple solution to that is to add
52
+that command to `/etc/rc.local` on systems that have it. However, you
53
+might want to consider setting Fossil up as an OS service instead, so
54
+that you get the benefits of the platform’s service management
55
+framework:
56
+
57
+* [Linux (systemd)](../debian/service.md)
58
+* [Windows service](../windows/service.md)
59
+* [macOS (launchd)](../macos/service.md)
60
+* [xinetd](../any/xinetd.md)
61
+* [inetd](../any/inetd.md)
62
+
63
+We go into more detail on nginx service setup with Fossil in our
64
+[Debian/Ubuntu specific guide](../debian/nginx.md). Then in [a later
65
+article](../../tls-nginx.md) that builds upon that, we show how to add
66
+TLS encryption to this basic SCGI + nginx setup on Debian type OSes.
5567
5668
*[Return to the top-level Fossil server article.](../)*
69
+
70
+[404]: https://en.wikipedia.org/wiki/HTTP_404
5771
--- www/server/any/scgi.md
+++ www/server/any/scgi.md
@@ -9,20 +9,30 @@
9 This can be used with a web server such as [nginx](http://nginx.org)
10 which does not support [Fossil’s CGI mode](./cgi.md).
11
12 A basic nginx configuration to support SCGI with Fossil looks like this:
13
14 location /example/ {
15 include scgi_params;
 
16 scgi_pass localhost:9000;
17 scgi_param SCRIPT_NAME "/example";
18 scgi_param HTTPS "on";
19 }
20
21 Start Fossil so that it will respond to nginx’s SCGI calls like this:
 
 
 
 
 
 
 
 
 
 
 
22
23 fossil server /path/to/repo.fossil --scgi --localhost --port 9000
24
25 The `--scgi` option switches Fossil into SCGI mode from its default,
26 which is [stand-alone HTTP server mode](./none.md). All of the other
27 options discussed in that linked document — such as the ability to serve
28 a directory full of Fossil repositories rather than just a single
@@ -34,23 +44,27 @@
34
35 Giving an explicit non-default TCP port number via `--port` is a good
36 idea to avoid conflicts with use of Fossil’s default TCP service port,
37 8080, which may conflict with local uses of `fossil ui` and such.
38
39 Fossil requires the `SCRIPT_NAME` environment variable in order to
40 function properly, but nginx does not provide this variable by default,
41 so it is necessary to provide it in the configuration. Failure to do
42 this will cause Fossil to return an error.
43
44 The [example `fslsrv` script](/file/tools/fslsrv) shows off these same
45 concepts in a more complicated setting. You might want to mine that
46 script for ideas.
47
48 You might want to next read one of the platform-specific versions of this
49 document, which goes into more detail:
50
51 * [Debian/Ubuntu](../debian/nginx.md)
52
53 There is a [separate article](../../tls-nginx.md) showing how to add TLS
54 encryption to this basic SCGI + nginx setup.
 
 
55
56 *[Return to the top-level Fossil server article.](../)*
 
 
57
--- www/server/any/scgi.md
+++ www/server/any/scgi.md
@@ -9,20 +9,30 @@
9 This can be used with a web server such as [nginx](http://nginx.org)
10 which does not support [Fossil’s CGI mode](./cgi.md).
11
12 A basic nginx configuration to support SCGI with Fossil looks like this:
13
14 location /code/ {
15 include scgi_params;
16 scgi_param SCRIPT_NAME "/code";
17 scgi_pass localhost:9000;
 
 
18 }
19
20 The `scgi_params` file comes with nginx, and it simply translates nginx
21 internal variables to `scgi_param` directives to create SCGI environment
22 variables for the proxied program; in this case, Fossil. Our explicit
23 `scgi_param` call to define `SCRIPT_NAME` adds one more variable to this
24 set, which is necessary for this configuration to work properly, because
25 our repo isn’t at the root of the URL hierarchy. Without it, when Fossil
26 generates absolute URLs, they’ll be missing the `/code` part at the
27 start, which will typically cause [404 errors][404].
28
29 The final directive simply tells nginx to proxy all calls to URLs under
30 `/code` down to an SCGI program on TCP port 9000. We can temporarily
31 set Fossil up as a server on that port like so:
32
33 $ fossil server /path/to/repo.fossil --scgi --localhost --port 9000 &
34
35 The `--scgi` option switches Fossil into SCGI mode from its default,
36 which is [stand-alone HTTP server mode](./none.md). All of the other
37 options discussed in that linked document — such as the ability to serve
38 a directory full of Fossil repositories rather than just a single
@@ -34,23 +44,27 @@
44
45 Giving an explicit non-default TCP port number via `--port` is a good
46 idea to avoid conflicts with use of Fossil’s default TCP service port,
47 8080, which may conflict with local uses of `fossil ui` and such.
48
49 We characterized the SCGI service start command above as “temporary”
50 because running Fossil in the background like that means it won’t start
51 back up on a reboot of the server. A simple solution to that is to add
52 that command to `/etc/rc.local` on systems that have it. However, you
53 might want to consider setting Fossil up as an OS service instead, so
54 that you get the benefits of the platform’s service management
55 framework:
56
57 * [Linux (systemd)](../debian/service.md)
58 * [Windows service](../windows/service.md)
59 * [macOS (launchd)](../macos/service.md)
60 * [xinetd](../any/xinetd.md)
61 * [inetd](../any/inetd.md)
62
63 We go into more detail on nginx service setup with Fossil in our
64 [Debian/Ubuntu specific guide](../debian/nginx.md). Then in [a later
65 article](../../tls-nginx.md) that builds upon that, we show how to add
66 TLS encryption to this basic SCGI + nginx setup on Debian type OSes.
67
68 *[Return to the top-level Fossil server article.](../)*
69
70 [404]: https://en.wikipedia.org/wiki/HTTP_404
71
--- www/server/debian/nginx.md
+++ www/server/debian/nginx.md
@@ -66,15 +66,15 @@
6666
6767
Fossil provides four major ways to access a repository it’s serving
6868
remotely, three of which are straightforward to use with nginx:
6969
7070
* **HTTP** — Fossil has a built-in HTTP server: [`fossil
71
- server`](/help/server). While this method is efficient and it’s
72
- possible to use nginx to proxy access to another HTTP server, this
73
- option is overkill for our purposes. nginx is itself a fully
74
- featured HTTP server, so we will choose in this guide not to make
75
- nginx reinterpret Fossil’s implementation of HTTP.
71
+ server`](../any/none.md). While this method is efficient and it’s
72
+ possible to use nginx to proxy access to another HTTP server, we
73
+ don’t see any particularly good reason to make nginx reinterpret
74
+ Fossil’s own implementation of HTTP when we have a better option.
75
+ (But see [below](#http).)
7676
7777
* **CGI** — This method is simple but inefficient, because it launches
7878
a separate Fossil instance on every HTTP hit.
7979
8080
Since Fossil is a relatively small self-contained program, and it’s
@@ -107,103 +107,74 @@
107107
$ sudo apt install fossil nginx
108108
109109
110110
## <a name="scgi"></a>Running Fossil in SCGI Mode
111111
112
-I run my Fossil SCGI server instances with a variant of [the `fslsrv`
113
-shell script](/file/tools/fslsrv) currently hosted in the Fossil source
114
-code repository. You’ll want to download that and make a copy of it, so
115
-you can customize it to your particular needs.
116
-
117
-This script allows running multiple Fossil SCGI servers, one per
118
-repository, each bound to a different high-numbered `localhost` port, so
119
-that only nginx can see and proxy them out to the public. The
120
-“`example`” repo is on TCP port localhost:12345, and the “`foo`” repo is
121
-on localhost:12346.
122
-
123
-As written, the `fslsrv` script expects repositories to be stored in the
124
-calling user’s home directory under `~/museum`, because where else do
125
-you keep Fossils?
126
-
127
-That home directory also needs to have a directory to hold log files,
128
-`~/log/fossil/*.log`. Fossil doesn’t put out much logging, but when it
129
-does, it’s better to have it captured than to need to re-create the
130
-problem after the fact.
131
-
132
-The use of `--baseurl` in this script lets us have each Fossil
133
-repository mounted in a different location in the URL scheme. Here, for
134
-example, we’re saying that the “`example`” repository is hosted under
135
-the `/code` URI on its domains, but that the “`foo`” repo is hosted at
136
-the top level of its domain. You’ll want to do something like the
137
-former for a Fossil repo that’s just one piece of a larger site, but the
138
-latter for a repo that is basically the whole point of the site.
139
-
140
-You might also want another script to automate the update, build, and
141
-deployment steps for new Fossil versions:
142
-
143
- #!/bin/sh
144
- cd $HOME/src/fossil/trunk
145
- fossil up
146
- make -j11
147
- killall fossil
148
- sudo make install
149
- fslsrv
150
-
151
-The `killall fossil` step is needed only on OSes that refuse to let you
152
-replace a running binary on disk.
153
-
154
-As written, the `fslsrv` script assumes a Linux environment. It expects
155
-`/bin/bash` to exist, and it depends on non-POSIX tools like `pgrep`.
156
-It should not be difficult to port to systems like macOS or the BSDs.
112
+For the following nginx configuration to work, it needs to contact a
113
+Fossil instance speaking the SCGI protocol. There are [many ways](../)
114
+to set that up. For Debian type systems, we primarily recommend
115
+following [our systemd user service guide](service.md).
116
+
117
+Another option would be to customize [the `fslsrv` shell
118
+script](/file/tools/fslsrv) that ships with Fossil as an example of
119
+launching multiple Fossil instances in the background to serve multiple
120
+URLs.
121
+
122
+However you do it, you need to match up the TCP port numbers between it
123
+and those in the nginx configuration below.
157124
158125
159126
## <a name="config"></a>Configuration
160127
161128
On Debian and Ubuntu systems the primary user-level configuration file
162129
for nginx is `/etc/nginx/sites-enabled/default`. I recommend that this
163130
file contain only a list of include statements, one for each site that
164131
server hosts:
165132
166
- include local/example
167
- include local/foo
133
+ include local/example.com
134
+ include local/foo.net
168135
169136
Those files then each define one domain’s configuration. Here,
170
-`/etc/nginx/local/example` contains the configuration for
171
-`*.example.com` and `*.example.net`; and `local/foo` contains the
172
-configuration for `*.foo.net`.
137
+`/etc/nginx/local/example.com` contains the configuration for
138
+`*.example.com` and its alias `*.example.net`; and `local/foo.net`
139
+contains the configuration for `*.foo.net`.
173140
174
-The configuration for our `foo.net` web site, stored in
175
-`/etc/nginx/sites-enabled/local/foo` is:
141
+The configuration for our `example.com` web site, stored in
142
+`/etc/nginx/sites-enabled/local/example.com` is:
176143
177144
server {
178
- server_name .foo.net;
145
+ server_name .example.com .example.net "";
179146
include local/generic;
180147
181
- access_log /var/log/nginx/foo.net-https-access.log;
182
- error_log /var/log/nginx/foo.net-https-error.log;
148
+ access_log /var/log/nginx/example.com-https-access.log;
149
+ error_log /var/log/nginx/example.com-https-error.log;
183150
184
- # Bypass Fossil for the static Doxygen docs
185
- location /doc/html {
186
- root /var/www/foo.net;
151
+ # Bypass Fossil for the static documentation generated from
152
+ # our source code by Doxygen, so it merges into the embedded
153
+ # doc URL hierarchy at Fossil’s $ROOT/doc without requiring that
154
+ # these generated files actually be stored in the repo. This
155
+ # also lets us set aggressive caching on these docs, since
156
+ # they rarely change.
157
+ location /code/doc/html {
158
+ root /var/www/example.com/code/doc/html;
187159
188160
location ~* \.(html|ico|css|js|gif|jpg|png)$ {
189161
expires 7d;
190162
add_header Vary Accept-Encoding;
191163
access_log off;
192164
}
193165
}
194166
195167
# Redirect everything else to the Fossil instance
196
- location / {
168
+ location /code {
197169
include scgi_params;
170
+ scgi_param SCRIPT_NAME "/code";
198171
scgi_pass 127.0.0.1:12345;
199
- scgi_param HTTPS "on";
200
- scgi_param SCRIPT_NAME "";
201172
}
202173
}
203174
204
-As you can see, this is a simple extension of [the basic nginx service
175
+As you can see, this is a pure extension of [the basic nginx service
205176
configuration for SCGI][scgii], showing off a few ideas you might want to
206177
try on your own site, such as static asset proxying.
207178
208179
The `local/generic` file referenced above helps us reduce unnecessary
209180
repetition among the multiple sites this configuration hosts:
@@ -221,10 +192,34 @@
221192
the `access_log` and `error_log` directives, which follow an obvious
222193
pattern from one host to the next. Sadly, you must tolerate some
223194
repetition across `server { }` blocks when setting up multiple domains
224195
on a single server.
225196
226
-The configuration for `example.com` and `example.net` is similar.
197
+The configuration for `foo.net` is similar.
227198
228199
See [the nginx docs](http://nginx.org/en/docs/) for more ideas.
229200
201
+
202
+## <a name="http"></a>Proxying HTTP Anyway
203
+
204
+[Above](#modes), we argued that proxying SCGI is a better option than
205
+making nginx reinterpret Fossil’s own implementation of HTTP. If you
206
+want Fossil to speak HTTP, just [set Fossil up as a standalone
207
+server](../any/none.md). And if you want nginx to [provide TLS
208
+encryption for Fossil][tls], proxying HTTP instead of SCGI provides no
209
+benefit.
210
+
211
+However, it is still worth showing the proper method of proxying
212
+Fossil’s HTTP server through nginx if only to make reading nginx
213
+documentation on other sites easier:
214
+
215
+ location /code {
216
+ rewrite ^/code(/.*) $1 break;
217
+ proxy_pass http://127.0.0.1:12345;
218
+ }
219
+
220
+The most common thing people get wrong when hand-rolling a configuration
221
+like this is to get the slashes wrong. Fossil is senstitive to this. For
222
+instance, Fossil will not collapse double slashes down to a single
223
+slash, as some other HTTP servers will.
224
+
230225
*[Return to the top-level Fossil server article.](../)*
231226
--- www/server/debian/nginx.md
+++ www/server/debian/nginx.md
@@ -66,15 +66,15 @@
66
67 Fossil provides four major ways to access a repository it’s serving
68 remotely, three of which are straightforward to use with nginx:
69
70 * **HTTP** — Fossil has a built-in HTTP server: [`fossil
71 server`](/help/server). While this method is efficient and it’s
72 possible to use nginx to proxy access to another HTTP server, this
73 option is overkill for our purposes. nginx is itself a fully
74 featured HTTP server, so we will choose in this guide not to make
75 nginx reinterpret Fossil’s implementation of HTTP.
76
77 * **CGI** — This method is simple but inefficient, because it launches
78 a separate Fossil instance on every HTTP hit.
79
80 Since Fossil is a relatively small self-contained program, and it’s
@@ -107,103 +107,74 @@
107 $ sudo apt install fossil nginx
108
109
110 ## <a name="scgi"></a>Running Fossil in SCGI Mode
111
112 I run my Fossil SCGI server instances with a variant of [the `fslsrv`
113 shell script](/file/tools/fslsrv) currently hosted in the Fossil source
114 code repository. You’ll want to download that and make a copy of it, so
115 you can customize it to your particular needs.
116
117 This script allows running multiple Fossil SCGI servers, one per
118 repository, each bound to a different high-numbered `localhost` port, so
119 that only nginx can see and proxy them out to the public. The
120 “`example`” repo is on TCP port localhost:12345, and the “`foo`” repo is
121 on localhost:12346.
122
123 As written, the `fslsrv` script expects repositories to be stored in the
124 calling user’s home directory under `~/museum`, because where else do
125 you keep Fossils?
126
127 That home directory also needs to have a directory to hold log files,
128 `~/log/fossil/*.log`. Fossil doesn’t put out much logging, but when it
129 does, it’s better to have it captured than to need to re-create the
130 problem after the fact.
131
132 The use of `--baseurl` in this script lets us have each Fossil
133 repository mounted in a different location in the URL scheme. Here, for
134 example, we’re saying that the “`example`” repository is hosted under
135 the `/code` URI on its domains, but that the “`foo`” repo is hosted at
136 the top level of its domain. You’ll want to do something like the
137 former for a Fossil repo that’s just one piece of a larger site, but the
138 latter for a repo that is basically the whole point of the site.
139
140 You might also want another script to automate the update, build, and
141 deployment steps for new Fossil versions:
142
143 #!/bin/sh
144 cd $HOME/src/fossil/trunk
145 fossil up
146 make -j11
147 killall fossil
148 sudo make install
149 fslsrv
150
151 The `killall fossil` step is needed only on OSes that refuse to let you
152 replace a running binary on disk.
153
154 As written, the `fslsrv` script assumes a Linux environment. It expects
155 `/bin/bash` to exist, and it depends on non-POSIX tools like `pgrep`.
156 It should not be difficult to port to systems like macOS or the BSDs.
157
158
159 ## <a name="config"></a>Configuration
160
161 On Debian and Ubuntu systems the primary user-level configuration file
162 for nginx is `/etc/nginx/sites-enabled/default`. I recommend that this
163 file contain only a list of include statements, one for each site that
164 server hosts:
165
166 include local/example
167 include local/foo
168
169 Those files then each define one domain’s configuration. Here,
170 `/etc/nginx/local/example` contains the configuration for
171 `*.example.com` and `*.example.net`; and `local/foo` contains the
172 configuration for `*.foo.net`.
173
174 The configuration for our `foo.net` web site, stored in
175 `/etc/nginx/sites-enabled/local/foo` is:
176
177 server {
178 server_name .foo.net;
179 include local/generic;
180
181 access_log /var/log/nginx/foo.net-https-access.log;
182 error_log /var/log/nginx/foo.net-https-error.log;
183
184 # Bypass Fossil for the static Doxygen docs
185 location /doc/html {
186 root /var/www/foo.net;
 
 
 
 
 
187
188 location ~* \.(html|ico|css|js|gif|jpg|png)$ {
189 expires 7d;
190 add_header Vary Accept-Encoding;
191 access_log off;
192 }
193 }
194
195 # Redirect everything else to the Fossil instance
196 location / {
197 include scgi_params;
 
198 scgi_pass 127.0.0.1:12345;
199 scgi_param HTTPS "on";
200 scgi_param SCRIPT_NAME "";
201 }
202 }
203
204 As you can see, this is a simple extension of [the basic nginx service
205 configuration for SCGI][scgii], showing off a few ideas you might want to
206 try on your own site, such as static asset proxying.
207
208 The `local/generic` file referenced above helps us reduce unnecessary
209 repetition among the multiple sites this configuration hosts:
@@ -221,10 +192,34 @@
221 the `access_log` and `error_log` directives, which follow an obvious
222 pattern from one host to the next. Sadly, you must tolerate some
223 repetition across `server { }` blocks when setting up multiple domains
224 on a single server.
225
226 The configuration for `example.com` and `example.net` is similar.
227
228 See [the nginx docs](http://nginx.org/en/docs/) for more ideas.
229
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
230 *[Return to the top-level Fossil server article.](../)*
231
--- www/server/debian/nginx.md
+++ www/server/debian/nginx.md
@@ -66,15 +66,15 @@
66
67 Fossil provides four major ways to access a repository it’s serving
68 remotely, three of which are straightforward to use with nginx:
69
70 * **HTTP** — Fossil has a built-in HTTP server: [`fossil
71 server`](../any/none.md). While this method is efficient and it’s
72 possible to use nginx to proxy access to another HTTP server, we
73 don’t see any particularly good reason to make nginx reinterpret
74 Fossil’s own implementation of HTTP when we have a better option.
75 (But see [below](#http).)
76
77 * **CGI** — This method is simple but inefficient, because it launches
78 a separate Fossil instance on every HTTP hit.
79
80 Since Fossil is a relatively small self-contained program, and it’s
@@ -107,103 +107,74 @@
107 $ sudo apt install fossil nginx
108
109
110 ## <a name="scgi"></a>Running Fossil in SCGI Mode
111
112 For the following nginx configuration to work, it needs to contact a
113 Fossil instance speaking the SCGI protocol. There are [many ways](../)
114 to set that up. For Debian type systems, we primarily recommend
115 following [our systemd user service guide](service.md).
116
117 Another option would be to customize [the `fslsrv` shell
118 script](/file/tools/fslsrv) that ships with Fossil as an example of
119 launching multiple Fossil instances in the background to serve multiple
120 URLs.
121
122 However you do it, you need to match up the TCP port numbers between it
123 and those in the nginx configuration below.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
124
125
126 ## <a name="config"></a>Configuration
127
128 On Debian and Ubuntu systems the primary user-level configuration file
129 for nginx is `/etc/nginx/sites-enabled/default`. I recommend that this
130 file contain only a list of include statements, one for each site that
131 server hosts:
132
133 include local/example.com
134 include local/foo.net
135
136 Those files then each define one domain’s configuration. Here,
137 `/etc/nginx/local/example.com` contains the configuration for
138 `*.example.com` and its alias `*.example.net`; and `local/foo.net`
139 contains the configuration for `*.foo.net`.
140
141 The configuration for our `example.com` web site, stored in
142 `/etc/nginx/sites-enabled/local/example.com` is:
143
144 server {
145 server_name .example.com .example.net "";
146 include local/generic;
147
148 access_log /var/log/nginx/example.com-https-access.log;
149 error_log /var/log/nginx/example.com-https-error.log;
150
151 # Bypass Fossil for the static documentation generated from
152 # our source code by Doxygen, so it merges into the embedded
153 # doc URL hierarchy at Fossil’s $ROOT/doc without requiring that
154 # these generated files actually be stored in the repo. This
155 # also lets us set aggressive caching on these docs, since
156 # they rarely change.
157 location /code/doc/html {
158 root /var/www/example.com/code/doc/html;
159
160 location ~* \.(html|ico|css|js|gif|jpg|png)$ {
161 expires 7d;
162 add_header Vary Accept-Encoding;
163 access_log off;
164 }
165 }
166
167 # Redirect everything else to the Fossil instance
168 location /code {
169 include scgi_params;
170 scgi_param SCRIPT_NAME "/code";
171 scgi_pass 127.0.0.1:12345;
 
 
172 }
173 }
174
175 As you can see, this is a pure extension of [the basic nginx service
176 configuration for SCGI][scgii], showing off a few ideas you might want to
177 try on your own site, such as static asset proxying.
178
179 The `local/generic` file referenced above helps us reduce unnecessary
180 repetition among the multiple sites this configuration hosts:
@@ -221,10 +192,34 @@
192 the `access_log` and `error_log` directives, which follow an obvious
193 pattern from one host to the next. Sadly, you must tolerate some
194 repetition across `server { }` blocks when setting up multiple domains
195 on a single server.
196
197 The configuration for `foo.net` is similar.
198
199 See [the nginx docs](http://nginx.org/en/docs/) for more ideas.
200
201
202 ## <a name="http"></a>Proxying HTTP Anyway
203
204 [Above](#modes), we argued that proxying SCGI is a better option than
205 making nginx reinterpret Fossil’s own implementation of HTTP. If you
206 want Fossil to speak HTTP, just [set Fossil up as a standalone
207 server](../any/none.md). And if you want nginx to [provide TLS
208 encryption for Fossil][tls], proxying HTTP instead of SCGI provides no
209 benefit.
210
211 However, it is still worth showing the proper method of proxying
212 Fossil’s HTTP server through nginx if only to make reading nginx
213 documentation on other sites easier:
214
215 location /code {
216 rewrite ^/code(/.*) $1 break;
217 proxy_pass http://127.0.0.1:12345;
218 }
219
220 The most common thing people get wrong when hand-rolling a configuration
221 like this is to get the slashes wrong. Fossil is senstitive to this. For
222 instance, Fossil will not collapse double slashes down to a single
223 slash, as some other HTTP servers will.
224
225 *[Return to the top-level Fossil server article.](../)*
226
--- www/server/index.html
+++ www/server/index.html
@@ -77,13 +77,14 @@
7777
minimum recommended preparation steps:</p>
7878
7979
<ol>
8080
<li><p>Fossil creates only one user in a <a
8181
href="$ROOT/help?cmd=new">new repository</a> and gives it the <a
82
- href="../caps/admin-v-setup.md#apsu">all-powerful Setup capability</a>. (“s”)
83
- The 6-digit random hex password generated for that user is not very strong against
84
- remote attack, so because that user has so much power, you should
82
+ href="../caps/admin-v-setup.md#apsu">all-powerful Setup capability</a>.
83
+ The 10-digit random password generated for that user is fairly strong
84
+ against remote attack, even without explicit password guess rate
85
+ limiting, but because that user has so much power, you may want to
8586
give it a much stronger password under Admin → Users.</a></li>
8687
8788
<li><p>Run the Admin → Security-Audit tool to verify that other
8889
security-related permissions and settings are as you want them.
8990
Consider clicking the “Take it private” link on that page to lock down
9091
--- www/server/index.html
+++ www/server/index.html
@@ -77,13 +77,14 @@
77 minimum recommended preparation steps:</p>
78
79 <ol>
80 <li><p>Fossil creates only one user in a <a
81 href="$ROOT/help?cmd=new">new repository</a> and gives it the <a
82 href="../caps/admin-v-setup.md#apsu">all-powerful Setup capability</a>. (“s”)
83 The 6-digit random hex password generated for that user is not very strong against
84 remote attack, so because that user has so much power, you should
 
85 give it a much stronger password under Admin → Users.</a></li>
86
87 <li><p>Run the Admin → Security-Audit tool to verify that other
88 security-related permissions and settings are as you want them.
89 Consider clicking the “Take it private” link on that page to lock down
90
--- www/server/index.html
+++ www/server/index.html
@@ -77,13 +77,14 @@
77 minimum recommended preparation steps:</p>
78
79 <ol>
80 <li><p>Fossil creates only one user in a <a
81 href="$ROOT/help?cmd=new">new repository</a> and gives it the <a
82 href="../caps/admin-v-setup.md#apsu">all-powerful Setup capability</a>.
83 The 10-digit random password generated for that user is fairly strong
84 against remote attack, even without explicit password guess rate
85 limiting, but because that user has so much power, you may want to
86 give it a much stronger password under Admin → Users.</a></li>
87
88 <li><p>Run the Admin → Security-Audit tool to verify that other
89 security-related permissions and settings are as you want them.
90 Consider clicking the “Take it private” link on that page to lock down
91
--- www/server/index.html
+++ www/server/index.html
@@ -77,13 +77,14 @@
7777
minimum recommended preparation steps:</p>
7878
7979
<ol>
8080
<li><p>Fossil creates only one user in a <a
8181
href="$ROOT/help?cmd=new">new repository</a> and gives it the <a
82
- href="../caps/admin-v-setup.md#apsu">all-powerful Setup capability</a>. (“s”)
83
- The 6-digit random hex password generated for that user is not very strong against
84
- remote attack, so because that user has so much power, you should
82
+ href="../caps/admin-v-setup.md#apsu">all-powerful Setup capability</a>.
83
+ The 10-digit random password generated for that user is fairly strong
84
+ against remote attack, even without explicit password guess rate
85
+ limiting, but because that user has so much power, you may want to
8586
give it a much stronger password under Admin → Users.</a></li>
8687
8788
<li><p>Run the Admin → Security-Audit tool to verify that other
8889
security-related permissions and settings are as you want them.
8990
Consider clicking the “Take it private” link on that page to lock down
9091
--- www/server/index.html
+++ www/server/index.html
@@ -77,13 +77,14 @@
77 minimum recommended preparation steps:</p>
78
79 <ol>
80 <li><p>Fossil creates only one user in a <a
81 href="$ROOT/help?cmd=new">new repository</a> and gives it the <a
82 href="../caps/admin-v-setup.md#apsu">all-powerful Setup capability</a>. (“s”)
83 The 6-digit random hex password generated for that user is not very strong against
84 remote attack, so because that user has so much power, you should
 
85 give it a much stronger password under Admin → Users.</a></li>
86
87 <li><p>Run the Admin → Security-Audit tool to verify that other
88 security-related permissions and settings are as you want them.
89 Consider clicking the “Take it private” link on that page to lock down
90
--- www/server/index.html
+++ www/server/index.html
@@ -77,13 +77,14 @@
77 minimum recommended preparation steps:</p>
78
79 <ol>
80 <li><p>Fossil creates only one user in a <a
81 href="$ROOT/help?cmd=new">new repository</a> and gives it the <a
82 href="../caps/admin-v-setup.md#apsu">all-powerful Setup capability</a>.
83 The 10-digit random password generated for that user is fairly strong
84 against remote attack, even without explicit password guess rate
85 limiting, but because that user has so much power, you may want to
86 give it a much stronger password under Admin → Users.</a></li>
87
88 <li><p>Run the Admin → Security-Audit tool to verify that other
89 security-related permissions and settings are as you want them.
90 Consider clicking the “Take it private” link on that page to lock down
91

Keyboard Shortcuts

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