| | @@ -139,10 +139,11 @@ |
| 139 | 139 | int minPrefix; /* Number of digits needed for a distinct UUID */ |
| 140 | 140 | int fSqlTrace; /* True if --sqltrace flag is present */ |
| 141 | 141 | int fSqlStats; /* True if --sqltrace or --sqlstats are present */ |
| 142 | 142 | int fSqlPrint; /* True if -sqlprint flag is present */ |
| 143 | 143 | int fQuiet; /* True if -quiet flag is present */ |
| 144 | + int fJail; /* True if running with a chroot jail */ |
| 144 | 145 | int fHttpTrace; /* Trace outbound HTTP requests */ |
| 145 | 146 | int fAnyTrace; /* Any kind of tracing */ |
| 146 | 147 | char *zHttpAuth; /* HTTP Authorization user:pass information */ |
| 147 | 148 | int fSystemTrace; /* Trace calls to fossil_system(), --systemtrace */ |
| 148 | 149 | int fSshTrace; /* Trace the SSH setup traffic */ |
| | @@ -1149,10 +1150,11 @@ |
| 1149 | 1150 | if( !noJail ){ |
| 1150 | 1151 | if( file_isdir(zDir)==1 ){ |
| 1151 | 1152 | if( file_chdir(zDir, 1) ){ |
| 1152 | 1153 | fossil_fatal("unable to chroot into %s", zDir); |
| 1153 | 1154 | } |
| 1155 | + g.fJail = 1; |
| 1154 | 1156 | zRepo = "/"; |
| 1155 | 1157 | }else{ |
| 1156 | 1158 | for(i=strlen(zDir)-1; i>0 && zDir[i]!='/'; i--){} |
| 1157 | 1159 | if( zDir[i]!='/' ) fossil_fatal("bad repository name: %s", zRepo); |
| 1158 | 1160 | if( i>0 ){ |
| | @@ -1182,48 +1184,88 @@ |
| 1182 | 1184 | } |
| 1183 | 1185 | |
| 1184 | 1186 | /* |
| 1185 | 1187 | ** Generate a web-page that lists all repositories located under the |
| 1186 | 1188 | ** g.zRepositoryName directory and return non-zero. |
| 1189 | +** |
| 1190 | +** For the special case when g.zRepositoryName a non-chroot-jail "/", |
| 1191 | +** compose the list using the "repo:" entries in the global_config |
| 1192 | +** table of the configuration database. These entries comprise all |
| 1193 | +** of the repositories known to the "all" command. The special case |
| 1194 | +** processing is disallowed for chroot jails because g.zRepositoryName |
| 1195 | +** is always "/" inside a chroot jail and so it cannot be used as a flag |
| 1196 | +** to signal the special processing in that case. The special case |
| 1197 | +** processing is intended for the "fossil all ui" command which never |
| 1198 | +** runs in a chroot jail anyhow. |
| 1187 | 1199 | ** |
| 1188 | 1200 | ** Or, if no repositories can be located beneath g.zRepositoryName, |
| 1189 | 1201 | ** return 0. |
| 1190 | 1202 | */ |
| 1191 | 1203 | static int repo_list_page(void){ |
| 1192 | 1204 | Blob base; |
| 1193 | 1205 | int n = 0; |
| 1194 | 1206 | |
| 1195 | 1207 | assert( g.db==0 ); |
| 1196 | | - blob_init(&base, g.zRepositoryName, -1); |
| 1197 | | - sqlite3_open(":memory:", &g.db); |
| 1198 | | - db_multi_exec("CREATE TABLE sfile(pathname TEXT);"); |
| 1199 | | - db_multi_exec("CREATE TABLE vfile(pathname);"); |
| 1200 | | - vfile_scan(&base, blob_size(&base), 0, 0, 0); |
| 1201 | | - db_multi_exec("DELETE FROM sfile WHERE pathname NOT GLOB '*[^/].fossil'"); |
| 1208 | + if( fossil_strcmp(g.zRepositoryName,"/")==0 && !g.fJail ){ |
| 1209 | + /* For the special case of the "repository directory" being "/", |
| 1210 | + ** show all of the repositories named in the ~/.fossil database. |
| 1211 | + ** |
| 1212 | + ** On unix systems, then entries are of the form "repo:/home/..." |
| 1213 | + ** and on Windows systems they are like "repo:C:/Users/...". We want |
| 1214 | + ** to skip the first 6 characters on unix and the first 5 characters |
| 1215 | + ** on Windows. |
| 1216 | + */ |
| 1217 | + db_open_config(1, 0); |
| 1218 | +#ifdef _WIN32 |
| 1219 | + db_multi_exec( |
| 1220 | + "CREATE TEMP VIEW sfile AS" |
| 1221 | + " SELECT substr(name,6) AS 'pathname' FROM global_config" |
| 1222 | + " WHERE name GLOB 'repo:*'" |
| 1223 | + ); |
| 1224 | +#else |
| 1225 | + db_multi_exec( |
| 1226 | + "CREATE TEMP VIEW sfile AS" |
| 1227 | + " SELECT substr(name,7) AS 'pathname' FROM global_config" |
| 1228 | + " WHERE name GLOB 'repo:*'" |
| 1229 | + ); |
| 1230 | +#endif |
| 1231 | + }else{ |
| 1232 | + /* The default case: All repositories under the g.zRepositoryName |
| 1233 | + ** directory. |
| 1234 | + */ |
| 1235 | + blob_init(&base, g.zRepositoryName, -1); |
| 1236 | + sqlite3_open(":memory:", &g.db); |
| 1237 | + db_multi_exec("CREATE TABLE sfile(pathname TEXT);"); |
| 1238 | + db_multi_exec("CREATE TABLE vfile(pathname);"); |
| 1239 | + vfile_scan(&base, blob_size(&base), 0, 0, 0); |
| 1240 | + db_multi_exec("DELETE FROM sfile WHERE pathname NOT GLOB '*[^/].fossil'"); |
| 1241 | + } |
| 1242 | + @ <html> |
| 1243 | + @ <head> |
| 1244 | + @ <base href="%s(g.zBaseURL)/" /> |
| 1245 | + @ <title>Repository List</title> |
| 1246 | + @ </head> |
| 1247 | + @ <body> |
| 1202 | 1248 | n = db_int(0, "SELECT count(*) FROM sfile"); |
| 1203 | 1249 | if( n>0 ){ |
| 1204 | 1250 | Stmt q; |
| 1205 | | - @ <html> |
| 1206 | | - @ <head> |
| 1207 | | - @ <base href="%s(g.zBaseURL)/" /> |
| 1208 | | - @ <title>Repository List</title> |
| 1209 | | - @ </head> |
| 1210 | | - @ <body> |
| 1211 | 1251 | @ <h1>Available Repositories:</h1> |
| 1212 | 1252 | @ <ol> |
| 1213 | 1253 | db_prepare(&q, "SELECT pathname, substr(pathname,-7,-100000)||'/home'" |
| 1214 | 1254 | " FROM sfile ORDER BY pathname COLLATE nocase;"); |
| 1215 | 1255 | while( db_step(&q)==SQLITE_ROW ){ |
| 1216 | 1256 | const char *zName = db_column_text(&q, 0); |
| 1217 | 1257 | const char *zUrl = db_column_text(&q, 1); |
| 1218 | | - @ <li><a href="%R/%h(zUrl)" target="_blank">%h(zName)</a></li> |
| 1258 | + @ <li><a href="%R/%T(zUrl)" target="_blank">%h(zName)</a></li> |
| 1219 | 1259 | } |
| 1220 | 1260 | @ </ol> |
| 1221 | | - @ </body> |
| 1222 | | - @ </html> |
| 1223 | | - cgi_reply(); |
| 1261 | + }else{ |
| 1262 | + @ <h1>No Repositories Found</h1> |
| 1224 | 1263 | } |
| 1264 | + @ </body> |
| 1265 | + @ </html> |
| 1266 | + cgi_reply(); |
| 1225 | 1267 | sqlite3_close(g.db); |
| 1226 | 1268 | g.db = 0; |
| 1227 | 1269 | return n; |
| 1228 | 1270 | } |
| 1229 | 1271 | |
| | @@ -1252,15 +1294,15 @@ |
| 1252 | 1294 | static void process_one_web_page( |
| 1253 | 1295 | const char *zNotFound, /* Redirect here on a 404 if not NULL */ |
| 1254 | 1296 | Glob *pFileGlob, /* Deliver static files matching */ |
| 1255 | 1297 | int allowRepoList /* Send repo list for "/" URL */ |
| 1256 | 1298 | ){ |
| 1257 | | - const char *zPathInfo; |
| 1258 | | - const char *zDirPathInfo; |
| 1299 | + const char *zPathInfo = PD("PATH_INFO", ""); |
| 1259 | 1300 | char *zPath = NULL; |
| 1260 | 1301 | int i; |
| 1261 | 1302 | const CmdOrPage *pCmd = 0; |
| 1303 | + const char *zBase = g.zRepositoryName; |
| 1262 | 1304 | |
| 1263 | 1305 | /* Handle universal query parameters */ |
| 1264 | 1306 | if( PB("utc") ){ |
| 1265 | 1307 | g.fTimeFormat = 1; |
| 1266 | 1308 | }else if( PB("localtime") ){ |
| | @@ -1268,90 +1310,138 @@ |
| 1268 | 1310 | } |
| 1269 | 1311 | |
| 1270 | 1312 | /* If the repository has not been opened already, then find the |
| 1271 | 1313 | ** repository based on the first element of PATH_INFO and open it. |
| 1272 | 1314 | */ |
| 1273 | | - zDirPathInfo = zPathInfo = PD("PATH_INFO",""); |
| 1274 | | - /* For the PATH_INFO that will be used to help build the final |
| 1275 | | - ** g.zBaseURL and g.zTop (only), skip over the initial directory |
| 1276 | | - ** portion of PATH_INFO; otherwise, it may be duplicated. |
| 1277 | | - */ |
| 1278 | | - if( g.zTop ){ |
| 1279 | | - int nTop = strlen(g.zTop); |
| 1280 | | - if ( strncmp(zDirPathInfo, g.zTop, nTop)==0 ){ |
| 1281 | | - zDirPathInfo += nTop; |
| 1282 | | - } |
| 1283 | | - } |
| 1284 | 1315 | if( !g.repositoryOpen ){ |
| 1285 | | - char *zRepo, *zToFree; |
| 1286 | | - const char *zOldScript = PD("SCRIPT_NAME", ""); |
| 1287 | | - char *zNewScript; |
| 1288 | | - int j, k; |
| 1289 | | - i64 szFile; |
| 1316 | + char *zRepo; /* Candidate repository name */ |
| 1317 | + char *zToFree = 0; /* Malloced memory that needs to be freed */ |
| 1318 | + const char *zCleanRepo; /* zRepo with surplus leading "/" removed */ |
| 1319 | + const char *zOldScript = PD("SCRIPT_NAME", ""); /* Original SCRIPT_NAME */ |
| 1320 | + char *zNewScript; /* Revised SCRIPT_NAME after processing */ |
| 1321 | + int j, k; /* Loop variables */ |
| 1322 | + i64 szFile; /* File size of the candidate repository */ |
| 1290 | 1323 | |
| 1291 | 1324 | i = zPathInfo[0]!=0; |
| 1325 | + if( fossil_strcmp(g.zRepositoryName, "/")==0 ){ |
| 1326 | + zBase++; |
| 1327 | +#ifdef _WIN32 |
| 1328 | + if( sqlite3_strglob("/[a-zA-Z]:/*", zPathInfo)==0 ) i = 4; |
| 1329 | +#endif |
| 1330 | + } |
| 1292 | 1331 | while( 1 ){ |
| 1293 | 1332 | while( zPathInfo[i] && zPathInfo[i]!='/' ){ i++; } |
| 1294 | | - zRepo = zToFree = mprintf("%s%.*s.fossil",g.zRepositoryName,i,zPathInfo); |
| 1333 | + |
| 1334 | + /* The candidate repository name is some prefix of the PATH_INFO |
| 1335 | + ** with ".fossil" appended */ |
| 1336 | + zRepo = zToFree = mprintf("%s%.*s.fossil",zBase,i,zPathInfo); |
| 1337 | + if( g.fHttpTrace ){ |
| 1338 | + @ <!-- Looking for repository named "%h(zRepo)" --> |
| 1339 | + fprintf(stderr, "# looking for repository named \"%s\"\n", zRepo); |
| 1340 | + } |
| 1341 | + |
| 1295 | 1342 | |
| 1296 | | - /* To avoid mischief, make sure the repository basename contains no |
| 1343 | + /* For safety -- to prevent an attacker from accessing arbitrary disk |
| 1344 | + ** files by sending a maliciously crafted request URI to a public |
| 1345 | + ** server -- make sure the repository basename contains no |
| 1297 | 1346 | ** characters other than alphanumerics, "/", "_", "-", and ".", and |
| 1298 | 1347 | ** that "-" never occurs immediately after a "/" and that "." is always |
| 1299 | 1348 | ** surrounded by two alphanumerics. Any character that does not |
| 1300 | 1349 | ** satisfy these constraints is converted into "_". |
| 1301 | 1350 | */ |
| 1302 | 1351 | szFile = 0; |
| 1303 | | - for(j=strlen(g.zRepositoryName)+1, k=0; zRepo[j] && k<i-1; j++, k++){ |
| 1352 | + for(j=strlen(zBase)+1, k=0; zRepo[j] && k<i-1; j++, k++){ |
| 1304 | 1353 | char c = zRepo[j]; |
| 1305 | 1354 | if( fossil_isalnum(c) ) continue; |
| 1355 | +#ifdef _WIN32 |
| 1356 | + /* Allow names to begin with "/X:/" on windows */ |
| 1357 | + if( c==':' && j==2 && sqlite3_strglob("/[a-zA-Z]:/*", zRepo)==0 ){ |
| 1358 | + continue; |
| 1359 | + } |
| 1360 | +#endif |
| 1306 | 1361 | if( c=='/' ) continue; |
| 1307 | 1362 | if( c=='_' ) continue; |
| 1308 | 1363 | if( c=='-' && zRepo[j-1]!='/' ) continue; |
| 1309 | 1364 | if( c=='.' && fossil_isalnum(zRepo[j-1]) && fossil_isalnum(zRepo[j+1])){ |
| 1310 | 1365 | continue; |
| 1311 | 1366 | } |
| 1367 | + /* If we reach this point, it means that the request URI contains |
| 1368 | + ** an illegal character or character combination. Provoke a |
| 1369 | + ** "Not Found" error. */ |
| 1312 | 1370 | szFile = 1; |
| 1371 | + if( g.fHttpTrace ){ |
| 1372 | + @ <!-- Unsafe pathname rejected: "%h(zRepo)" --> |
| 1373 | + fprintf(stderr, "# unsafe pathname rejected: %s\n", zRepo); |
| 1374 | + } |
| 1313 | 1375 | break; |
| 1314 | 1376 | } |
| 1377 | + |
| 1378 | + /* Check to see if a file name zRepo exists. If a file named zRepo |
| 1379 | + ** does not exist, szFile will become -1. If the file does exist, |
| 1380 | + ** then szFile will become zero (for an empty file) or positive. |
| 1381 | + ** Special case: Assume any file with a basename of ".fossil" does |
| 1382 | + ** not exist. |
| 1383 | + */ |
| 1384 | + zCleanRepo = file_cleanup_fullpath(zRepo); |
| 1315 | 1385 | if( szFile==0 && sqlite3_strglob("*/.fossil",zRepo)!=0 ){ |
| 1316 | | - if( zRepo[0]=='/' && zRepo[1]=='/' ){ zRepo++; j--; } |
| 1317 | | - szFile = file_size(zRepo); |
| 1318 | | - /* this should only be set from the --baseurl option, not CGI */ |
| 1319 | | - if( g.zBaseURL && g.zBaseURL[0]!=0 && g.zTop && g.zTop[0]!=0 && |
| 1320 | | - file_isdir(g.zRepositoryName)==1 ){ |
| 1321 | | - if( zPathInfo==zDirPathInfo ){ |
| 1322 | | - g.zBaseURL = mprintf("%s%.*s", g.zBaseURL, i, zPathInfo); |
| 1323 | | - g.zTop = mprintf("%s%.*s", g.zTop, i, zPathInfo); |
| 1324 | | - } |
| 1386 | + szFile = file_size(zCleanRepo); |
| 1387 | + if( g.fHttpTrace ){ |
| 1388 | + @ <!-- file_size(%h(zCleanRepo)) is %lld(szFile) --> |
| 1389 | + fprintf(stderr, "# file_size(%s) = %lld\n", zCleanRepo, szFile); |
| 1325 | 1390 | } |
| 1326 | 1391 | } |
| 1392 | + |
| 1393 | + /* If no file named by zRepo exists, remove the added ".fossil" suffix |
| 1394 | + ** and check to see if there is a file or directory with the same |
| 1395 | + ** name as the raw PATH_INFO text. |
| 1396 | + */ |
| 1327 | 1397 | if( szFile<0 && i>0 ){ |
| 1328 | 1398 | const char *zMimetype; |
| 1329 | | - assert( fossil_strcmp(&zRepo[j], ".fossil")==0 ); |
| 1330 | | - zRepo[j] = 0; |
| 1331 | | - if( zPathInfo[i]=='/' && file_isdir(zRepo)==1 ){ |
| 1399 | + assert( fossil_strcmp(&zRepo[j], ".fossil")==0 ); |
| 1400 | + zRepo[j] = 0; /* Remove the ".fossil" suffix */ |
| 1401 | + |
| 1402 | + /* The PATH_INFO prefix seen so far is a valid directory. |
| 1403 | + ** Continue the loop with the next element of the PATH_INFO */ |
| 1404 | + if( zPathInfo[i]=='/' && file_isdir(zCleanRepo)==1 ){ |
| 1332 | 1405 | fossil_free(zToFree); |
| 1333 | 1406 | i++; |
| 1334 | 1407 | continue; |
| 1335 | 1408 | } |
| 1409 | + |
| 1410 | + /* If zRepo is the name of an ordinary file that matches the |
| 1411 | + ** "--file GLOB" pattern, then the CGI reply is the text of |
| 1412 | + ** of the file. |
| 1413 | + ** |
| 1414 | + ** For safety, do not allow any file whose name contains ".fossil" |
| 1415 | + ** to be returned this way, to prevent complete repositories from |
| 1416 | + ** being delivered accidently. This is not intended to be a |
| 1417 | + ** general-purpose web server. The "--file GLOB" mechanism is |
| 1418 | + ** designed to allow the delivery of a few static images or HTML |
| 1419 | + ** pages. |
| 1420 | + */ |
| 1336 | 1421 | if( pFileGlob!=0 |
| 1337 | | - && file_isfile(zRepo) |
| 1338 | | - && glob_match(pFileGlob, zRepo) |
| 1422 | + && file_isfile(zCleanRepo) |
| 1423 | + && glob_match(pFileGlob, file_cleanup_fullpath(zRepo)) |
| 1339 | 1424 | && sqlite3_strglob("*.fossil*",zRepo)!=0 |
| 1340 | 1425 | && (zMimetype = mimetype_from_name(zRepo))!=0 |
| 1341 | 1426 | && strcmp(zMimetype, "application/x-fossil-artifact")!=0 |
| 1342 | 1427 | ){ |
| 1343 | 1428 | Blob content; |
| 1344 | | - blob_read_from_file(&content, zRepo); |
| 1429 | + blob_read_from_file(&content, file_cleanup_fullpath(zRepo)); |
| 1345 | 1430 | cgi_set_content_type(zMimetype); |
| 1346 | 1431 | cgi_set_content(&content); |
| 1347 | 1432 | cgi_reply(); |
| 1348 | 1433 | return; |
| 1349 | 1434 | } |
| 1350 | 1435 | zRepo[j] = '.'; |
| 1351 | 1436 | } |
| 1352 | 1437 | |
| 1438 | + /* If we reach this point, it means that the search of the PATH_INFO |
| 1439 | + ** string is finished. Either zRepo contains the name of the |
| 1440 | + ** repository to be used, or else no repository could be found an |
| 1441 | + ** some kind of error response is required. |
| 1442 | + */ |
| 1353 | 1443 | if( szFile<1024 ){ |
| 1354 | 1444 | set_base_url(0); |
| 1355 | 1445 | if( strcmp(zPathInfo,"/")==0 |
| 1356 | 1446 | && allowRepoList |
| 1357 | 1447 | && repo_list_page() ){ |
| | @@ -1371,34 +1461,59 @@ |
| 1371 | 1461 | } |
| 1372 | 1462 | return; |
| 1373 | 1463 | } |
| 1374 | 1464 | break; |
| 1375 | 1465 | } |
| 1466 | + |
| 1467 | + /* Add the repository name (without the ".fossil" suffix) to the end |
| 1468 | + ** of SCRIPT_NAME and g.zTop and g.zBaseURL and remove the repository |
| 1469 | + ** name from the beginning of PATH_INFO. |
| 1470 | + */ |
| 1376 | 1471 | zNewScript = mprintf("%s%.*s", zOldScript, i, zPathInfo); |
| 1472 | + if( g.zTop ) g.zTop = mprintf("%s%.*s", g.zTop, i, zPathInfo); |
| 1473 | + if( g.zBaseURL ) g.zBaseURL = mprintf("%s%.*s", g.zBaseURL, i, zPathInfo); |
| 1377 | 1474 | cgi_replace_parameter("PATH_INFO", &zPathInfo[i+1]); |
| 1378 | 1475 | zPathInfo += i; |
| 1379 | 1476 | cgi_replace_parameter("SCRIPT_NAME", zNewScript); |
| 1380 | | - db_open_repository(zRepo); |
| 1477 | + db_open_repository(file_cleanup_fullpath(zRepo)); |
| 1381 | 1478 | if( g.fHttpTrace ){ |
| 1479 | + @ <!-- repository: "%h(zRepo)" --> |
| 1480 | + @ <!-- translated PATH_INFO: "%h(zPathInfo)" --> |
| 1481 | + @ <!-- translated SCRIPT_NAME: "%h(zNewScript)" --> |
| 1382 | 1482 | fprintf(stderr, |
| 1383 | 1483 | "# repository: [%s]\n" |
| 1384 | | - "# new PATH_INFO = [%s]\n" |
| 1385 | | - "# new SCRIPT_NAME = [%s]\n", |
| 1484 | + "# translated PATH_INFO = [%s]\n" |
| 1485 | + "# translated SCRIPT_NAME = [%s]\n", |
| 1386 | 1486 | zRepo, zPathInfo, zNewScript); |
| 1487 | + if( g.zTop ){ |
| 1488 | + @ <!-- translated g.zTop: "%h(g.zTop)" --> |
| 1489 | + fprintf(stderr, "# translated g.zTop = [%s]\n", g.zTop); |
| 1490 | + } |
| 1491 | + if( g.zBaseURL ){ |
| 1492 | + @ <!-- translated g.zBaseURL: "%h(g.zBaseURL)" --> |
| 1493 | + fprintf(stderr, "# translated g.zBaseURL = [%s]\n", g.zBaseURL); |
| 1494 | + } |
| 1387 | 1495 | } |
| 1388 | 1496 | } |
| 1389 | 1497 | |
| 1390 | | - /* Find the page that the user has requested, construct and deliver that |
| 1391 | | - ** page. |
| 1498 | + /* At this point, the appropriate repository database file will have |
| 1499 | + ** been opened. Use the first element of PATH_INFO as the page name |
| 1500 | + ** and deliver the appropriate page back to the user. |
| 1392 | 1501 | */ |
| 1393 | 1502 | if( g.zContentType && |
| 1394 | 1503 | strncmp(g.zContentType, "application/x-fossil", 20)==0 ){ |
| 1504 | + /* Special case: If the content mimetype shows that it is "fossil sync" |
| 1505 | + ** payload, then pretend that the PATH_INFO is /xfer so that we always |
| 1506 | + ** invoke the sync page. */ |
| 1395 | 1507 | zPathInfo = "/xfer"; |
| 1396 | 1508 | } |
| 1397 | 1509 | set_base_url(0); |
| 1398 | 1510 | if( zPathInfo==0 || zPathInfo[0]==0 |
| 1399 | 1511 | || (zPathInfo[0]=='/' && zPathInfo[1]==0) ){ |
| 1512 | + /* Second special case: If the PATH_INFO is blank, issue a redirect to |
| 1513 | + ** the home page identified by the "index-page" setting in the repository |
| 1514 | + ** CONFIG table, to "/index" if there no "index-page" setting. */ |
| 1400 | 1515 | #ifdef FOSSIL_ENABLE_JSON |
| 1401 | 1516 | if(g.json.isJsonMode){ |
| 1402 | 1517 | json_err(FSL_JSON_E_RESOURCE_NOT_FOUND,NULL,1); |
| 1403 | 1518 | fossil_exit(0); |
| 1404 | 1519 | } |
| | @@ -1415,56 +1530,10 @@ |
| 1415 | 1530 | g.zPath = &zPath[1]; |
| 1416 | 1531 | for(i=1; zPath[i] && zPath[i]!='/'; i++){} |
| 1417 | 1532 | if( zPath[i]=='/' ){ |
| 1418 | 1533 | zPath[i] = 0; |
| 1419 | 1534 | g.zExtra = &zPath[i+1]; |
| 1420 | | - |
| 1421 | | -#ifdef FOSSIL_ENABLE_SUBREPOSITORY |
| 1422 | | - char *zAltRepo = 0; |
| 1423 | | - /* 2016-09-21: Subrepos are undocumented and apparently no longer work. |
| 1424 | | - ** So they are now removed unless the -DFOSSIL_ENABLE_SUBREPOSITORY |
| 1425 | | - ** compile-time option is used. If there are no complaints after |
| 1426 | | - ** a while, we can delete the code entirely. |
| 1427 | | - */ |
| 1428 | | - /* Look for sub-repositories. A sub-repository is another repository |
| 1429 | | - ** that accepts the login credentials of the current repository. A |
| 1430 | | - ** subrepository is identified by a CONFIG table entry "subrepo:NAME" |
| 1431 | | - ** where NAME is the first component of the path. The value of the |
| 1432 | | - ** the CONFIG entries is the string "USER:FILENAME" where USER is the |
| 1433 | | - ** USER name to log in as in the subrepository and FILENAME is the |
| 1434 | | - ** repository filename. |
| 1435 | | - */ |
| 1436 | | - zAltRepo = db_text(0, "SELECT value FROM config WHERE name='subrepo:%q'", |
| 1437 | | - g.zPath); |
| 1438 | | - if( zAltRepo ){ |
| 1439 | | - int nHost; |
| 1440 | | - int jj; |
| 1441 | | - char *zUser = zAltRepo; |
| 1442 | | - login_check_credentials(); |
| 1443 | | - for(jj=0; zAltRepo[jj] && zAltRepo[jj]!=':'; jj++){} |
| 1444 | | - if( zAltRepo[jj]==':' ){ |
| 1445 | | - zAltRepo[jj] = 0; |
| 1446 | | - zAltRepo += jj+1; |
| 1447 | | - }else{ |
| 1448 | | - zUser = "nobody"; |
| 1449 | | - } |
| 1450 | | - if( g.zLogin==0 || g.zLogin[0]==0 ) zUser = "nobody"; |
| 1451 | | - if( zAltRepo[0]!='/' ){ |
| 1452 | | - zAltRepo = mprintf("%s/../%s", g.zRepositoryName, zAltRepo); |
| 1453 | | - file_simplify_name(zAltRepo, -1, 0); |
| 1454 | | - } |
| 1455 | | - db_close(1); |
| 1456 | | - db_open_repository(zAltRepo); |
| 1457 | | - login_as_user(zUser); |
| 1458 | | - g.perm.Password = 0; |
| 1459 | | - zPath += i; |
| 1460 | | - nHost = g.zTop - g.zBaseURL; |
| 1461 | | - g.zBaseURL = mprintf("%z/%s", g.zBaseURL, g.zPath); |
| 1462 | | - g.zTop = g.zBaseURL + nHost; |
| 1463 | | - continue; |
| 1464 | | - } |
| 1465 | | -#endif /* FOSSIL_ENABLE_SUBREPOSITORY */ |
| 1466 | 1535 | }else{ |
| 1467 | 1536 | g.zExtra = 0; |
| 1468 | 1537 | } |
| 1469 | 1538 | break; |
| 1470 | 1539 | } |
| | @@ -2203,10 +2272,15 @@ |
| 2203 | 2272 | ** list of glob patterns given by --files and that have known suffixes |
| 2204 | 2273 | ** such as ".txt" or ".html" or ".jpeg" and do not match the pattern |
| 2205 | 2274 | ** "*.fossil*" will be served as static content. With the "ui" command, |
| 2206 | 2275 | ** the REPOSITORY can only be a directory if the --notfound option is |
| 2207 | 2276 | ** also present. |
| 2277 | +** |
| 2278 | +** For the special case REPOSITORY name of "/", the list global configuration |
| 2279 | +** database is consulted for a list of all known repositories. The --repolist |
| 2280 | +** option is implied by this special case. See also the "fossil all ui" |
| 2281 | +** command. |
| 2208 | 2282 | ** |
| 2209 | 2283 | ** By default, the "ui" command provides full administrative access without |
| 2210 | 2284 | ** having to log in. This can be disabled by turning off the "localauth" |
| 2211 | 2285 | ** setting. Automatic login for the "server" command is available if the |
| 2212 | 2286 | ** --localauth option is present and the "localauth" setting is off and the |
| | @@ -2377,11 +2451,15 @@ |
| 2377 | 2451 | if( g.fHttpTrace || g.fSqlTrace ){ |
| 2378 | 2452 | fprintf(stderr, "====== SERVER pid %d =======\n", getpid()); |
| 2379 | 2453 | } |
| 2380 | 2454 | g.cgiOutput = 1; |
| 2381 | 2455 | find_server_repository(2, 0); |
| 2382 | | - g.zRepositoryName = enter_chroot_jail(g.zRepositoryName, noJail); |
| 2456 | + if( fossil_strcmp(g.zRepositoryName,"/")==0 ){ |
| 2457 | + allowRepoList = 1; |
| 2458 | + }else{ |
| 2459 | + g.zRepositoryName = enter_chroot_jail(g.zRepositoryName, noJail); |
| 2460 | + } |
| 2383 | 2461 | if( flags & HTTP_SERVER_SCGI ){ |
| 2384 | 2462 | cgi_handle_scgi_request(); |
| 2385 | 2463 | }else{ |
| 2386 | 2464 | cgi_handle_http_request(0); |
| 2387 | 2465 | } |
| 2388 | 2466 | |