Fossil SCM
Merged in trunk changes
Commit
493254b2e7753546c4a4c0996c16a9f53cf1dee9246884380694be578229919a
Parent
b72795a339e262a…
12 files changed
+1
-1
+1
+21
-7
+2
-2
+5
-1
+53
+1
+24
-19
+35
-21
+63
-68
+4
-3
+4
-3
+1
-1
| --- skins/default/header.txt | ||
| +++ skins/default/header.txt | ||
| @@ -17,11 +17,11 @@ | ||
| 17 | 17 | html "<a href='$home$url' class='active $cls'>$name</a>\n" |
| 18 | 18 | } else { |
| 19 | 19 | html "<a href='$home$url' class='$cls'>$name</a>\n" |
| 20 | 20 | } |
| 21 | 21 | } |
| 22 | -html "<a id='hbbtn' href='#'>☰</a>" | |
| 22 | +html "<a id='hbbtn' href='/sitemap'>☰</a>" | |
| 23 | 23 | menulink $index_page Home {} |
| 24 | 24 | if {[anycap jor]} { |
| 25 | 25 | menulink /timeline Timeline {} |
| 26 | 26 | } |
| 27 | 27 | if {[hascap oh]} { |
| 28 | 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='#'>☰</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'>☰</a>" |
| 23 | menulink $index_page Home {} |
| 24 | if {[anycap jor]} { |
| 25 | menulink /timeline Timeline {} |
| 26 | } |
| 27 | if {[hascap oh]} { |
| 28 |
+1
| --- src/checkin.c | ||
| +++ src/checkin.c | ||
| @@ -1939,10 +1939,11 @@ | ||
| 1939 | 1939 | /* |
| 1940 | 1940 | ** COMMAND: ci* |
| 1941 | 1941 | ** COMMAND: commit |
| 1942 | 1942 | ** |
| 1943 | 1943 | ** Usage: %fossil commit ?OPTIONS? ?FILE...? |
| 1944 | +** or: %fossil ci ?OPTIONS? ?FILE...? | |
| 1944 | 1945 | ** |
| 1945 | 1946 | ** Create a new version containing all of the changes in the current |
| 1946 | 1947 | ** checkout. You will be prompted to enter a check-in comment unless |
| 1947 | 1948 | ** the comment has been specified on the command-line using "-m" or a |
| 1948 | 1949 | ** file containing the comment using -M. The editor defined in the |
| 1949 | 1950 |
| --- 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 |
M
src/db.c
+21
-7
| --- src/db.c | ||
| +++ src/db.c | ||
| @@ -117,10 +117,11 @@ | ||
| 117 | 117 | /* |
| 118 | 118 | ** Arrange for the given file to be deleted on a failure. |
| 119 | 119 | */ |
| 120 | 120 | void db_delete_on_failure(const char *zFilename){ |
| 121 | 121 | assert( db.nDeleteOnFail<count(db.azDeleteOnFail) ); |
| 122 | + if( zFilename==0 ) return; | |
| 122 | 123 | db.azDeleteOnFail[db.nDeleteOnFail++] = fossil_strdup(zFilename); |
| 123 | 124 | } |
| 124 | 125 | |
| 125 | 126 | /* |
| 126 | 127 | ** Return the transaction nesting depth. 0 means we are currently |
| @@ -799,10 +800,13 @@ | ||
| 799 | 800 | } |
| 800 | 801 | |
| 801 | 802 | /* |
| 802 | 803 | ** Initialize a new database file with the given schema. If anything |
| 803 | 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. | |
| 804 | 808 | */ |
| 805 | 809 | void db_init_database( |
| 806 | 810 | const char *zFileName, /* Name of database file to create */ |
| 807 | 811 | const char *zSchema, /* First part of schema */ |
| 808 | 812 | ... /* Additional SQL to run. Terminate with NULL. */ |
| @@ -810,11 +814,11 @@ | ||
| 810 | 814 | sqlite3 *db; |
| 811 | 815 | int rc; |
| 812 | 816 | const char *zSql; |
| 813 | 817 | va_list ap; |
| 814 | 818 | |
| 815 | - db = db_open(zFileName); | |
| 819 | + db = db_open(zFileName ? zFileName : ":memory:"); | |
| 816 | 820 | sqlite3_exec(db, "BEGIN EXCLUSIVE", 0, 0, 0); |
| 817 | 821 | rc = sqlite3_exec(db, zSchema, 0, 0, 0); |
| 818 | 822 | if( rc!=SQLITE_OK ){ |
| 819 | 823 | db_err("%s", sqlite3_errmsg(db)); |
| 820 | 824 | } |
| @@ -825,11 +829,15 @@ | ||
| 825 | 829 | db_err("%s", sqlite3_errmsg(db)); |
| 826 | 830 | } |
| 827 | 831 | } |
| 828 | 832 | va_end(ap); |
| 829 | 833 | 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 | + } | |
| 831 | 839 | } |
| 832 | 840 | |
| 833 | 841 | /* |
| 834 | 842 | ** Function to return the number of seconds since 1970. This is |
| 835 | 843 | ** the same as strftime('%s','now') but is more compact. |
| @@ -1768,12 +1776,13 @@ | ||
| 1768 | 1776 | |
| 1769 | 1777 | /* |
| 1770 | 1778 | ** Flags for the db_find_and_open_repository() function. |
| 1771 | 1779 | */ |
| 1772 | 1780 | #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 */ | |
| 1775 | 1784 | #endif |
| 1776 | 1785 | |
| 1777 | 1786 | /* |
| 1778 | 1787 | ** Try to find the repository and open it. Use the -R or --repository |
| 1779 | 1788 | ** option to locate the repository. If no such option is available, then |
| @@ -1802,11 +1811,16 @@ | ||
| 1802 | 1811 | if( g.repositoryOpen ){ |
| 1803 | 1812 | if( (bFlags & OPEN_ANY_SCHEMA)==0 ) db_verify_schema(); |
| 1804 | 1813 | return; |
| 1805 | 1814 | } |
| 1806 | 1815 | 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{ | |
| 1808 | 1822 | #ifdef FOSSIL_ENABLE_JSON |
| 1809 | 1823 | g.json.resultCode = FSL_JSON_E_DB_NOT_FOUND; |
| 1810 | 1824 | #endif |
| 1811 | 1825 | if( nArgUsed==0 ){ |
| 1812 | 1826 | fossil_fatal("use --repository or -R to specify the repository database"); |
| @@ -2032,12 +2046,12 @@ | ||
| 2032 | 2046 | } |
| 2033 | 2047 | db_multi_exec( |
| 2034 | 2048 | "INSERT OR IGNORE INTO user(login, info) VALUES(%Q,'')", zUser |
| 2035 | 2049 | ); |
| 2036 | 2050 | 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 | |
| 2039 | 2053 | ); |
| 2040 | 2054 | if( !setupUserOnly ){ |
| 2041 | 2055 | db_multi_exec( |
| 2042 | 2056 | "INSERT OR IGNORE INTO user(login,pw,cap,info)" |
| 2043 | 2057 | " VALUES('anonymous',hex(randomblob(8)),'hmnc','Anon');" |
| 2044 | 2058 |
| --- 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 @@ | ||
| 2018 | 2018 | const char *zNewName = find_option("name",0,1); |
| 2019 | 2019 | const char *zOther; |
| 2020 | 2020 | char *zErr = 0; |
| 2021 | 2021 | verify_all_options(); |
| 2022 | 2022 | if( g.argc!=4 ){ |
| 2023 | - fossil_fatal("unknown extra arguments to \"login-group add\""); | |
| 2023 | + fossil_fatal("unknown extra arguments to \"login-group join\""); | |
| 2024 | 2024 | } |
| 2025 | 2025 | zOther = g.argv[3]; |
| 2026 | 2026 | login_group_leave(&zErr); |
| 2027 | 2027 | sqlite3_free(zErr); |
| 2028 | 2028 | zErr = 0; |
| @@ -2042,11 +2042,11 @@ | ||
| 2042 | 2042 | login_group_leave(&zErr); |
| 2043 | 2043 | if( zErr ) fossil_fatal("Oops: %s", zErr); |
| 2044 | 2044 | return; |
| 2045 | 2045 | } |
| 2046 | 2046 | }else{ |
| 2047 | - fossil_fatal("unknown command \"%s\" - should be \"add\" or \"leave\"", | |
| 2047 | + fossil_fatal("unknown command \"%s\" - should be \"join\" or \"leave\"", | |
| 2048 | 2048 | zCmd); |
| 2049 | 2049 | } |
| 2050 | 2050 | } |
| 2051 | 2051 | /* Show the current login group information */ |
| 2052 | 2052 | zLGName = login_group_name(); |
| 2053 | 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 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 |
+5
-1
| --- src/markdown_html.c | ||
| +++ src/markdown_html.c | ||
| @@ -433,11 +433,15 @@ | ||
| 433 | 433 | char zClose[20]; |
| 434 | 434 | |
| 435 | 435 | if( zLink==0 || zLink[0]==0 ){ |
| 436 | 436 | zClose[0] = 0; |
| 437 | 437 | }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); | |
| 439 | 443 | } |
| 440 | 444 | if( blob_size(content)==0 ){ |
| 441 | 445 | if( link ) BLOB_APPEND_BLOB(ob, link); |
| 442 | 446 | }else{ |
| 443 | 447 | BLOB_APPEND_BLOB(ob, content); |
| 444 | 448 |
| --- 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 @@ | ||
| 525 | 525 | fossil_panic("pledge(\"%s\",NULL) fails with errno=%d", |
| 526 | 526 | promises, (int)errno); |
| 527 | 527 | } |
| 528 | 528 | } |
| 529 | 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 | +} | |
| 530 | 583 |
| --- 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 @@ | ||
| 1629 | 1629 | ** Render markdown wiki from FILE to stdout. |
| 1630 | 1630 | ** |
| 1631 | 1631 | */ |
| 1632 | 1632 | void test_markdown_render(void){ |
| 1633 | 1633 | Blob in, out; |
| 1634 | + db_find_and_open_repository(OPEN_OK_NOT_FOUND|OPEN_SUBSTITUTE,0); | |
| 1634 | 1635 | verify_all_options(); |
| 1635 | 1636 | if( g.argc!=3 ) usage("FILE"); |
| 1636 | 1637 | blob_zero(&out); |
| 1637 | 1638 | blob_read_from_file(&in, g.argv[2], ExtFILE); |
| 1638 | 1639 | markdown_to_html(&in, 0, &out); |
| 1639 | 1640 |
| --- 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 @@ | ||
| 30 | 30 | #define WIKI_NOBLOCK 0x004 /* No block markup of any kind */ |
| 31 | 31 | #define WIKI_BUTTONS 0x008 /* Allow sub-menu buttons */ |
| 32 | 32 | #define WIKI_NOBADLINKS 0x010 /* Ignore broken hyperlinks */ |
| 33 | 33 | #define WIKI_LINKSONLY 0x020 /* No markup. Only decorate links */ |
| 34 | 34 | #define WIKI_NEWLINE 0x040 /* Honor \n - break lines at each \n */ |
| 35 | +#define WIKI_MARKDOWNLINKS 0x080 /* Resolve hyperlinks as in markdown */ | |
| 35 | 36 | #endif |
| 36 | 37 | |
| 37 | 38 | |
| 38 | 39 | /* |
| 39 | 40 | ** These are the only markup attributes allowed. |
| @@ -1211,20 +1212,22 @@ | ||
| 1211 | 1212 | ** [http://www.fossil-scm.org/] |
| 1212 | 1213 | ** [https://www.fossil-scm.org/] |
| 1213 | 1214 | ** [ftp://www.fossil-scm.org/] |
| 1214 | 1215 | ** [mailto:[email protected]] |
| 1215 | 1216 | ** |
| 1216 | -** [/path] | |
| 1217 | +** [/path] -> Refers to the root of the Fossil hierarchy, not | |
| 1218 | +** the root of the URI domain | |
| 1217 | 1219 | ** |
| 1218 | 1220 | ** [./relpath] |
| 1221 | +** [../relpath] | |
| 1219 | 1222 | ** |
| 1220 | -** [WikiPageName] | |
| 1221 | -** [wiki:WikiPageName] | |
| 1223 | +** [#fragment] | |
| 1222 | 1224 | ** |
| 1223 | 1225 | ** [0123456789abcdef] |
| 1224 | 1226 | ** |
| 1225 | -** [#fragment] | |
| 1227 | +** [WikiPageName] | |
| 1228 | +** [wiki:WikiPageName] | |
| 1226 | 1229 | ** |
| 1227 | 1230 | ** [2010-02-27 07:13] |
| 1228 | 1231 | */ |
| 1229 | 1232 | void wiki_resolve_hyperlink( |
| 1230 | 1233 | Blob *pOut, /* Write the HTML output here */ |
| @@ -1296,24 +1299,35 @@ | ||
| 1296 | 1299 | blob_appendf(pOut, "%z[",xhref(zExtraNS, "%R/info/%s", zTarget)); |
| 1297 | 1300 | zTerm = "]</a>"; |
| 1298 | 1301 | }else{ |
| 1299 | 1302 | zTerm = ""; |
| 1300 | 1303 | } |
| 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 | 1304 | }else if( (z = validWikiPageName(mFlags, zTarget))!=0 ){ |
| 1305 | + /* The link is to a valid wiki page name */ | |
| 1305 | 1306 | const char *zOverride = wiki_is_overridden(zTarget); |
| 1306 | 1307 | if( zOverride ){ |
| 1307 | 1308 | blob_appendf(pOut, "<a href=\"%R/info/%S\"%s>", zOverride, zExtra); |
| 1308 | 1309 | }else{ |
| 1309 | 1310 | blob_appendf(pOut, "<a href=\"%R/wiki?name=%T\"%s>", z, zExtra); |
| 1310 | 1311 | } |
| 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. */ | |
| 1313 | 1326 | zTerm = ""; |
| 1314 | 1327 | }else if( (mFlags & (WIKI_NOBADLINKS|WIKI_LINKSONLY))!=0 ){ |
| 1328 | + /* Also ignore the link if various flags are set */ | |
| 1315 | 1329 | zTerm = ""; |
| 1316 | 1330 | }else{ |
| 1317 | 1331 | blob_appendf(pOut, "<span class=\"brokenlink\">[%h]", zTarget); |
| 1318 | 1332 | zTerm = "</span>"; |
| 1319 | 1333 | } |
| @@ -1758,20 +1772,10 @@ | ||
| 1758 | 1772 | } |
| 1759 | 1773 | blob_append(renderer.pOut, "\n", 1); |
| 1760 | 1774 | free(renderer.aStack); |
| 1761 | 1775 | } |
| 1762 | 1776 | |
| 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 | 1777 | /* |
| 1774 | 1778 | ** COMMAND: test-wiki-render |
| 1775 | 1779 | ** |
| 1776 | 1780 | ** Usage: %fossil test-wiki-render FILE [OPTIONS] |
| 1777 | 1781 | ** |
| @@ -1790,10 +1794,11 @@ | ||
| 1790 | 1794 | if( find_option("htmlonly",0,0)!=0 ) flags |= WIKI_HTMLONLY; |
| 1791 | 1795 | if( find_option("linksonly",0,0)!=0 ) flags |= WIKI_LINKSONLY; |
| 1792 | 1796 | if( find_option("nobadlinks",0,0)!=0 ) flags |= WIKI_NOBADLINKS; |
| 1793 | 1797 | if( find_option("inline",0,0)!=0 ) flags |= WIKI_INLINE; |
| 1794 | 1798 | if( find_option("noblock",0,0)!=0 ) flags |= WIKI_NOBLOCK; |
| 1799 | + db_find_and_open_repository(OPEN_OK_NOT_FOUND|OPEN_SUBSTITUTE,0); | |
| 1795 | 1800 | verify_all_options(); |
| 1796 | 1801 | if( g.argc!=3 ) usage("FILE"); |
| 1797 | 1802 | blob_zero(&out); |
| 1798 | 1803 | blob_read_from_file(&in, g.argv[2], ExtFILE); |
| 1799 | 1804 | wiki_convert(&in, &out, flags); |
| 1800 | 1805 |
| --- 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 |
+35
-21
| --- www/server/any/scgi.md | ||
| +++ www/server/any/scgi.md | ||
| @@ -9,20 +9,30 @@ | ||
| 9 | 9 | This can be used with a web server such as [nginx](http://nginx.org) |
| 10 | 10 | which does not support [Fossil’s CGI mode](./cgi.md). |
| 11 | 11 | |
| 12 | 12 | A basic nginx configuration to support SCGI with Fossil looks like this: |
| 13 | 13 | |
| 14 | - location /example/ { | |
| 14 | + location /code/ { | |
| 15 | 15 | include scgi_params; |
| 16 | + scgi_param SCRIPT_NAME "/code"; | |
| 16 | 17 | scgi_pass localhost:9000; |
| 17 | - scgi_param SCRIPT_NAME "/example"; | |
| 18 | - scgi_param HTTPS "on"; | |
| 19 | 18 | } |
| 20 | 19 | |
| 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: | |
| 22 | 32 | |
| 23 | - fossil server /path/to/repo.fossil --scgi --localhost --port 9000 | |
| 33 | + $ fossil server /path/to/repo.fossil --scgi --localhost --port 9000 & | |
| 24 | 34 | |
| 25 | 35 | The `--scgi` option switches Fossil into SCGI mode from its default, |
| 26 | 36 | which is [stand-alone HTTP server mode](./none.md). All of the other |
| 27 | 37 | options discussed in that linked document — such as the ability to serve |
| 28 | 38 | a directory full of Fossil repositories rather than just a single |
| @@ -34,23 +44,27 @@ | ||
| 34 | 44 | |
| 35 | 45 | Giving an explicit non-default TCP port number via `--port` is a good |
| 36 | 46 | idea to avoid conflicts with use of Fossil’s default TCP service port, |
| 37 | 47 | 8080, which may conflict with local uses of `fossil ui` and such. |
| 38 | 48 | |
| 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. | |
| 55 | 67 | |
| 56 | 68 | *[Return to the top-level Fossil server article.](../)* |
| 69 | + | |
| 70 | +[404]: https://en.wikipedia.org/wiki/HTTP_404 | |
| 57 | 71 |
| --- 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 |
+63
-68
| --- www/server/debian/nginx.md | ||
| +++ www/server/debian/nginx.md | ||
| @@ -66,15 +66,15 @@ | ||
| 66 | 66 | |
| 67 | 67 | Fossil provides four major ways to access a repository it’s serving |
| 68 | 68 | remotely, three of which are straightforward to use with nginx: |
| 69 | 69 | |
| 70 | 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. | |
| 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 | 76 | |
| 77 | 77 | * **CGI** — This method is simple but inefficient, because it launches |
| 78 | 78 | a separate Fossil instance on every HTTP hit. |
| 79 | 79 | |
| 80 | 80 | Since Fossil is a relatively small self-contained program, and it’s |
| @@ -107,103 +107,74 @@ | ||
| 107 | 107 | $ sudo apt install fossil nginx |
| 108 | 108 | |
| 109 | 109 | |
| 110 | 110 | ## <a name="scgi"></a>Running Fossil in SCGI Mode |
| 111 | 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. | |
| 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. | |
| 157 | 124 | |
| 158 | 125 | |
| 159 | 126 | ## <a name="config"></a>Configuration |
| 160 | 127 | |
| 161 | 128 | On Debian and Ubuntu systems the primary user-level configuration file |
| 162 | 129 | for nginx is `/etc/nginx/sites-enabled/default`. I recommend that this |
| 163 | 130 | file contain only a list of include statements, one for each site that |
| 164 | 131 | server hosts: |
| 165 | 132 | |
| 166 | - include local/example | |
| 167 | - include local/foo | |
| 133 | + include local/example.com | |
| 134 | + include local/foo.net | |
| 168 | 135 | |
| 169 | 136 | 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`. | |
| 173 | 140 | |
| 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: | |
| 176 | 143 | |
| 177 | 144 | server { |
| 178 | - server_name .foo.net; | |
| 145 | + server_name .example.com .example.net ""; | |
| 179 | 146 | include local/generic; |
| 180 | 147 | |
| 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; | |
| 183 | 150 | |
| 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; | |
| 187 | 159 | |
| 188 | 160 | location ~* \.(html|ico|css|js|gif|jpg|png)$ { |
| 189 | 161 | expires 7d; |
| 190 | 162 | add_header Vary Accept-Encoding; |
| 191 | 163 | access_log off; |
| 192 | 164 | } |
| 193 | 165 | } |
| 194 | 166 | |
| 195 | 167 | # Redirect everything else to the Fossil instance |
| 196 | - location / { | |
| 168 | + location /code { | |
| 197 | 169 | include scgi_params; |
| 170 | + scgi_param SCRIPT_NAME "/code"; | |
| 198 | 171 | scgi_pass 127.0.0.1:12345; |
| 199 | - scgi_param HTTPS "on"; | |
| 200 | - scgi_param SCRIPT_NAME ""; | |
| 201 | 172 | } |
| 202 | 173 | } |
| 203 | 174 | |
| 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 | |
| 205 | 176 | configuration for SCGI][scgii], showing off a few ideas you might want to |
| 206 | 177 | try on your own site, such as static asset proxying. |
| 207 | 178 | |
| 208 | 179 | The `local/generic` file referenced above helps us reduce unnecessary |
| 209 | 180 | repetition among the multiple sites this configuration hosts: |
| @@ -221,10 +192,34 @@ | ||
| 221 | 192 | the `access_log` and `error_log` directives, which follow an obvious |
| 222 | 193 | pattern from one host to the next. Sadly, you must tolerate some |
| 223 | 194 | repetition across `server { }` blocks when setting up multiple domains |
| 224 | 195 | on a single server. |
| 225 | 196 | |
| 226 | -The configuration for `example.com` and `example.net` is similar. | |
| 197 | +The configuration for `foo.net` is similar. | |
| 227 | 198 | |
| 228 | 199 | See [the nginx docs](http://nginx.org/en/docs/) for more ideas. |
| 229 | 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 | + | |
| 230 | 225 | *[Return to the top-level Fossil server article.](../)* |
| 231 | 226 |
| --- 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 |
+4
-3
| --- www/server/index.html | ||
| +++ www/server/index.html | ||
| @@ -77,13 +77,14 @@ | ||
| 77 | 77 | minimum recommended preparation steps:</p> |
| 78 | 78 | |
| 79 | 79 | <ol> |
| 80 | 80 | <li><p>Fossil creates only one user in a <a |
| 81 | 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 | |
| 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 | |
| 85 | 86 | give it a much stronger password under Admin → Users.</a></li> |
| 86 | 87 | |
| 87 | 88 | <li><p>Run the Admin → Security-Audit tool to verify that other |
| 88 | 89 | security-related permissions and settings are as you want them. |
| 89 | 90 | Consider clicking the “Take it private” link on that page to lock down |
| 90 | 91 |
| --- 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 |
+4
-3
| --- www/server/index.html | ||
| +++ www/server/index.html | ||
| @@ -77,13 +77,14 @@ | ||
| 77 | 77 | minimum recommended preparation steps:</p> |
| 78 | 78 | |
| 79 | 79 | <ol> |
| 80 | 80 | <li><p>Fossil creates only one user in a <a |
| 81 | 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 | |
| 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 | |
| 85 | 86 | give it a much stronger password under Admin → Users.</a></li> |
| 86 | 87 | |
| 87 | 88 | <li><p>Run the Admin → Security-Audit tool to verify that other |
| 88 | 89 | security-related permissions and settings are as you want them. |
| 89 | 90 | Consider clicking the “Take it private” link on that page to lock down |
| 90 | 91 |
| --- 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 |