Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
| Comment: | Merged in trunk changes |
|---|---|
| Downloads: | Tarball | ZIP archive |
| Timelines: | family | ancestors | descendants | both | caps-doc |
| Files: | files | file ages | folders |
| SHA3-256: |
493254b2e7753546c4a4c0996c16a9f5 |
| User & Date: | wyoung 2019-08-29 00:31:34.531 |
Context
|
2019-08-29
| ||
| 00:45 | Clarified meaning of EmailAlert (7) in cap ref. ... (check-in: 4aceb600dc user: wyoung tags: caps-doc) | |
| 00:31 | Merged in trunk changes ... (check-in: 493254b2e7 user: wyoung tags: caps-doc) | |
| 00:28 | Updated comment about "6-character random hex password" at the top level of the new setup docs to track [23a9f9bac2]. ... (check-in: f304ba31fe user: wyoung tags: trunk) | |
| 00:21 | Linked to the new material showing Fossil's idea of user power hierarchy from the comment in fossil-v-git.wiki about Fossil's support for the organization's social and power hierarchies. It's not that Fossil has *no* support for enforcing this, it's that it's usually a fairly loose match between the two systems. This is an important point, because some people new to Fossil expect 1:1 mapping and get disappointed when we tell them it just doesn't do that. ... (check-in: b72795a339 user: wyoung tags: caps-doc) | |
Changes
Changes to skins/default/header.txt.
| ︙ | ︙ | |||
15 16 17 18 19 20 21 |
upvar home home
if {[string range $url 0 [string length $current]] eq "/$current"} {
html "<a href='$home$url' class='active $cls'>$name</a>\n"
} else {
html "<a href='$home$url' class='$cls'>$name</a>\n"
}
}
| | | 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
upvar home home
if {[string range $url 0 [string length $current]] eq "/$current"} {
html "<a href='$home$url' class='active $cls'>$name</a>\n"
} else {
html "<a href='$home$url' class='$cls'>$name</a>\n"
}
}
html "<a id='hbbtn' href='/sitemap'>☰</a>"
menulink $index_page Home {}
if {[anycap jor]} {
menulink /timeline Timeline {}
}
if {[hascap oh]} {
menulink /dir?ci=tip Files desktoponly
}
|
| ︙ | ︙ |
Changes to src/checkin.c.
| ︙ | ︙ | |||
1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 | } /* ** COMMAND: ci* ** COMMAND: commit ** ** Usage: %fossil commit ?OPTIONS? ?FILE...? ** ** Create a new version containing all of the changes in the current ** checkout. You will be prompted to enter a check-in comment unless ** the comment has been specified on the command-line using "-m" or a ** file containing the comment using -M. The editor defined in the ** "editor" fossil option (see %fossil help set) will be used, or from ** the "VISUAL" or "EDITOR" environment variables (in that order) if | > | 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 | } /* ** COMMAND: ci* ** COMMAND: commit ** ** Usage: %fossil commit ?OPTIONS? ?FILE...? ** or: %fossil ci ?OPTIONS? ?FILE...? ** ** Create a new version containing all of the changes in the current ** checkout. You will be prompted to enter a check-in comment unless ** the comment has been specified on the command-line using "-m" or a ** file containing the comment using -M. The editor defined in the ** "editor" fossil option (see %fossil help set) will be used, or from ** the "VISUAL" or "EDITOR" environment variables (in that order) if |
| ︙ | ︙ |
Changes to src/db.c.
| ︙ | ︙ | |||
115 116 117 118 119 120 121 122 123 124 125 126 127 128 |
} db = {0, 0, 0, 0, 0, 0, };
/*
** Arrange for the given file to be deleted on a failure.
*/
void db_delete_on_failure(const char *zFilename){
assert( db.nDeleteOnFail<count(db.azDeleteOnFail) );
db.azDeleteOnFail[db.nDeleteOnFail++] = fossil_strdup(zFilename);
}
/*
** Return the transaction nesting depth. 0 means we are currently
** not in a transaction.
*/
| > | 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 |
} db = {0, 0, 0, 0, 0, 0, };
/*
** Arrange for the given file to be deleted on a failure.
*/
void db_delete_on_failure(const char *zFilename){
assert( db.nDeleteOnFail<count(db.azDeleteOnFail) );
if( zFilename==0 ) return;
db.azDeleteOnFail[db.nDeleteOnFail++] = fossil_strdup(zFilename);
}
/*
** Return the transaction nesting depth. 0 means we are currently
** not in a transaction.
*/
|
| ︙ | ︙ | |||
797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 |
db_finalize(&s);
return z;
}
/*
** Initialize a new database file with the given schema. If anything
** goes wrong, call db_err() to exit.
*/
void db_init_database(
const char *zFileName, /* Name of database file to create */
const char *zSchema, /* First part of schema */
... /* Additional SQL to run. Terminate with NULL. */
){
sqlite3 *db;
int rc;
const char *zSql;
va_list ap;
| > > > | > | > > > | 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 |
db_finalize(&s);
return z;
}
/*
** Initialize a new database file with the given schema. If anything
** goes wrong, call db_err() to exit.
**
** If zFilename is NULL, then create an empty repository in an in-memory
** database.
*/
void db_init_database(
const char *zFileName, /* Name of database file to create */
const char *zSchema, /* First part of schema */
... /* Additional SQL to run. Terminate with NULL. */
){
sqlite3 *db;
int rc;
const char *zSql;
va_list ap;
db = db_open(zFileName ? zFileName : ":memory:");
sqlite3_exec(db, "BEGIN EXCLUSIVE", 0, 0, 0);
rc = sqlite3_exec(db, zSchema, 0, 0, 0);
if( rc!=SQLITE_OK ){
db_err("%s", sqlite3_errmsg(db));
}
va_start(ap, zSchema);
while( (zSql = va_arg(ap, const char*))!=0 ){
rc = sqlite3_exec(db, zSql, 0, 0, 0);
if( rc!=SQLITE_OK ){
db_err("%s", sqlite3_errmsg(db));
}
}
va_end(ap);
sqlite3_exec(db, "COMMIT", 0, 0, 0);
if( zFileName || g.db!=0 ){
sqlite3_close(db);
}else{
g.db = db;
}
}
/*
** Function to return the number of seconds since 1970. This is
** the same as strftime('%s','now') but is more compact.
*/
void db_now_function(
|
| ︙ | ︙ | |||
1766 1767 1768 1769 1770 1771 1772 | return g.iRepoDataVers != v; } /* ** Flags for the db_find_and_open_repository() function. */ #if INTERFACE | | | > | 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 | return g.iRepoDataVers != v; } /* ** Flags for the db_find_and_open_repository() function. */ #if INTERFACE #define OPEN_OK_NOT_FOUND 0x001 /* Do not error out if not found */ #define OPEN_ANY_SCHEMA 0x002 /* Do not error if schema is wrong */ #define OPEN_SUBSTITUTE 0x004 /* Fake in-memory repo if not found */ #endif /* ** Try to find the repository and open it. Use the -R or --repository ** option to locate the repository. If no such option is available, then ** use the repository of the open checkout if there is one. ** |
| ︙ | ︙ | |||
1800 1801 1802 1803 1804 1805 1806 |
}
db_open_repository(zRep);
if( g.repositoryOpen ){
if( (bFlags & OPEN_ANY_SCHEMA)==0 ) db_verify_schema();
return;
}
rep_not_found:
| | > > > > > | 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 |
}
db_open_repository(zRep);
if( g.repositoryOpen ){
if( (bFlags & OPEN_ANY_SCHEMA)==0 ) db_verify_schema();
return;
}
rep_not_found:
if( bFlags & OPEN_OK_NOT_FOUND ){
/* No errors if the database is not found */
if( bFlags & OPEN_SUBSTITUTE ){
db_create_repository(0);
}
}else{
#ifdef FOSSIL_ENABLE_JSON
g.json.resultCode = FSL_JSON_E_DB_NOT_FOUND;
#endif
if( nArgUsed==0 ){
fossil_fatal("use --repository or -R to specify the repository database");
}else{
fossil_fatal("specify the repository name as a command-line argument");
|
| ︙ | ︙ | |||
2030 2031 2032 2033 2034 2035 2036 |
if( zUser==0 ){
zUser = "root";
}
db_multi_exec(
"INSERT OR IGNORE INTO user(login, info) VALUES(%Q,'')", zUser
);
db_multi_exec(
| | | | 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 |
if( zUser==0 ){
zUser = "root";
}
db_multi_exec(
"INSERT OR IGNORE INTO user(login, info) VALUES(%Q,'')", zUser
);
db_multi_exec(
"UPDATE user SET cap='s', pw=%Q"
" WHERE login=%Q", fossil_random_password(10), zUser
);
if( !setupUserOnly ){
db_multi_exec(
"INSERT OR IGNORE INTO user(login,pw,cap,info)"
" VALUES('anonymous',hex(randomblob(8)),'hmnc','Anon');"
"INSERT OR IGNORE INTO user(login,pw,cap,info)"
" VALUES('nobody','','gjorz','Nobody');"
|
| ︙ | ︙ |
Changes to src/login.c.
| ︙ | ︙ | |||
2016 2017 2018 2019 2020 2021 2022 |
nCmd = (int)strlen(zCmd);
if( strncmp(zCmd,"join",nCmd)==0 && nCmd>=1 ){
const char *zNewName = find_option("name",0,1);
const char *zOther;
char *zErr = 0;
verify_all_options();
if( g.argc!=4 ){
| | | 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 |
nCmd = (int)strlen(zCmd);
if( strncmp(zCmd,"join",nCmd)==0 && nCmd>=1 ){
const char *zNewName = find_option("name",0,1);
const char *zOther;
char *zErr = 0;
verify_all_options();
if( g.argc!=4 ){
fossil_fatal("unknown extra arguments to \"login-group join\"");
}
zOther = g.argv[3];
login_group_leave(&zErr);
sqlite3_free(zErr);
zErr = 0;
login_group_join(zOther,0,0,0,zNewName,&zErr);
if( zErr ){
|
| ︙ | ︙ | |||
2040 2041 2042 2043 2044 2045 2046 |
char *zErr = 0;
fossil_print("Leaving login-group \"%s\"\n", zLGName);
login_group_leave(&zErr);
if( zErr ) fossil_fatal("Oops: %s", zErr);
return;
}
}else{
| | | 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 |
char *zErr = 0;
fossil_print("Leaving login-group \"%s\"\n", zLGName);
login_group_leave(&zErr);
if( zErr ) fossil_fatal("Oops: %s", zErr);
return;
}
}else{
fossil_fatal("unknown command \"%s\" - should be \"join\" or \"leave\"",
zCmd);
}
}
/* Show the current login group information */
zLGName = login_group_name();
if( zLGName==0 ){
fossil_print("Not currently a part of any login-group\n");
|
| ︙ | ︙ |
Changes to src/markdown_html.c.
| ︙ | ︙ | |||
431 432 433 434 435 436 437 |
char *zLink = blob_buffer(link);
char *zTitle = title!=0 && blob_size(title)>0 ? blob_str(title) : 0;
char zClose[20];
if( zLink==0 || zLink[0]==0 ){
zClose[0] = 0;
}else{
| > > > > | | 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 |
char *zLink = blob_buffer(link);
char *zTitle = title!=0 && blob_size(title)>0 ? blob_str(title) : 0;
char zClose[20];
if( zLink==0 || zLink[0]==0 ){
zClose[0] = 0;
}else{
static const int flags =
WIKI_NOBADLINKS |
WIKI_MARKDOWNLINKS
;
wiki_resolve_hyperlink(ob, flags, zLink, zClose, sizeof(zClose), 0, zTitle);
}
if( blob_size(content)==0 ){
if( link ) BLOB_APPEND_BLOB(ob, link);
}else{
BLOB_APPEND_BLOB(ob, content);
}
blob_append(ob, zClose, -1);
|
| ︙ | ︙ |
Changes to src/util.c.
| ︙ | ︙ | |||
523 524 525 526 527 528 529 |
void fossil_pledge(const char *promises){
if( pledge(promises, 0) ){
fossil_panic("pledge(\"%s\",NULL) fails with errno=%d",
promises, (int)errno);
}
}
#endif /* defined(HAVE_PLEDGE) */
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 |
void fossil_pledge(const char *promises){
if( pledge(promises, 0) ){
fossil_panic("pledge(\"%s\",NULL) fails with errno=%d",
promises, (int)errno);
}
}
#endif /* defined(HAVE_PLEDGE) */
/*
** Construct a random password and return it as a string. N is the
** recommended number of characters for the password.
**
** Space to hold the returned string is obtained from fossil_malloc()
** and should be freed by the caller.
*/
char *fossil_random_password(int N){
char zSrc[60];
int nSrc;
int i;
char z[60];
/* Source characters for the password. Omit characters like "0", "O",
** "1" and "I" that might be easily confused */
static const char zAlphabet[] =
/* 0 1 2 3 4 5 */
/* 123456789 123456789 123456789 123456789 123456789 123456 */
"23456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ";
if( N<8 ) N = 8;
else if( N>sizeof(zAlphabet)-2 ) N = sizeof(zAlphabet)-2;
nSrc = sizeof(zAlphabet) - 1;
memcpy(zSrc, zAlphabet, nSrc);
for(i=0; i<N; i++){
unsigned r;
sqlite3_randomness(sizeof(r), &r);
r %= nSrc;
z[i] = zSrc[r];
zSrc[r] = zSrc[--nSrc];
}
z[i] = 0;
return fossil_strdup(z);
}
/*
** COMMAND: test-random-password
**
** Usage: %fossil test-random-password ?N?
**
** Generate a random password string of approximately N characters in length.
** If N is omitted, use 10. Values of N less than 8 are changed to 8
** and greater than 55 and changed to 55.
*/
void test_random_password(void){
int N = 10;
if( g.argc>=3 ){
N = atoi(g.argv[2]);
}
fossil_print("%s\n", fossil_random_password(N));
}
|
Changes to src/wiki.c.
| ︙ | ︙ | |||
1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 |
** Usage: %fossil test-markdown-render FILE
**
** Render markdown wiki from FILE to stdout.
**
*/
void test_markdown_render(void){
Blob in, out;
verify_all_options();
if( g.argc!=3 ) usage("FILE");
blob_zero(&out);
blob_read_from_file(&in, g.argv[2], ExtFILE);
markdown_to_html(&in, 0, &out);
blob_write_to_file(&out, "-");
}
| > | 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 |
** Usage: %fossil test-markdown-render FILE
**
** Render markdown wiki from FILE to stdout.
**
*/
void test_markdown_render(void){
Blob in, out;
db_find_and_open_repository(OPEN_OK_NOT_FOUND|OPEN_SUBSTITUTE,0);
verify_all_options();
if( g.argc!=3 ) usage("FILE");
blob_zero(&out);
blob_read_from_file(&in, g.argv[2], ExtFILE);
markdown_to_html(&in, 0, &out);
blob_write_to_file(&out, "-");
}
|
| ︙ | ︙ |
Changes to src/wikiformat.c.
| ︙ | ︙ | |||
28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
#define WIKI_HTMLONLY 0x001 /* HTML markup only. No wiki */
#define WIKI_INLINE 0x002 /* Do not surround with <p>..</p> */
#define WIKI_NOBLOCK 0x004 /* No block markup of any kind */
#define WIKI_BUTTONS 0x008 /* Allow sub-menu buttons */
#define WIKI_NOBADLINKS 0x010 /* Ignore broken hyperlinks */
#define WIKI_LINKSONLY 0x020 /* No markup. Only decorate links */
#define WIKI_NEWLINE 0x040 /* Honor \n - break lines at each \n */
#endif
/*
** These are the only markup attributes allowed.
*/
enum allowed_attr_t {
| > | 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
#define WIKI_HTMLONLY 0x001 /* HTML markup only. No wiki */
#define WIKI_INLINE 0x002 /* Do not surround with <p>..</p> */
#define WIKI_NOBLOCK 0x004 /* No block markup of any kind */
#define WIKI_BUTTONS 0x008 /* Allow sub-menu buttons */
#define WIKI_NOBADLINKS 0x010 /* Ignore broken hyperlinks */
#define WIKI_LINKSONLY 0x020 /* No markup. Only decorate links */
#define WIKI_NEWLINE 0x040 /* Honor \n - break lines at each \n */
#define WIKI_MARKDOWNLINKS 0x080 /* Resolve hyperlinks as in markdown */
#endif
/*
** These are the only markup attributes allowed.
*/
enum allowed_attr_t {
|
| ︙ | ︙ | |||
1209 1210 1211 1212 1213 1214 1215 | ** "History" permission. ** ** [http://www.fossil-scm.org/] ** [https://www.fossil-scm.org/] ** [ftp://www.fossil-scm.org/] ** [mailto:fossil-users@lists.fossil-scm.org] ** | | > > | < | > | 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 | ** "History" permission. ** ** [http://www.fossil-scm.org/] ** [https://www.fossil-scm.org/] ** [ftp://www.fossil-scm.org/] ** [mailto:fossil-users@lists.fossil-scm.org] ** ** [/path] -> Refers to the root of the Fossil hierarchy, not ** the root of the URI domain ** ** [./relpath] ** [../relpath] ** ** [#fragment] ** ** [0123456789abcdef] ** ** [WikiPageName] ** [wiki:WikiPageName] ** ** [2010-02-27 07:13] */ void wiki_resolve_hyperlink( Blob *pOut, /* Write the HTML output here */ int mFlags, /* Rendering option flags */ const char *zTarget, /* Hyperlink target; text within [...] */ |
| ︙ | ︙ | |||
1294 1295 1296 1297 1298 1299 1300 |
}
}else if( g.perm.Hyperlink ){
blob_appendf(pOut, "%z[",xhref(zExtraNS, "%R/info/%s", zTarget));
zTerm = "]</a>";
}else{
zTerm = "";
}
| < < < > > > > > > > > > > | > > | > > | 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 |
}
}else if( g.perm.Hyperlink ){
blob_appendf(pOut, "%z[",xhref(zExtraNS, "%R/info/%s", zTarget));
zTerm = "]</a>";
}else{
zTerm = "";
}
}else if( (z = validWikiPageName(mFlags, zTarget))!=0 ){
/* The link is to a valid wiki page name */
const char *zOverride = wiki_is_overridden(zTarget);
if( zOverride ){
blob_appendf(pOut, "<a href=\"%R/info/%S\"%s>", zOverride, zExtra);
}else{
blob_appendf(pOut, "<a href=\"%R/wiki?name=%T\"%s>", z, zExtra);
}
}else if( strlen(zTarget)>=10 && fossil_isdigit(zTarget[0]) && zTarget[4]=='-'
&& db_int(0, "SELECT datetime(%Q) NOT NULL", zTarget) ){
/* Dates or date-and-times in ISO8610 resolve to a link to the
** timeline for that date */
blob_appendf(pOut, "<a href=\"%R/timeline?c=%T\"%s>", zTarget, zExtra);
}else if( mFlags & WIKI_MARKDOWNLINKS ){
/* If none of the above, and if rendering links for markdown, then
** create a link to the literal text of the target */
blob_appendf(pOut, "<a href=\"%h\"%s>", zTarget, zExtra);
}else if( zOrig && zTarget>=&zOrig[2]
&& zTarget[-1]=='[' && !fossil_isspace(zTarget[-2]) ){
/* If the hyperlink markup is not preceded by whitespace, then it
** is probably a C-language subscript or similar, not really a
** hyperlink. Just ignore it. */
zTerm = "";
}else if( (mFlags & (WIKI_NOBADLINKS|WIKI_LINKSONLY))!=0 ){
/* Also ignore the link if various flags are set */
zTerm = "";
}else{
blob_appendf(pOut, "<span class=\"brokenlink\">[%h]", zTarget);
zTerm = "</span>";
}
if( zExtra ) fossil_free(zExtra);
assert( strlen(zTerm)<nClose );
|
| ︙ | ︙ | |||
1756 1757 1758 1759 1760 1761 1762 |
while( renderer.nStack ){
popStack(&renderer);
}
blob_append(renderer.pOut, "\n", 1);
free(renderer.aStack);
}
| < < < < < < < < < < | 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 |
while( renderer.nStack ){
popStack(&renderer);
}
blob_append(renderer.pOut, "\n", 1);
free(renderer.aStack);
}
/*
** COMMAND: test-wiki-render
**
** Usage: %fossil test-wiki-render FILE [OPTIONS]
**
** Options:
** --buttons Set the WIKI_BUTTONS flag
|
| ︙ | ︙ | |||
1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 |
int flags = 0;
if( find_option("buttons",0,0)!=0 ) flags |= WIKI_BUTTONS;
if( find_option("htmlonly",0,0)!=0 ) flags |= WIKI_HTMLONLY;
if( find_option("linksonly",0,0)!=0 ) flags |= WIKI_LINKSONLY;
if( find_option("nobadlinks",0,0)!=0 ) flags |= WIKI_NOBADLINKS;
if( find_option("inline",0,0)!=0 ) flags |= WIKI_INLINE;
if( find_option("noblock",0,0)!=0 ) flags |= WIKI_NOBLOCK;
verify_all_options();
if( g.argc!=3 ) usage("FILE");
blob_zero(&out);
blob_read_from_file(&in, g.argv[2], ExtFILE);
wiki_convert(&in, &out, flags);
blob_write_to_file(&out, "-");
}
| > | 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 |
int flags = 0;
if( find_option("buttons",0,0)!=0 ) flags |= WIKI_BUTTONS;
if( find_option("htmlonly",0,0)!=0 ) flags |= WIKI_HTMLONLY;
if( find_option("linksonly",0,0)!=0 ) flags |= WIKI_LINKSONLY;
if( find_option("nobadlinks",0,0)!=0 ) flags |= WIKI_NOBADLINKS;
if( find_option("inline",0,0)!=0 ) flags |= WIKI_INLINE;
if( find_option("noblock",0,0)!=0 ) flags |= WIKI_NOBLOCK;
db_find_and_open_repository(OPEN_OK_NOT_FOUND|OPEN_SUBSTITUTE,0);
verify_all_options();
if( g.argc!=3 ) usage("FILE");
blob_zero(&out);
blob_read_from_file(&in, g.argv[2], ExtFILE);
wiki_convert(&in, &out, flags);
blob_write_to_file(&out, "-");
}
|
| ︙ | ︙ |
Changes to www/server/any/scgi.md.
1 2 3 4 5 6 7 8 9 10 11 12 13 | # Serving via SCGI There is an alternative to running Fossil as a [standalone HTTP server](./none.md), which is to run it in SimpleCGI (a.k.a. SCGI) mode, which uses the same [`fossil server`](/help/server) command as for HTTP service. Simply add the `--scgi` command-line option and the stand-alone server will speak the SCGI protocol rather than raw HTTP. This can be used with a web server such as [nginx](http://nginx.org) which does not support [Fossil’s CGI mode](./cgi.md). A basic nginx configuration to support SCGI with Fossil looks like this: | | < | | > > > > > > > > | > > > | | < < | | < > | > | > | | | | > > | | > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
# Serving via SCGI
There is an alternative to running Fossil as a [standalone HTTP
server](./none.md), which is to run it in SimpleCGI (a.k.a. SCGI) mode,
which uses the same [`fossil server`](/help/server) command as for HTTP
service. Simply add the `--scgi` command-line option and the stand-alone
server will speak the SCGI protocol rather than raw HTTP.
This can be used with a web server such as [nginx](http://nginx.org)
which does not support [Fossil’s CGI mode](./cgi.md).
A basic nginx configuration to support SCGI with Fossil looks like this:
location /code/ {
include scgi_params;
scgi_param SCRIPT_NAME "/code";
scgi_pass localhost:9000;
}
The `scgi_params` file comes with nginx, and it simply translates nginx
internal variables to `scgi_param` directives to create SCGI environment
variables for the proxied program; in this case, Fossil. Our explicit
`scgi_param` call to define `SCRIPT_NAME` adds one more variable to this
set, which is necessary for this configuration to work properly, because
our repo isn’t at the root of the URL hierarchy. Without it, when Fossil
generates absolute URLs, they’ll be missing the `/code` part at the
start, which will typically cause [404 errors][404].
The final directive simply tells nginx to proxy all calls to URLs under
`/code` down to an SCGI program on TCP port 9000. We can temporarily
set Fossil up as a server on that port like so:
$ fossil server /path/to/repo.fossil --scgi --localhost --port 9000 &
The `--scgi` option switches Fossil into SCGI mode from its default,
which is [stand-alone HTTP server mode](./none.md). All of the other
options discussed in that linked document — such as the ability to serve
a directory full of Fossil repositories rather than just a single
repository — work the same way in SCGI mode.
The `--localhost` option is simply good security: we’re using nginx to
expose Fossil service to the outside world, so there is no good reason
to allow outsiders to contact this Fossil SCGI server directly.
Giving an explicit non-default TCP port number via `--port` is a good
idea to avoid conflicts with use of Fossil’s default TCP service port,
8080, which may conflict with local uses of `fossil ui` and such.
We characterized the SCGI service start command above as “temporary”
because running Fossil in the background like that means it won’t start
back up on a reboot of the server. A simple solution to that is to add
that command to `/etc/rc.local` on systems that have it. However, you
might want to consider setting Fossil up as an OS service instead, so
that you get the benefits of the platform’s service management
framework:
* [Linux (systemd)](../debian/service.md)
* [Windows service](../windows/service.md)
* [macOS (launchd)](../macos/service.md)
* [xinetd](../any/xinetd.md)
* [inetd](../any/inetd.md)
We go into more detail on nginx service setup with Fossil in our
[Debian/Ubuntu specific guide](../debian/nginx.md). Then in [a later
article](../../tls-nginx.md) that builds upon that, we show how to add
TLS encryption to this basic SCGI + nginx setup on Debian type OSes.
*[Return to the top-level Fossil server article.](../)*
[404]: https://en.wikipedia.org/wiki/HTTP_404
|
Changes to www/server/debian/nginx.md.
| ︙ | ︙ | |||
64 65 66 67 68 69 70 | ## <a name="modes"></a>Fossil Service Modes Fossil provides four major ways to access a repository it’s serving remotely, three of which are straightforward to use with nginx: * **HTTP** — Fossil has a built-in HTTP server: [`fossil | | | | < | > | 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 |
## <a name="modes"></a>Fossil Service Modes
Fossil provides four major ways to access a repository it’s serving
remotely, three of which are straightforward to use with nginx:
* **HTTP** — Fossil has a built-in HTTP server: [`fossil
server`](../any/none.md). While this method is efficient and it’s
possible to use nginx to proxy access to another HTTP server, we
don’t see any particularly good reason to make nginx reinterpret
Fossil’s own implementation of HTTP when we have a better option.
(But see [below](#http).)
* **CGI** — This method is simple but inefficient, because it launches
a separate Fossil instance on every HTTP hit.
Since Fossil is a relatively small self-contained program, and it’s
designed to start up quickly, this method can work well in a
surprisingly large number of cases.
|
| ︙ | ︙ | |||
105 106 107 108 109 110 111 |
your server, then say:
$ sudo apt install fossil nginx
## <a name="scgi"></a>Running Fossil in SCGI Mode
| < < < < | < < < < < | < < < | < < < < | < < < < < < < < < | < < | < | < | < < | < < < > | | | | | | | | | | | > > > > > | | | > < < | | 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 |
your server, then say:
$ sudo apt install fossil nginx
## <a name="scgi"></a>Running Fossil in SCGI Mode
For the following nginx configuration to work, it needs to contact a
Fossil instance speaking the SCGI protocol. There are [many ways](../)
to set that up. For Debian type systems, we primarily recommend
following [our systemd user service guide](service.md).
Another option would be to customize [the `fslsrv` shell
script](/file/tools/fslsrv) that ships with Fossil as an example of
launching multiple Fossil instances in the background to serve multiple
URLs.
However you do it, you need to match up the TCP port numbers between it
and those in the nginx configuration below.
## <a name="config"></a>Configuration
On Debian and Ubuntu systems the primary user-level configuration file
for nginx is `/etc/nginx/sites-enabled/default`. I recommend that this
file contain only a list of include statements, one for each site that
server hosts:
include local/example.com
include local/foo.net
Those files then each define one domain’s configuration. Here,
`/etc/nginx/local/example.com` contains the configuration for
`*.example.com` and its alias `*.example.net`; and `local/foo.net`
contains the configuration for `*.foo.net`.
The configuration for our `example.com` web site, stored in
`/etc/nginx/sites-enabled/local/example.com` is:
server {
server_name .example.com .example.net "";
include local/generic;
access_log /var/log/nginx/example.com-https-access.log;
error_log /var/log/nginx/example.com-https-error.log;
# Bypass Fossil for the static documentation generated from
# our source code by Doxygen, so it merges into the embedded
# doc URL hierarchy at Fossil’s $ROOT/doc without requiring that
# these generated files actually be stored in the repo. This
# also lets us set aggressive caching on these docs, since
# they rarely change.
location /code/doc/html {
root /var/www/example.com/code/doc/html;
location ~* \.(html|ico|css|js|gif|jpg|png)$ {
expires 7d;
add_header Vary Accept-Encoding;
access_log off;
}
}
# Redirect everything else to the Fossil instance
location /code {
include scgi_params;
scgi_param SCRIPT_NAME "/code";
scgi_pass 127.0.0.1:12345;
}
}
As you can see, this is a pure extension of [the basic nginx service
configuration for SCGI][scgii], showing off a few ideas you might want to
try on your own site, such as static asset proxying.
The `local/generic` file referenced above helps us reduce unnecessary
repetition among the multiple sites this configuration hosts:
root /var/www/$host;
|
| ︙ | ︙ | |||
219 220 221 222 223 224 225 |
variables into, citing performance considerations, so there is a limit
to how much repetition you can squeeze out this way. One such example is
the `access_log` and `error_log` directives, which follow an obvious
pattern from one host to the next. Sadly, you must tolerate some
repetition across `server { }` blocks when setting up multiple domains
on a single server.
| | > > > > > > > > > > > > > > > > > > > > > > > > | 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 |
variables into, citing performance considerations, so there is a limit
to how much repetition you can squeeze out this way. One such example is
the `access_log` and `error_log` directives, which follow an obvious
pattern from one host to the next. Sadly, you must tolerate some
repetition across `server { }` blocks when setting up multiple domains
on a single server.
The configuration for `foo.net` is similar.
See [the nginx docs](http://nginx.org/en/docs/) for more ideas.
## <a name="http"></a>Proxying HTTP Anyway
[Above](#modes), we argued that proxying SCGI is a better option than
making nginx reinterpret Fossil’s own implementation of HTTP. If you
want Fossil to speak HTTP, just [set Fossil up as a standalone
server](../any/none.md). And if you want nginx to [provide TLS
encryption for Fossil][tls], proxying HTTP instead of SCGI provides no
benefit.
However, it is still worth showing the proper method of proxying
Fossil’s HTTP server through nginx if only to make reading nginx
documentation on other sites easier:
location /code {
rewrite ^/code(/.*) $1 break;
proxy_pass http://127.0.0.1:12345;
}
The most common thing people get wrong when hand-rolling a configuration
like this is to get the slashes wrong. Fossil is senstitive to this. For
instance, Fossil will not collapse double slashes down to a single
slash, as some other HTTP servers will.
*[Return to the top-level Fossil server article.](../)*
|
Changes to www/server/index.html.
| ︙ | ︙ | |||
75 76 77 78 79 80 81 | <p>Prior to serving a Fossil repository to others, consider running <a href="$ROOT/help?cmd=ui"><tt>fossil ui</tt></a> locally and taking these minimum recommended preparation steps:</p> <ol> <li><p>Fossil creates only one user in a <a href="$ROOT/help?cmd=new">new repository</a> and gives it the <a | | | > | | 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 | <p>Prior to serving a Fossil repository to others, consider running <a href="$ROOT/help?cmd=ui"><tt>fossil ui</tt></a> locally and taking these minimum recommended preparation steps:</p> <ol> <li><p>Fossil creates only one user in a <a href="$ROOT/help?cmd=new">new repository</a> and gives it the <a href="../caps/admin-v-setup.md#apsu">all-powerful Setup capability</a>. The 10-digit random password generated for that user is fairly strong against remote attack, even without explicit password guess rate limiting, but because that user has so much power, you may want to give it a much stronger password under Admin → Users.</a></li> <li><p>Run the Admin → Security-Audit tool to verify that other security-related permissions and settings are as you want them. Consider clicking the “Take it private” link on that page to lock down the security on that site to a level appropriate to a private repository, even if you will eventually want some public service. It's |
| ︙ | ︙ |