Fossil SCM

For commands "ui", "server", and "http", and for CGI, when a directory is supplied instead of a specific repository, add the ability to show a list of available repositories under that directory. Enabled by default with the "ui" command. Use --repolist with "server" and "http" and the "repolist" attribute in the CGI script to enable.

drh 2015-02-20 15:40 trunk
Commit 87508e0bf995f4eaee57cdba22fa2875b7bfece4
1 file changed +77 -23
+77 -23
--- src/main.c
+++ src/main.c
@@ -1408,10 +1408,48 @@
14081408
}
14091409
}
14101410
#endif
14111411
return zRepo;
14121412
}
1413
+
1414
+/*
1415
+** Generate a web-page that lists all repositories located under the
1416
+** g.zRepositoryName directory and return non-zero.
1417
+**
1418
+** Or, if no repositories can be located beneath g.zRepositoryName,
1419
+** return 0.
1420
+*/
1421
+static int repo_list_page(void){
1422
+ Blob base;
1423
+ int n = 0;
1424
+
1425
+ assert( g.db==0 );
1426
+ blob_init(&base, g.zRepositoryName, -1);
1427
+ sqlite3_open(":memory:", &g.db);
1428
+ db_multi_exec("CREATE TABLE sfile(x TEXT);");
1429
+ db_multi_exec("CREATE TABLE vfile(pathname);");
1430
+ vfile_scan(&base, blob_size(&base), 0, 0, 0);
1431
+ db_multi_exec("DELETE FROM sfile WHERE x NOT GLOB '*.fossil'");
1432
+ n = db_int(0, "SELECT count(*) FROM sfile");
1433
+ if( n>0 ){
1434
+ Stmt q;
1435
+ @ <h1>Available Repositories:</h1>
1436
+ @ <ol>
1437
+ db_prepare(&q, "SELECT x, substr(x,-7,-100000)||'/home'"
1438
+ " FROM sfile ORDER BY x COLLATE nocase;");
1439
+ while( db_step(&q)==SQLITE_ROW ){
1440
+ const char *zName = db_column_text(&q, 0);
1441
+ const char *zUrl = db_column_text(&q, 1);
1442
+ @ <li><a href="%h(zUrl)">%h(zName)</a></li>
1443
+ }
1444
+ @ </ol>
1445
+ cgi_reply();
1446
+ }
1447
+ sqlite3_close(g.db);
1448
+ g.db = 0;
1449
+ return n;
1450
+}
14131451
14141452
/*
14151453
** Preconditions:
14161454
**
14171455
** * Environment variables are set up according to the CGI standard.
@@ -1431,11 +1469,15 @@
14311469
** $prefix can be determined from its suffix, then the file $prefix is
14321470
** returned as static text.
14331471
**
14341472
** If no suitable webpage is found, try to redirect to zNotFound.
14351473
*/
1436
-static void process_one_web_page(const char *zNotFound, Glob *pFileGlob){
1474
+static void process_one_web_page(
1475
+ const char *zNotFound, /* Redirect here on a 404 if not NULL */
1476
+ Glob *pFileGlob, /* Deliver static files matching */
1477
+ int allowRepoList /* Send repo list for "/" URL */
1478
+){
14371479
const char *zPathInfo;
14381480
char *zPath = NULL;
14391481
int idx;
14401482
int i;
14411483
@@ -1506,10 +1548,12 @@
15061548
15071549
if( szFile<1024 ){
15081550
set_base_url(0);
15091551
if( zNotFound ){
15101552
cgi_redirect(zNotFound);
1553
+ }else if( strcmp(zPathInfo,"/")==0 && repo_list_page() ){
1554
+ /* Will return a list of repositories */
15111555
}else{
15121556
#ifdef FOSSIL_ENABLE_JSON
15131557
if(g.json.isJsonMode){
15141558
json_err(FSL_JSON_E_RESOURCE_NOT_FOUND,NULL,1);
15151559
return;
@@ -1805,10 +1849,11 @@
18051849
const char *zFile;
18061850
const char *zNotFound = 0;
18071851
char **azRedirect = 0; /* List of repositories to redirect to */
18081852
int nRedirect = 0; /* Number of entries in azRedirect */
18091853
Glob *pFileGlob = 0; /* Pattern for files */
1854
+ int allowRepoList = 0; /* Allow lists of repository files */
18101855
Blob config, line, key, value, value2;
18111856
if( g.argc==3 && fossil_strcmp(g.argv[1],"cgi")==0 ){
18121857
zFile = g.argv[2];
18131858
}else{
18141859
zFile = g.argv[1];
@@ -1862,10 +1907,19 @@
18621907
** Grant "administrator" privileges to users connecting with HTTP
18631908
** from IP address 127.0.0.1. Do not bother checking credentials.
18641909
*/
18651910
g.useLocalauth = 1;
18661911
continue;
1912
+ }
1913
+ if( blob_eq(&key, "repolist") ){
1914
+ /* repolist
1915
+ **
1916
+ ** If using "directory:" and the URL is "/" then generate a page
1917
+ ** showing a list of available repositories.
1918
+ */
1919
+ allowRepoList = 1;
1920
+ continue;
18671921
}
18681922
if( blob_eq(&key, "redirect:") && blob_token(&line, &value)
18691923
&& blob_token(&line, &value2) ){
18701924
/* See the header comment on the redirect_web_page() function
18711925
** above for details. */
@@ -1952,11 +2006,11 @@
19522006
}
19532007
cgi_init();
19542008
if( nRedirect ){
19552009
redirect_web_page(nRedirect, azRedirect);
19562010
}else{
1957
- process_one_web_page(zNotFound, pFileGlob);
2011
+ process_one_web_page(zNotFound, pFileGlob, allowRepoList);
19582012
}
19592013
}
19602014
19612015
/*
19622016
** If g.argv[arg] exists then it is either the name of a repository
@@ -1970,24 +2024,17 @@
19702024
** Open the repository to be served if it is known. If g.argv[arg] is
19712025
** a directory full of repositories, then set g.zRepositoryName to
19722026
** the name of that directory and the specific repository will be
19732027
** opened later by process_one_web_page() based on the content of
19742028
** the PATH_INFO variable.
1975
-**
1976
-** If disallowDir is set, then the directory full of repositories method
1977
-** is disallowed.
19782029
*/
1979
-static void find_server_repository(int disallowDir, int arg){
2030
+static void find_server_repository(int arg){
19802031
if( g.argc<=arg ){
19812032
db_must_be_within_tree();
19822033
}else if( file_isdir(g.argv[arg])==1 ){
1983
- if( disallowDir ){
1984
- fossil_fatal("\"%s\" is a directory, not a repository file", g.argv[arg]);
1985
- }else{
1986
- g.zRepositoryName = mprintf("%s", g.argv[arg]);
1987
- file_simplify_name(g.zRepositoryName, -1, 0);
1988
- }
2034
+ g.zRepositoryName = mprintf("%s", g.argv[arg]);
2035
+ file_simplify_name(g.zRepositoryName, -1, 0);
19892036
}else{
19902037
db_open_repository(g.argv[arg]);
19912038
}
19922039
}
19932040
@@ -2028,18 +2075,19 @@
20282075
** If the --localauth option is given, then automatic login is performed
20292076
** for requests coming from localhost, if the "localauth" setting is not
20302077
** enabled.
20312078
**
20322079
** Options:
2080
+** --baseurl URL base URL (useful with reverse proxies)
2081
+** --files GLOB comma-separate glob patterns for static file to serve
20332082
** --localauth enable automatic login for local connections
20342083
** --host NAME specify hostname of the server
20352084
** --https signal a request coming in via https
20362085
** --nojail drop root privilege but do not enter the chroot jail
20372086
** --nossl signal that no SSL connections are available
20382087
** --notfound URL use URL as "HTTP 404, object not found" page.
2039
-** --files GLOB comma-separate glob patterns for static file to serve
2040
-** --baseurl URL base URL (useful with reverse proxies)
2088
+** --repolist If REPOSITORY is directory, URL "/" lists all repos
20412089
** --scgi Interpret input as SCGI rather than HTTP
20422090
** --skin LABEL Use override skin LABEL
20432091
**
20442092
** See also: cgi, server, winsrv
20452093
*/
@@ -2049,10 +2097,11 @@
20492097
const char *zHost;
20502098
const char *zAltBase;
20512099
const char *zFileGlob;
20522100
int useSCGI;
20532101
int noJail;
2102
+ int allowRepoList;
20542103
20552104
/* The winhttp module passes the --files option as --files-urlenc with
20562105
** the argument being URL encoded, to avoid wildcard expansion in the
20572106
** shell. This option is for internal use and is undocumented.
20582107
*/
@@ -2065,10 +2114,11 @@
20652114
zFileGlob = find_option("files",0,1);
20662115
}
20672116
skin_override();
20682117
zNotFound = find_option("notfound", 0, 1);
20692118
noJail = find_option("nojail",0,0)!=0;
2119
+ allowRepoList = find_option("repolist",0,0)!=0;
20702120
g.useLocalauth = find_option("localauth", 0, 0)!=0;
20712121
g.sslNotAvailable = find_option("nossl", 0, 0)!=0;
20722122
useSCGI = find_option("scgi", 0, 0)!=0;
20732123
zAltBase = find_option("baseurl", 0, 1);
20742124
if( zAltBase ) set_base_url(zAltBase);
@@ -2089,15 +2139,15 @@
20892139
g.fullHttpReply = 1;
20902140
if( g.argc>=5 ){
20912141
g.httpIn = fossil_fopen(g.argv[2], "rb");
20922142
g.httpOut = fossil_fopen(g.argv[3], "wb");
20932143
zIpAddr = g.argv[4];
2094
- find_server_repository(0, 5);
2144
+ find_server_repository(5);
20952145
}else{
20962146
g.httpIn = stdin;
20972147
g.httpOut = stdout;
2098
- find_server_repository(0, 2);
2148
+ find_server_repository(2);
20992149
}
21002150
if( zIpAddr==0 ){
21012151
zIpAddr = cgi_ssh_remote_addr(0);
21022152
if( zIpAddr && zIpAddr[0] ){
21032153
g.fSshClient |= CGI_SSH_CLIENT;
@@ -2109,21 +2159,21 @@
21092159
}else if( g.fSshClient & CGI_SSH_CLIENT ){
21102160
ssh_request_loop(zIpAddr, glob_create(zFileGlob));
21112161
}else{
21122162
cgi_handle_http_request(zIpAddr);
21132163
}
2114
- process_one_web_page(zNotFound, glob_create(zFileGlob));
2164
+ process_one_web_page(zNotFound, glob_create(zFileGlob), allowRepoList);
21152165
}
21162166
21172167
/*
21182168
** Process all requests in a single SSH connection if possible.
21192169
*/
21202170
void ssh_request_loop(const char *zIpAddr, Glob *FileGlob){
21212171
blob_zero(&g.cgiIn);
21222172
do{
21232173
cgi_handle_ssh_http_request(zIpAddr);
2124
- process_one_web_page(0, FileGlob);
2174
+ process_one_web_page(0, FileGlob, 0);
21252175
blob_reset(&g.cgiIn);
21262176
} while ( g.fSshClient & CGI_SSH_FOSSIL ||
21272177
g.fSshClient & CGI_SSH_COMPAT );
21282178
}
21292179
@@ -2140,21 +2190,21 @@
21402190
Th_InitTraceLog();
21412191
login_set_capabilities("sx", 0);
21422192
g.useLocalauth = 1;
21432193
g.httpIn = stdin;
21442194
g.httpOut = stdout;
2145
- find_server_repository(0, 2);
2195
+ find_server_repository(2);
21462196
g.cgiOutput = 1;
21472197
g.fullHttpReply = 1;
21482198
zIpAddr = cgi_ssh_remote_addr(0);
21492199
if( zIpAddr && zIpAddr[0] ){
21502200
g.fSshClient |= CGI_SSH_CLIENT;
21512201
ssh_request_loop(zIpAddr, 0);
21522202
}else{
21532203
cgi_set_parameter("REMOTE_ADDR", "127.0.0.1");
21542204
cgi_handle_http_request(0);
2155
- process_one_web_page(0, 0);
2205
+ process_one_web_page(0, 0, 0);
21562206
}
21572207
}
21582208
21592209
#if !defined(_WIN32)
21602210
#if !defined(__DARWIN__) && !defined(__APPLE__) && !defined(__HAIKU__)
@@ -2227,10 +2277,11 @@
22272277
** --localhost listen on 127.0.0.1 only (always true for "ui")
22282278
** --nojail Drop root privileges but do not enter the chroot jail
22292279
** --notfound URL Redirect
22302280
** -P|--port TCPPORT listen to request on port TCPPORT
22312281
** --th-trace trace TH1 execution (for debugging purposes)
2282
+** --repolist If REPOSITORY is dir, URL "/" lists repos.
22322283
** --scgi Accept SCGI rather than HTTP
22332284
** --skin LABEL Use override skin LABEL
22342285
22352286
**
22362287
** See also: cgi, http, winsrv
@@ -2244,10 +2295,11 @@
22442295
const char *zNotFound; /* The --notfound option or NULL */
22452296
int flags = 0; /* Server flags */
22462297
#if !defined(_WIN32)
22472298
int noJail; /* Do not enter the chroot jail */
22482299
#endif
2300
+ int allowRepoList; /* List repositories on URL "/" */
22492301
const char *zAltBase; /* Argument to the --baseurl option */
22502302
const char *zFileGlob; /* Static content must match this */
22512303
char *zIpAddr = 0; /* Bind to this IP address */
22522304
22532305
#if defined(_WIN32)
@@ -2269,10 +2321,11 @@
22692321
#endif
22702322
g.useLocalauth = find_option("localauth", 0, 0)!=0;
22712323
Th_InitTraceLog();
22722324
zPort = find_option("port", "P", 1);
22732325
zNotFound = find_option("notfound", 0, 1);
2326
+ allowRepoList = find_option("repolist",0,0)!=0;
22742327
zAltBase = find_option("baseurl", 0, 1);
22752328
if( find_option("scgi", 0, 0)!=0 ) flags |= HTTP_SERVER_SCGI;
22762329
if( zAltBase ){
22772330
set_base_url(zAltBase);
22782331
}
@@ -2286,12 +2339,13 @@
22862339
if( g.argc!=2 && g.argc!=3 ) usage("?REPOSITORY?");
22872340
isUiCmd = g.argv[1][0]=='u';
22882341
if( isUiCmd ){
22892342
flags |= HTTP_SERVER_LOCALHOST;
22902343
g.useLocalauth = 1;
2344
+ allowRepoList = 1;
22912345
}
2292
- find_server_repository(isUiCmd && zNotFound==0, 2);
2346
+ find_server_repository(2);
22932347
if( zPort ){
22942348
int i;
22952349
for(i=strlen(zPort)-1; i>=0 && zPort[i]!=':'; i--){}
22962350
if( i>0 ){
22972351
zIpAddr = mprintf("%.*s", i, zPort);
@@ -2341,18 +2395,18 @@
23412395
g.httpOut = stdout;
23422396
if( g.fHttpTrace || g.fSqlTrace ){
23432397
fprintf(stderr, "====== SERVER pid %d =======\n", getpid());
23442398
}
23452399
g.cgiOutput = 1;
2346
- find_server_repository(isUiCmd && zNotFound==0, 2);
2400
+ find_server_repository(2);
23472401
g.zRepositoryName = enter_chroot_jail(g.zRepositoryName, noJail);
23482402
if( flags & HTTP_SERVER_SCGI ){
23492403
cgi_handle_scgi_request();
23502404
}else{
23512405
cgi_handle_http_request(0);
23522406
}
2353
- process_one_web_page(zNotFound, glob_create(zFileGlob));
2407
+ process_one_web_page(zNotFound, glob_create(zFileGlob), allowRepoList);
23542408
#else
23552409
/* Win32 implementation */
23562410
if( isUiCmd ){
23572411
zBrowser = db_get("web-browser", "start");
23582412
if( zIpAddr ){
23592413
--- src/main.c
+++ src/main.c
@@ -1408,10 +1408,48 @@
1408 }
1409 }
1410 #endif
1411 return zRepo;
1412 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1413
1414 /*
1415 ** Preconditions:
1416 **
1417 ** * Environment variables are set up according to the CGI standard.
@@ -1431,11 +1469,15 @@
1431 ** $prefix can be determined from its suffix, then the file $prefix is
1432 ** returned as static text.
1433 **
1434 ** If no suitable webpage is found, try to redirect to zNotFound.
1435 */
1436 static void process_one_web_page(const char *zNotFound, Glob *pFileGlob){
 
 
 
 
1437 const char *zPathInfo;
1438 char *zPath = NULL;
1439 int idx;
1440 int i;
1441
@@ -1506,10 +1548,12 @@
1506
1507 if( szFile<1024 ){
1508 set_base_url(0);
1509 if( zNotFound ){
1510 cgi_redirect(zNotFound);
 
 
1511 }else{
1512 #ifdef FOSSIL_ENABLE_JSON
1513 if(g.json.isJsonMode){
1514 json_err(FSL_JSON_E_RESOURCE_NOT_FOUND,NULL,1);
1515 return;
@@ -1805,10 +1849,11 @@
1805 const char *zFile;
1806 const char *zNotFound = 0;
1807 char **azRedirect = 0; /* List of repositories to redirect to */
1808 int nRedirect = 0; /* Number of entries in azRedirect */
1809 Glob *pFileGlob = 0; /* Pattern for files */
 
1810 Blob config, line, key, value, value2;
1811 if( g.argc==3 && fossil_strcmp(g.argv[1],"cgi")==0 ){
1812 zFile = g.argv[2];
1813 }else{
1814 zFile = g.argv[1];
@@ -1862,10 +1907,19 @@
1862 ** Grant "administrator" privileges to users connecting with HTTP
1863 ** from IP address 127.0.0.1. Do not bother checking credentials.
1864 */
1865 g.useLocalauth = 1;
1866 continue;
 
 
 
 
 
 
 
 
 
1867 }
1868 if( blob_eq(&key, "redirect:") && blob_token(&line, &value)
1869 && blob_token(&line, &value2) ){
1870 /* See the header comment on the redirect_web_page() function
1871 ** above for details. */
@@ -1952,11 +2006,11 @@
1952 }
1953 cgi_init();
1954 if( nRedirect ){
1955 redirect_web_page(nRedirect, azRedirect);
1956 }else{
1957 process_one_web_page(zNotFound, pFileGlob);
1958 }
1959 }
1960
1961 /*
1962 ** If g.argv[arg] exists then it is either the name of a repository
@@ -1970,24 +2024,17 @@
1970 ** Open the repository to be served if it is known. If g.argv[arg] is
1971 ** a directory full of repositories, then set g.zRepositoryName to
1972 ** the name of that directory and the specific repository will be
1973 ** opened later by process_one_web_page() based on the content of
1974 ** the PATH_INFO variable.
1975 **
1976 ** If disallowDir is set, then the directory full of repositories method
1977 ** is disallowed.
1978 */
1979 static void find_server_repository(int disallowDir, int arg){
1980 if( g.argc<=arg ){
1981 db_must_be_within_tree();
1982 }else if( file_isdir(g.argv[arg])==1 ){
1983 if( disallowDir ){
1984 fossil_fatal("\"%s\" is a directory, not a repository file", g.argv[arg]);
1985 }else{
1986 g.zRepositoryName = mprintf("%s", g.argv[arg]);
1987 file_simplify_name(g.zRepositoryName, -1, 0);
1988 }
1989 }else{
1990 db_open_repository(g.argv[arg]);
1991 }
1992 }
1993
@@ -2028,18 +2075,19 @@
2028 ** If the --localauth option is given, then automatic login is performed
2029 ** for requests coming from localhost, if the "localauth" setting is not
2030 ** enabled.
2031 **
2032 ** Options:
 
 
2033 ** --localauth enable automatic login for local connections
2034 ** --host NAME specify hostname of the server
2035 ** --https signal a request coming in via https
2036 ** --nojail drop root privilege but do not enter the chroot jail
2037 ** --nossl signal that no SSL connections are available
2038 ** --notfound URL use URL as "HTTP 404, object not found" page.
2039 ** --files GLOB comma-separate glob patterns for static file to serve
2040 ** --baseurl URL base URL (useful with reverse proxies)
2041 ** --scgi Interpret input as SCGI rather than HTTP
2042 ** --skin LABEL Use override skin LABEL
2043 **
2044 ** See also: cgi, server, winsrv
2045 */
@@ -2049,10 +2097,11 @@
2049 const char *zHost;
2050 const char *zAltBase;
2051 const char *zFileGlob;
2052 int useSCGI;
2053 int noJail;
 
2054
2055 /* The winhttp module passes the --files option as --files-urlenc with
2056 ** the argument being URL encoded, to avoid wildcard expansion in the
2057 ** shell. This option is for internal use and is undocumented.
2058 */
@@ -2065,10 +2114,11 @@
2065 zFileGlob = find_option("files",0,1);
2066 }
2067 skin_override();
2068 zNotFound = find_option("notfound", 0, 1);
2069 noJail = find_option("nojail",0,0)!=0;
 
2070 g.useLocalauth = find_option("localauth", 0, 0)!=0;
2071 g.sslNotAvailable = find_option("nossl", 0, 0)!=0;
2072 useSCGI = find_option("scgi", 0, 0)!=0;
2073 zAltBase = find_option("baseurl", 0, 1);
2074 if( zAltBase ) set_base_url(zAltBase);
@@ -2089,15 +2139,15 @@
2089 g.fullHttpReply = 1;
2090 if( g.argc>=5 ){
2091 g.httpIn = fossil_fopen(g.argv[2], "rb");
2092 g.httpOut = fossil_fopen(g.argv[3], "wb");
2093 zIpAddr = g.argv[4];
2094 find_server_repository(0, 5);
2095 }else{
2096 g.httpIn = stdin;
2097 g.httpOut = stdout;
2098 find_server_repository(0, 2);
2099 }
2100 if( zIpAddr==0 ){
2101 zIpAddr = cgi_ssh_remote_addr(0);
2102 if( zIpAddr && zIpAddr[0] ){
2103 g.fSshClient |= CGI_SSH_CLIENT;
@@ -2109,21 +2159,21 @@
2109 }else if( g.fSshClient & CGI_SSH_CLIENT ){
2110 ssh_request_loop(zIpAddr, glob_create(zFileGlob));
2111 }else{
2112 cgi_handle_http_request(zIpAddr);
2113 }
2114 process_one_web_page(zNotFound, glob_create(zFileGlob));
2115 }
2116
2117 /*
2118 ** Process all requests in a single SSH connection if possible.
2119 */
2120 void ssh_request_loop(const char *zIpAddr, Glob *FileGlob){
2121 blob_zero(&g.cgiIn);
2122 do{
2123 cgi_handle_ssh_http_request(zIpAddr);
2124 process_one_web_page(0, FileGlob);
2125 blob_reset(&g.cgiIn);
2126 } while ( g.fSshClient & CGI_SSH_FOSSIL ||
2127 g.fSshClient & CGI_SSH_COMPAT );
2128 }
2129
@@ -2140,21 +2190,21 @@
2140 Th_InitTraceLog();
2141 login_set_capabilities("sx", 0);
2142 g.useLocalauth = 1;
2143 g.httpIn = stdin;
2144 g.httpOut = stdout;
2145 find_server_repository(0, 2);
2146 g.cgiOutput = 1;
2147 g.fullHttpReply = 1;
2148 zIpAddr = cgi_ssh_remote_addr(0);
2149 if( zIpAddr && zIpAddr[0] ){
2150 g.fSshClient |= CGI_SSH_CLIENT;
2151 ssh_request_loop(zIpAddr, 0);
2152 }else{
2153 cgi_set_parameter("REMOTE_ADDR", "127.0.0.1");
2154 cgi_handle_http_request(0);
2155 process_one_web_page(0, 0);
2156 }
2157 }
2158
2159 #if !defined(_WIN32)
2160 #if !defined(__DARWIN__) && !defined(__APPLE__) && !defined(__HAIKU__)
@@ -2227,10 +2277,11 @@
2227 ** --localhost listen on 127.0.0.1 only (always true for "ui")
2228 ** --nojail Drop root privileges but do not enter the chroot jail
2229 ** --notfound URL Redirect
2230 ** -P|--port TCPPORT listen to request on port TCPPORT
2231 ** --th-trace trace TH1 execution (for debugging purposes)
 
2232 ** --scgi Accept SCGI rather than HTTP
2233 ** --skin LABEL Use override skin LABEL
2234
2235 **
2236 ** See also: cgi, http, winsrv
@@ -2244,10 +2295,11 @@
2244 const char *zNotFound; /* The --notfound option or NULL */
2245 int flags = 0; /* Server flags */
2246 #if !defined(_WIN32)
2247 int noJail; /* Do not enter the chroot jail */
2248 #endif
 
2249 const char *zAltBase; /* Argument to the --baseurl option */
2250 const char *zFileGlob; /* Static content must match this */
2251 char *zIpAddr = 0; /* Bind to this IP address */
2252
2253 #if defined(_WIN32)
@@ -2269,10 +2321,11 @@
2269 #endif
2270 g.useLocalauth = find_option("localauth", 0, 0)!=0;
2271 Th_InitTraceLog();
2272 zPort = find_option("port", "P", 1);
2273 zNotFound = find_option("notfound", 0, 1);
 
2274 zAltBase = find_option("baseurl", 0, 1);
2275 if( find_option("scgi", 0, 0)!=0 ) flags |= HTTP_SERVER_SCGI;
2276 if( zAltBase ){
2277 set_base_url(zAltBase);
2278 }
@@ -2286,12 +2339,13 @@
2286 if( g.argc!=2 && g.argc!=3 ) usage("?REPOSITORY?");
2287 isUiCmd = g.argv[1][0]=='u';
2288 if( isUiCmd ){
2289 flags |= HTTP_SERVER_LOCALHOST;
2290 g.useLocalauth = 1;
 
2291 }
2292 find_server_repository(isUiCmd && zNotFound==0, 2);
2293 if( zPort ){
2294 int i;
2295 for(i=strlen(zPort)-1; i>=0 && zPort[i]!=':'; i--){}
2296 if( i>0 ){
2297 zIpAddr = mprintf("%.*s", i, zPort);
@@ -2341,18 +2395,18 @@
2341 g.httpOut = stdout;
2342 if( g.fHttpTrace || g.fSqlTrace ){
2343 fprintf(stderr, "====== SERVER pid %d =======\n", getpid());
2344 }
2345 g.cgiOutput = 1;
2346 find_server_repository(isUiCmd && zNotFound==0, 2);
2347 g.zRepositoryName = enter_chroot_jail(g.zRepositoryName, noJail);
2348 if( flags & HTTP_SERVER_SCGI ){
2349 cgi_handle_scgi_request();
2350 }else{
2351 cgi_handle_http_request(0);
2352 }
2353 process_one_web_page(zNotFound, glob_create(zFileGlob));
2354 #else
2355 /* Win32 implementation */
2356 if( isUiCmd ){
2357 zBrowser = db_get("web-browser", "start");
2358 if( zIpAddr ){
2359
--- src/main.c
+++ src/main.c
@@ -1408,10 +1408,48 @@
1408 }
1409 }
1410 #endif
1411 return zRepo;
1412 }
1413
1414 /*
1415 ** Generate a web-page that lists all repositories located under the
1416 ** g.zRepositoryName directory and return non-zero.
1417 **
1418 ** Or, if no repositories can be located beneath g.zRepositoryName,
1419 ** return 0.
1420 */
1421 static int repo_list_page(void){
1422 Blob base;
1423 int n = 0;
1424
1425 assert( g.db==0 );
1426 blob_init(&base, g.zRepositoryName, -1);
1427 sqlite3_open(":memory:", &g.db);
1428 db_multi_exec("CREATE TABLE sfile(x TEXT);");
1429 db_multi_exec("CREATE TABLE vfile(pathname);");
1430 vfile_scan(&base, blob_size(&base), 0, 0, 0);
1431 db_multi_exec("DELETE FROM sfile WHERE x NOT GLOB '*.fossil'");
1432 n = db_int(0, "SELECT count(*) FROM sfile");
1433 if( n>0 ){
1434 Stmt q;
1435 @ <h1>Available Repositories:</h1>
1436 @ <ol>
1437 db_prepare(&q, "SELECT x, substr(x,-7,-100000)||'/home'"
1438 " FROM sfile ORDER BY x COLLATE nocase;");
1439 while( db_step(&q)==SQLITE_ROW ){
1440 const char *zName = db_column_text(&q, 0);
1441 const char *zUrl = db_column_text(&q, 1);
1442 @ <li><a href="%h(zUrl)">%h(zName)</a></li>
1443 }
1444 @ </ol>
1445 cgi_reply();
1446 }
1447 sqlite3_close(g.db);
1448 g.db = 0;
1449 return n;
1450 }
1451
1452 /*
1453 ** Preconditions:
1454 **
1455 ** * Environment variables are set up according to the CGI standard.
@@ -1431,11 +1469,15 @@
1469 ** $prefix can be determined from its suffix, then the file $prefix is
1470 ** returned as static text.
1471 **
1472 ** If no suitable webpage is found, try to redirect to zNotFound.
1473 */
1474 static void process_one_web_page(
1475 const char *zNotFound, /* Redirect here on a 404 if not NULL */
1476 Glob *pFileGlob, /* Deliver static files matching */
1477 int allowRepoList /* Send repo list for "/" URL */
1478 ){
1479 const char *zPathInfo;
1480 char *zPath = NULL;
1481 int idx;
1482 int i;
1483
@@ -1506,10 +1548,12 @@
1548
1549 if( szFile<1024 ){
1550 set_base_url(0);
1551 if( zNotFound ){
1552 cgi_redirect(zNotFound);
1553 }else if( strcmp(zPathInfo,"/")==0 && repo_list_page() ){
1554 /* Will return a list of repositories */
1555 }else{
1556 #ifdef FOSSIL_ENABLE_JSON
1557 if(g.json.isJsonMode){
1558 json_err(FSL_JSON_E_RESOURCE_NOT_FOUND,NULL,1);
1559 return;
@@ -1805,10 +1849,11 @@
1849 const char *zFile;
1850 const char *zNotFound = 0;
1851 char **azRedirect = 0; /* List of repositories to redirect to */
1852 int nRedirect = 0; /* Number of entries in azRedirect */
1853 Glob *pFileGlob = 0; /* Pattern for files */
1854 int allowRepoList = 0; /* Allow lists of repository files */
1855 Blob config, line, key, value, value2;
1856 if( g.argc==3 && fossil_strcmp(g.argv[1],"cgi")==0 ){
1857 zFile = g.argv[2];
1858 }else{
1859 zFile = g.argv[1];
@@ -1862,10 +1907,19 @@
1907 ** Grant "administrator" privileges to users connecting with HTTP
1908 ** from IP address 127.0.0.1. Do not bother checking credentials.
1909 */
1910 g.useLocalauth = 1;
1911 continue;
1912 }
1913 if( blob_eq(&key, "repolist") ){
1914 /* repolist
1915 **
1916 ** If using "directory:" and the URL is "/" then generate a page
1917 ** showing a list of available repositories.
1918 */
1919 allowRepoList = 1;
1920 continue;
1921 }
1922 if( blob_eq(&key, "redirect:") && blob_token(&line, &value)
1923 && blob_token(&line, &value2) ){
1924 /* See the header comment on the redirect_web_page() function
1925 ** above for details. */
@@ -1952,11 +2006,11 @@
2006 }
2007 cgi_init();
2008 if( nRedirect ){
2009 redirect_web_page(nRedirect, azRedirect);
2010 }else{
2011 process_one_web_page(zNotFound, pFileGlob, allowRepoList);
2012 }
2013 }
2014
2015 /*
2016 ** If g.argv[arg] exists then it is either the name of a repository
@@ -1970,24 +2024,17 @@
2024 ** Open the repository to be served if it is known. If g.argv[arg] is
2025 ** a directory full of repositories, then set g.zRepositoryName to
2026 ** the name of that directory and the specific repository will be
2027 ** opened later by process_one_web_page() based on the content of
2028 ** the PATH_INFO variable.
 
 
 
2029 */
2030 static void find_server_repository(int arg){
2031 if( g.argc<=arg ){
2032 db_must_be_within_tree();
2033 }else if( file_isdir(g.argv[arg])==1 ){
2034 g.zRepositoryName = mprintf("%s", g.argv[arg]);
2035 file_simplify_name(g.zRepositoryName, -1, 0);
 
 
 
 
2036 }else{
2037 db_open_repository(g.argv[arg]);
2038 }
2039 }
2040
@@ -2028,18 +2075,19 @@
2075 ** If the --localauth option is given, then automatic login is performed
2076 ** for requests coming from localhost, if the "localauth" setting is not
2077 ** enabled.
2078 **
2079 ** Options:
2080 ** --baseurl URL base URL (useful with reverse proxies)
2081 ** --files GLOB comma-separate glob patterns for static file to serve
2082 ** --localauth enable automatic login for local connections
2083 ** --host NAME specify hostname of the server
2084 ** --https signal a request coming in via https
2085 ** --nojail drop root privilege but do not enter the chroot jail
2086 ** --nossl signal that no SSL connections are available
2087 ** --notfound URL use URL as "HTTP 404, object not found" page.
2088 ** --repolist If REPOSITORY is directory, URL "/" lists all repos
 
2089 ** --scgi Interpret input as SCGI rather than HTTP
2090 ** --skin LABEL Use override skin LABEL
2091 **
2092 ** See also: cgi, server, winsrv
2093 */
@@ -2049,10 +2097,11 @@
2097 const char *zHost;
2098 const char *zAltBase;
2099 const char *zFileGlob;
2100 int useSCGI;
2101 int noJail;
2102 int allowRepoList;
2103
2104 /* The winhttp module passes the --files option as --files-urlenc with
2105 ** the argument being URL encoded, to avoid wildcard expansion in the
2106 ** shell. This option is for internal use and is undocumented.
2107 */
@@ -2065,10 +2114,11 @@
2114 zFileGlob = find_option("files",0,1);
2115 }
2116 skin_override();
2117 zNotFound = find_option("notfound", 0, 1);
2118 noJail = find_option("nojail",0,0)!=0;
2119 allowRepoList = find_option("repolist",0,0)!=0;
2120 g.useLocalauth = find_option("localauth", 0, 0)!=0;
2121 g.sslNotAvailable = find_option("nossl", 0, 0)!=0;
2122 useSCGI = find_option("scgi", 0, 0)!=0;
2123 zAltBase = find_option("baseurl", 0, 1);
2124 if( zAltBase ) set_base_url(zAltBase);
@@ -2089,15 +2139,15 @@
2139 g.fullHttpReply = 1;
2140 if( g.argc>=5 ){
2141 g.httpIn = fossil_fopen(g.argv[2], "rb");
2142 g.httpOut = fossil_fopen(g.argv[3], "wb");
2143 zIpAddr = g.argv[4];
2144 find_server_repository(5);
2145 }else{
2146 g.httpIn = stdin;
2147 g.httpOut = stdout;
2148 find_server_repository(2);
2149 }
2150 if( zIpAddr==0 ){
2151 zIpAddr = cgi_ssh_remote_addr(0);
2152 if( zIpAddr && zIpAddr[0] ){
2153 g.fSshClient |= CGI_SSH_CLIENT;
@@ -2109,21 +2159,21 @@
2159 }else if( g.fSshClient & CGI_SSH_CLIENT ){
2160 ssh_request_loop(zIpAddr, glob_create(zFileGlob));
2161 }else{
2162 cgi_handle_http_request(zIpAddr);
2163 }
2164 process_one_web_page(zNotFound, glob_create(zFileGlob), allowRepoList);
2165 }
2166
2167 /*
2168 ** Process all requests in a single SSH connection if possible.
2169 */
2170 void ssh_request_loop(const char *zIpAddr, Glob *FileGlob){
2171 blob_zero(&g.cgiIn);
2172 do{
2173 cgi_handle_ssh_http_request(zIpAddr);
2174 process_one_web_page(0, FileGlob, 0);
2175 blob_reset(&g.cgiIn);
2176 } while ( g.fSshClient & CGI_SSH_FOSSIL ||
2177 g.fSshClient & CGI_SSH_COMPAT );
2178 }
2179
@@ -2140,21 +2190,21 @@
2190 Th_InitTraceLog();
2191 login_set_capabilities("sx", 0);
2192 g.useLocalauth = 1;
2193 g.httpIn = stdin;
2194 g.httpOut = stdout;
2195 find_server_repository(2);
2196 g.cgiOutput = 1;
2197 g.fullHttpReply = 1;
2198 zIpAddr = cgi_ssh_remote_addr(0);
2199 if( zIpAddr && zIpAddr[0] ){
2200 g.fSshClient |= CGI_SSH_CLIENT;
2201 ssh_request_loop(zIpAddr, 0);
2202 }else{
2203 cgi_set_parameter("REMOTE_ADDR", "127.0.0.1");
2204 cgi_handle_http_request(0);
2205 process_one_web_page(0, 0, 0);
2206 }
2207 }
2208
2209 #if !defined(_WIN32)
2210 #if !defined(__DARWIN__) && !defined(__APPLE__) && !defined(__HAIKU__)
@@ -2227,10 +2277,11 @@
2277 ** --localhost listen on 127.0.0.1 only (always true for "ui")
2278 ** --nojail Drop root privileges but do not enter the chroot jail
2279 ** --notfound URL Redirect
2280 ** -P|--port TCPPORT listen to request on port TCPPORT
2281 ** --th-trace trace TH1 execution (for debugging purposes)
2282 ** --repolist If REPOSITORY is dir, URL "/" lists repos.
2283 ** --scgi Accept SCGI rather than HTTP
2284 ** --skin LABEL Use override skin LABEL
2285
2286 **
2287 ** See also: cgi, http, winsrv
@@ -2244,10 +2295,11 @@
2295 const char *zNotFound; /* The --notfound option or NULL */
2296 int flags = 0; /* Server flags */
2297 #if !defined(_WIN32)
2298 int noJail; /* Do not enter the chroot jail */
2299 #endif
2300 int allowRepoList; /* List repositories on URL "/" */
2301 const char *zAltBase; /* Argument to the --baseurl option */
2302 const char *zFileGlob; /* Static content must match this */
2303 char *zIpAddr = 0; /* Bind to this IP address */
2304
2305 #if defined(_WIN32)
@@ -2269,10 +2321,11 @@
2321 #endif
2322 g.useLocalauth = find_option("localauth", 0, 0)!=0;
2323 Th_InitTraceLog();
2324 zPort = find_option("port", "P", 1);
2325 zNotFound = find_option("notfound", 0, 1);
2326 allowRepoList = find_option("repolist",0,0)!=0;
2327 zAltBase = find_option("baseurl", 0, 1);
2328 if( find_option("scgi", 0, 0)!=0 ) flags |= HTTP_SERVER_SCGI;
2329 if( zAltBase ){
2330 set_base_url(zAltBase);
2331 }
@@ -2286,12 +2339,13 @@
2339 if( g.argc!=2 && g.argc!=3 ) usage("?REPOSITORY?");
2340 isUiCmd = g.argv[1][0]=='u';
2341 if( isUiCmd ){
2342 flags |= HTTP_SERVER_LOCALHOST;
2343 g.useLocalauth = 1;
2344 allowRepoList = 1;
2345 }
2346 find_server_repository(2);
2347 if( zPort ){
2348 int i;
2349 for(i=strlen(zPort)-1; i>=0 && zPort[i]!=':'; i--){}
2350 if( i>0 ){
2351 zIpAddr = mprintf("%.*s", i, zPort);
@@ -2341,18 +2395,18 @@
2395 g.httpOut = stdout;
2396 if( g.fHttpTrace || g.fSqlTrace ){
2397 fprintf(stderr, "====== SERVER pid %d =======\n", getpid());
2398 }
2399 g.cgiOutput = 1;
2400 find_server_repository(2);
2401 g.zRepositoryName = enter_chroot_jail(g.zRepositoryName, noJail);
2402 if( flags & HTTP_SERVER_SCGI ){
2403 cgi_handle_scgi_request();
2404 }else{
2405 cgi_handle_http_request(0);
2406 }
2407 process_one_web_page(zNotFound, glob_create(zFileGlob), allowRepoList);
2408 #else
2409 /* Win32 implementation */
2410 if( isUiCmd ){
2411 zBrowser = db_get("web-browser", "start");
2412 if( zIpAddr ){
2413

Keyboard Shortcuts

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