Fossil SCM
Fix the "fossil all ui" command so that it works on Windows. But also comment out some very confused logic in process_on_web_page() that is associated with "--baseurl" option. This logic needs to be fixed prior to merging with trunk.
Commit
da1c769cd236de86ccbc93cf5b5a8bd46ff043cc
Parent
98e9fd735211c57…
2 files changed
+15
+131
-74
+15
| --- src/file.c | ||
| +++ src/file.c | ||
| @@ -1435,5 +1435,20 @@ | ||
| 1435 | 1435 | int i; |
| 1436 | 1436 | for(i=2; i<g.argc; i++){ |
| 1437 | 1437 | fossil_print("%s %s\n", file_is_win_reserved(g.argv[i]), g.argv[i]); |
| 1438 | 1438 | } |
| 1439 | 1439 | } |
| 1440 | + | |
| 1441 | +/* | |
| 1442 | +** Remove surplus "/" characters from the beginning of a full pathname. | |
| 1443 | +** Extra leading "/" characters are benign on unix. But on Windows | |
| 1444 | +** machines, they must be removed. Example: Convert "/C:/fossil/xyx.fossil" | |
| 1445 | +** into "c:/fossil/xyz.fossil". | |
| 1446 | +*/ | |
| 1447 | +const char *file_cleanup_fullpath(const char *z){ | |
| 1448 | +#ifdef _WIN32 | |
| 1449 | + if( z[0]=='/' && fossil_isalpha(z[1]) && z[2]==':' && z[3]=='/' ) z++; | |
| 1450 | +#else | |
| 1451 | + while( z[0]=='/' && z[1]=='/' ) z++; | |
| 1452 | +#endif | |
| 1453 | + return z; | |
| 1454 | +} | |
| 1440 | 1455 |
| --- src/file.c | |
| +++ src/file.c | |
| @@ -1435,5 +1435,20 @@ | |
| 1435 | int i; |
| 1436 | for(i=2; i<g.argc; i++){ |
| 1437 | fossil_print("%s %s\n", file_is_win_reserved(g.argv[i]), g.argv[i]); |
| 1438 | } |
| 1439 | } |
| 1440 |
| --- src/file.c | |
| +++ src/file.c | |
| @@ -1435,5 +1435,20 @@ | |
| 1435 | int i; |
| 1436 | for(i=2; i<g.argc; i++){ |
| 1437 | fossil_print("%s %s\n", file_is_win_reserved(g.argv[i]), g.argv[i]); |
| 1438 | } |
| 1439 | } |
| 1440 | |
| 1441 | /* |
| 1442 | ** Remove surplus "/" characters from the beginning of a full pathname. |
| 1443 | ** Extra leading "/" characters are benign on unix. But on Windows |
| 1444 | ** machines, they must be removed. Example: Convert "/C:/fossil/xyx.fossil" |
| 1445 | ** into "c:/fossil/xyz.fossil". |
| 1446 | */ |
| 1447 | const char *file_cleanup_fullpath(const char *z){ |
| 1448 | #ifdef _WIN32 |
| 1449 | if( z[0]=='/' && fossil_isalpha(z[1]) && z[2]==':' && z[3]=='/' ) z++; |
| 1450 | #else |
| 1451 | while( z[0]=='/' && z[1]=='/' ) z++; |
| 1452 | #endif |
| 1453 | return z; |
| 1454 | } |
| 1455 |
+131
-74
| --- src/main.c | ||
| +++ src/main.c | ||
| @@ -1199,17 +1199,30 @@ | ||
| 1199 | 1199 | |
| 1200 | 1200 | assert( g.db==0 ); |
| 1201 | 1201 | if( fossil_strcmp(g.zRepositoryName,"/")==0 ){ |
| 1202 | 1202 | /* For the special case of the "repository directory" being "/", |
| 1203 | 1203 | ** show all of the repositories named in the ~/.fossil database. |
| 1204 | + ** | |
| 1205 | + ** On unix systems, then entries are of the form "repo:/home/..." | |
| 1206 | + ** and on Windows systems they are like "repo:C:/Users/...". We want | |
| 1207 | + ** to skip the first 6 characters on unix and the first 5 characters | |
| 1208 | + ** on Windows. | |
| 1204 | 1209 | */ |
| 1205 | 1210 | db_open_config(1, 0); |
| 1211 | +#ifdef _WIN32 | |
| 1212 | + db_multi_exec( | |
| 1213 | + "CREATE TEMP VIEW sfile AS" | |
| 1214 | + " SELECT substr(name,6) AS 'pathname' FROM global_config" | |
| 1215 | + " WHERE name GLOB 'repo:*'" | |
| 1216 | + ); | |
| 1217 | +#else | |
| 1206 | 1218 | db_multi_exec( |
| 1207 | 1219 | "CREATE TEMP VIEW sfile AS" |
| 1208 | 1220 | " SELECT substr(name,7) AS 'pathname' FROM global_config" |
| 1209 | 1221 | " WHERE name GLOB 'repo:*'" |
| 1210 | 1222 | ); |
| 1223 | +#endif | |
| 1211 | 1224 | }else{ |
| 1212 | 1225 | /* The default case: All repositories under the g.zRepositoryName |
| 1213 | 1226 | ** directory. |
| 1214 | 1227 | */ |
| 1215 | 1228 | blob_init(&base, g.zRepositoryName, -1); |
| @@ -1233,11 +1246,11 @@ | ||
| 1233 | 1246 | db_prepare(&q, "SELECT pathname, substr(pathname,-7,-100000)||'/home'" |
| 1234 | 1247 | " FROM sfile ORDER BY pathname COLLATE nocase;"); |
| 1235 | 1248 | while( db_step(&q)==SQLITE_ROW ){ |
| 1236 | 1249 | const char *zName = db_column_text(&q, 0); |
| 1237 | 1250 | const char *zUrl = db_column_text(&q, 1); |
| 1238 | - @ <li><a href="%R/%h(zUrl)" target="_blank">%h(zName)</a></li> | |
| 1251 | + @ <li><a href="%R/%T(zUrl)" target="_blank">%h(zName)</a></li> | |
| 1239 | 1252 | } |
| 1240 | 1253 | @ </ol> |
| 1241 | 1254 | }else{ |
| 1242 | 1255 | @ <h1>No Repositories Found</h1> |
| 1243 | 1256 | } |
| @@ -1274,27 +1287,25 @@ | ||
| 1274 | 1287 | static void process_one_web_page( |
| 1275 | 1288 | const char *zNotFound, /* Redirect here on a 404 if not NULL */ |
| 1276 | 1289 | Glob *pFileGlob, /* Deliver static files matching */ |
| 1277 | 1290 | int allowRepoList /* Send repo list for "/" URL */ |
| 1278 | 1291 | ){ |
| 1279 | - const char *zPathInfo; | |
| 1280 | - const char *zDirPathInfo; | |
| 1292 | + const char *zPathInfo = PD("PATH_INFO", ""); | |
| 1293 | + /* const char *zDirPathInfo; **** refactor needed */ | |
| 1281 | 1294 | char *zPath = NULL; |
| 1282 | 1295 | int i; |
| 1283 | 1296 | const CmdOrPage *pCmd = 0; |
| 1297 | + const char *zBase = g.zRepositoryName; | |
| 1284 | 1298 | |
| 1285 | 1299 | /* Handle universal query parameters */ |
| 1286 | 1300 | if( PB("utc") ){ |
| 1287 | 1301 | g.fTimeFormat = 1; |
| 1288 | 1302 | }else if( PB("localtime") ){ |
| 1289 | 1303 | g.fTimeFormat = 2; |
| 1290 | 1304 | } |
| 1291 | 1305 | |
| 1292 | - /* If the repository has not been opened already, then find the | |
| 1293 | - ** repository based on the first element of PATH_INFO and open it. | |
| 1294 | - */ | |
| 1295 | - zDirPathInfo = zPathInfo = PD("PATH_INFO",""); | |
| 1306 | +#if 0 /* Refactor needed */ | |
| 1296 | 1307 | /* For the PATH_INFO that will be used to help build the final |
| 1297 | 1308 | ** g.zBaseURL and g.zTop (only), skip over the initial directory |
| 1298 | 1309 | ** portion of PATH_INFO; otherwise, it may be duplicated. |
| 1299 | 1310 | */ |
| 1300 | 1311 | if( g.zTop ){ |
| @@ -1301,79 +1312,155 @@ | ||
| 1301 | 1312 | int nTop = strlen(g.zTop); |
| 1302 | 1313 | if ( strncmp(zDirPathInfo, g.zTop, nTop)==0 ){ |
| 1303 | 1314 | zDirPathInfo += nTop; |
| 1304 | 1315 | } |
| 1305 | 1316 | } |
| 1317 | +#endif | |
| 1318 | + | |
| 1319 | + /* If the repository has not been opened already, then find the | |
| 1320 | + ** repository based on the first element of PATH_INFO and open it. | |
| 1321 | + */ | |
| 1306 | 1322 | if( !g.repositoryOpen ){ |
| 1307 | - char *zRepo, *zToFree; | |
| 1308 | - const char *zOldScript = PD("SCRIPT_NAME", ""); | |
| 1309 | - char *zNewScript; | |
| 1310 | - int j, k; | |
| 1311 | - i64 szFile; | |
| 1323 | + char *zRepo; /* Candidate repository name */ | |
| 1324 | + char *zToFree = 0; /* Malloced memory that needs to be freed */ | |
| 1325 | + const char *zCleanRepo; /* zRepo with surplus leading "/" removed */ | |
| 1326 | + const char *zOldScript = PD("SCRIPT_NAME", ""); /* Original SCRIPT_NAME */ | |
| 1327 | + char *zNewScript; /* Revised SCRIPT_NAME after processing */ | |
| 1328 | + int j, k; /* Loop variables */ | |
| 1329 | + i64 szFile; /* File size of the candidate repository */ | |
| 1312 | 1330 | |
| 1313 | 1331 | i = zPathInfo[0]!=0; |
| 1332 | + if( fossil_strcmp(g.zRepositoryName, "/")==0 ){ | |
| 1333 | + zBase++; | |
| 1334 | +#ifdef _WIN32 | |
| 1335 | + if( sqlite3_strglob("/[a-zA-Z]:/*", zPathInfo)==0 ) i = 4; | |
| 1336 | +#endif | |
| 1337 | + } | |
| 1314 | 1338 | while( 1 ){ |
| 1315 | 1339 | while( zPathInfo[i] && zPathInfo[i]!='/' ){ i++; } |
| 1316 | - zRepo = zToFree = mprintf("%s%.*s.fossil",g.zRepositoryName,i,zPathInfo); | |
| 1340 | + | |
| 1341 | + /* The candidate repository name is some prefix of the PATH_INFO | |
| 1342 | + ** with ".fossil" appended */ | |
| 1343 | + zRepo = zToFree = mprintf("%s%.*s.fossil",zBase,i,zPathInfo); | |
| 1344 | + if( g.fHttpTrace ){ | |
| 1345 | + @ <!-- Looking for repository named "%h(zRepo)" --> | |
| 1346 | + fprintf(stderr, "# looking for repository named \"%s\"\n", zRepo); | |
| 1347 | + } | |
| 1348 | + | |
| 1317 | 1349 | |
| 1318 | - /* To avoid mischief, make sure the repository basename contains no | |
| 1350 | + /* For safety -- to prevent an attacker from accessing arbitrary disk | |
| 1351 | + ** files by sending a maliciously crafted request URI to a public | |
| 1352 | + ** server -- make sure the repository basename contains no | |
| 1319 | 1353 | ** characters other than alphanumerics, "/", "_", "-", and ".", and |
| 1320 | 1354 | ** that "-" never occurs immediately after a "/" and that "." is always |
| 1321 | 1355 | ** surrounded by two alphanumerics. Any character that does not |
| 1322 | 1356 | ** satisfy these constraints is converted into "_". |
| 1323 | 1357 | */ |
| 1324 | 1358 | szFile = 0; |
| 1325 | - for(j=strlen(g.zRepositoryName)+1, k=0; zRepo[j] && k<i-1; j++, k++){ | |
| 1359 | + for(j=strlen(zBase)+1, k=0; zRepo[j] && k<i-1; j++, k++){ | |
| 1326 | 1360 | char c = zRepo[j]; |
| 1327 | 1361 | if( fossil_isalnum(c) ) continue; |
| 1362 | +#ifdef _WIN32 | |
| 1363 | + /* Allow names to begin with "/X:/" on windows */ | |
| 1364 | + if( c==':' && j==2 && sqlite3_strglob("/[a-zA-Z]:/*", zRepo)==0 ){ | |
| 1365 | + continue; | |
| 1366 | + } | |
| 1367 | +#endif | |
| 1328 | 1368 | if( c=='/' ) continue; |
| 1329 | 1369 | if( c=='_' ) continue; |
| 1330 | 1370 | if( c=='-' && zRepo[j-1]!='/' ) continue; |
| 1331 | 1371 | if( c=='.' && fossil_isalnum(zRepo[j-1]) && fossil_isalnum(zRepo[j+1])){ |
| 1332 | 1372 | continue; |
| 1333 | 1373 | } |
| 1374 | + /* If we reach this point, it means that the request URI contains | |
| 1375 | + ** an illegal character or character combination. Provoke a | |
| 1376 | + ** "Not Found" error. */ | |
| 1334 | 1377 | szFile = 1; |
| 1378 | + if( g.fHttpTrace ){ | |
| 1379 | + @ <!-- Unsafe pathname rejected: "%h(zRepo)" --> | |
| 1380 | + fprintf(stderr, "# unsafe pathname rejected: %s\n", zRepo); | |
| 1381 | + } | |
| 1335 | 1382 | break; |
| 1336 | 1383 | } |
| 1384 | + | |
| 1385 | + /* Check to see if a file name zRepo exists. If a file named zRepo | |
| 1386 | + ** does not exist, szFile will become -1. If the file does exist, | |
| 1387 | + ** then szFile will become zero (for an empty file) or positive. | |
| 1388 | + ** Special case: Assume any file with a basename of ".fossil" does | |
| 1389 | + ** not exist. | |
| 1390 | + */ | |
| 1391 | + zCleanRepo = file_cleanup_fullpath(zRepo); | |
| 1337 | 1392 | if( szFile==0 && sqlite3_strglob("*/.fossil",zRepo)!=0 ){ |
| 1338 | - if( zRepo[0]=='/' && zRepo[1]=='/' ){ zRepo++; j--; } | |
| 1339 | - szFile = file_size(zRepo); | |
| 1340 | - /* this should only be set from the --baseurl option, not CGI */ | |
| 1393 | + szFile = file_size(zCleanRepo); | |
| 1394 | + if( g.fHttpTrace ){ | |
| 1395 | + @ <!-- file_size(%h(zCleanRepo)) is %lld(szFile) --> | |
| 1396 | + fprintf(stderr, "# file_size(%s) = %lld\n", zCleanRepo, szFile); | |
| 1397 | + } | |
| 1398 | + | |
| 1399 | +#if 0 | |
| 1400 | + /* This logic for handling --baseurl is confused and needs to be | |
| 1401 | + ** completely rethought. */ | |
| 1341 | 1402 | if( g.zBaseURL && g.zBaseURL[0]!=0 && g.zTop && g.zTop[0]!=0 && |
| 1342 | 1403 | file_isdir(g.zRepositoryName)==1 ){ |
| 1343 | 1404 | if( zPathInfo==zDirPathInfo ){ |
| 1344 | 1405 | g.zBaseURL = mprintf("%s%.*s", g.zBaseURL, i, zPathInfo); |
| 1345 | 1406 | g.zTop = mprintf("%s%.*s", g.zTop, i, zPathInfo); |
| 1346 | 1407 | } |
| 1347 | 1408 | } |
| 1409 | +#endif | |
| 1348 | 1410 | } |
| 1411 | + | |
| 1412 | + /* If no file named by zRepo exists, remove the added ".fossil" suffix | |
| 1413 | + ** and check to see if there is a file or directory with the same | |
| 1414 | + ** name as the raw PATH_INFO text. | |
| 1415 | + */ | |
| 1349 | 1416 | if( szFile<0 && i>0 ){ |
| 1350 | 1417 | const char *zMimetype; |
| 1351 | - assert( fossil_strcmp(&zRepo[j], ".fossil")==0 ); | |
| 1352 | - zRepo[j] = 0; | |
| 1353 | - if( zPathInfo[i]=='/' && file_isdir(zRepo)==1 ){ | |
| 1418 | + assert( fossil_strcmp(&zRepo[j], ".fossil")==0 ); | |
| 1419 | + zRepo[j] = 0; /* Remove the ".fossil" suffix */ | |
| 1420 | + | |
| 1421 | + /* The PATH_INFO prefix seen so far is a valid directory. | |
| 1422 | + ** Continue the loop with the next element of the PATH_INFO */ | |
| 1423 | + if( zPathInfo[i]=='/' && file_isdir(zCleanRepo)==1 ){ | |
| 1354 | 1424 | fossil_free(zToFree); |
| 1355 | 1425 | i++; |
| 1356 | 1426 | continue; |
| 1357 | 1427 | } |
| 1428 | + | |
| 1429 | + /* If zRepo is the name of an ordinary file that matches the | |
| 1430 | + ** "--file GLOB" pattern, then the CGI reply is the text of | |
| 1431 | + ** of the file. | |
| 1432 | + ** | |
| 1433 | + ** For safety, do not allow any file whose name contains ".fossil" | |
| 1434 | + ** to be returned this way, to prevent complete repositories from | |
| 1435 | + ** being delivered accidently. This is not intended to be a | |
| 1436 | + ** general-purpose web server. The "--file GLOB" mechanism is | |
| 1437 | + ** designed to allow the delivery of a few static images or HTML | |
| 1438 | + ** pages. | |
| 1439 | + */ | |
| 1358 | 1440 | if( pFileGlob!=0 |
| 1359 | - && file_isfile(zRepo) | |
| 1360 | - && glob_match(pFileGlob, zRepo) | |
| 1441 | + && file_isfile(zCleanRepo) | |
| 1442 | + && glob_match(pFileGlob, file_cleanup_fullpath(zRepo)) | |
| 1361 | 1443 | && sqlite3_strglob("*.fossil*",zRepo)!=0 |
| 1362 | 1444 | && (zMimetype = mimetype_from_name(zRepo))!=0 |
| 1363 | 1445 | && strcmp(zMimetype, "application/x-fossil-artifact")!=0 |
| 1364 | 1446 | ){ |
| 1365 | 1447 | Blob content; |
| 1366 | - blob_read_from_file(&content, zRepo); | |
| 1448 | + blob_read_from_file(&content, file_cleanup_fullpath(zRepo)); | |
| 1367 | 1449 | cgi_set_content_type(zMimetype); |
| 1368 | 1450 | cgi_set_content(&content); |
| 1369 | 1451 | cgi_reply(); |
| 1370 | 1452 | return; |
| 1371 | 1453 | } |
| 1372 | 1454 | zRepo[j] = '.'; |
| 1373 | 1455 | } |
| 1374 | 1456 | |
| 1457 | + /* If we reach this point, it means that the search of the PATH_INFO | |
| 1458 | + ** string is finished. Either zRepo contains the name of the | |
| 1459 | + ** repository to be used, or else no repository could be found an | |
| 1460 | + ** some kind of error response is required. | |
| 1461 | + */ | |
| 1375 | 1462 | if( szFile<1024 ){ |
| 1376 | 1463 | set_base_url(0); |
| 1377 | 1464 | if( strcmp(zPathInfo,"/")==0 |
| 1378 | 1465 | && allowRepoList |
| 1379 | 1466 | && repo_list_page() ){ |
| @@ -1393,34 +1480,49 @@ | ||
| 1393 | 1480 | } |
| 1394 | 1481 | return; |
| 1395 | 1482 | } |
| 1396 | 1483 | break; |
| 1397 | 1484 | } |
| 1485 | + | |
| 1486 | + /* Add the repository name (without the ".fossil" suffix) to the end | |
| 1487 | + ** of SCRIPT_NAME and remove the repository name from the beginning | |
| 1488 | + ** of PATH_INFO. | |
| 1489 | + */ | |
| 1398 | 1490 | zNewScript = mprintf("%s%.*s", zOldScript, i, zPathInfo); |
| 1399 | 1491 | cgi_replace_parameter("PATH_INFO", &zPathInfo[i+1]); |
| 1400 | 1492 | zPathInfo += i; |
| 1401 | 1493 | cgi_replace_parameter("SCRIPT_NAME", zNewScript); |
| 1402 | - db_open_repository(zRepo); | |
| 1494 | + db_open_repository(file_cleanup_fullpath(zRepo)); | |
| 1403 | 1495 | if( g.fHttpTrace ){ |
| 1496 | + @ <!-- repository: "%h(zRepo)" --> | |
| 1497 | + @ <!-- new PATH_INFO: "%h(zPathInfo)" --> | |
| 1498 | + @ <!-- new SCRIPT_NAME: "%h(zNewScript)" --> | |
| 1404 | 1499 | fprintf(stderr, |
| 1405 | 1500 | "# repository: [%s]\n" |
| 1406 | 1501 | "# new PATH_INFO = [%s]\n" |
| 1407 | 1502 | "# new SCRIPT_NAME = [%s]\n", |
| 1408 | 1503 | zRepo, zPathInfo, zNewScript); |
| 1409 | 1504 | } |
| 1410 | 1505 | } |
| 1411 | 1506 | |
| 1412 | - /* Find the page that the user has requested, construct and deliver that | |
| 1413 | - ** page. | |
| 1507 | + /* At this point, the appropriate repository database file will have | |
| 1508 | + ** been opened. Use the first element of PATH_INFO as the page name | |
| 1509 | + ** and deliver the appropriate page back to the user. | |
| 1414 | 1510 | */ |
| 1415 | 1511 | if( g.zContentType && |
| 1416 | 1512 | strncmp(g.zContentType, "application/x-fossil", 20)==0 ){ |
| 1513 | + /* Special case: If the content mimetype shows that it is "fossil sync" | |
| 1514 | + ** payload, then pretend that the PATH_INFO is /xfer so that we always | |
| 1515 | + ** invoke the sync page. */ | |
| 1417 | 1516 | zPathInfo = "/xfer"; |
| 1418 | 1517 | } |
| 1419 | 1518 | set_base_url(0); |
| 1420 | 1519 | if( zPathInfo==0 || zPathInfo[0]==0 |
| 1421 | 1520 | || (zPathInfo[0]=='/' && zPathInfo[1]==0) ){ |
| 1521 | + /* Second special case: If the PATH_INFO is blank, issue a redirect to | |
| 1522 | + ** the home page identified by the "index-page" setting in the repository | |
| 1523 | + ** CONFIG table, to "/index" if there no "index-page" setting. */ | |
| 1422 | 1524 | #ifdef FOSSIL_ENABLE_JSON |
| 1423 | 1525 | if(g.json.isJsonMode){ |
| 1424 | 1526 | json_err(FSL_JSON_E_RESOURCE_NOT_FOUND,NULL,1); |
| 1425 | 1527 | fossil_exit(0); |
| 1426 | 1528 | } |
| @@ -1437,56 +1539,10 @@ | ||
| 1437 | 1539 | g.zPath = &zPath[1]; |
| 1438 | 1540 | for(i=1; zPath[i] && zPath[i]!='/'; i++){} |
| 1439 | 1541 | if( zPath[i]=='/' ){ |
| 1440 | 1542 | zPath[i] = 0; |
| 1441 | 1543 | g.zExtra = &zPath[i+1]; |
| 1442 | - | |
| 1443 | -#ifdef FOSSIL_ENABLE_SUBREPOSITORY | |
| 1444 | - char *zAltRepo = 0; | |
| 1445 | - /* 2016-09-21: Subrepos are undocumented and apparently no longer work. | |
| 1446 | - ** So they are now removed unless the -DFOSSIL_ENABLE_SUBREPOSITORY | |
| 1447 | - ** compile-time option is used. If there are no complaints after | |
| 1448 | - ** a while, we can delete the code entirely. | |
| 1449 | - */ | |
| 1450 | - /* Look for sub-repositories. A sub-repository is another repository | |
| 1451 | - ** that accepts the login credentials of the current repository. A | |
| 1452 | - ** subrepository is identified by a CONFIG table entry "subrepo:NAME" | |
| 1453 | - ** where NAME is the first component of the path. The value of the | |
| 1454 | - ** the CONFIG entries is the string "USER:FILENAME" where USER is the | |
| 1455 | - ** USER name to log in as in the subrepository and FILENAME is the | |
| 1456 | - ** repository filename. | |
| 1457 | - */ | |
| 1458 | - zAltRepo = db_text(0, "SELECT value FROM config WHERE name='subrepo:%q'", | |
| 1459 | - g.zPath); | |
| 1460 | - if( zAltRepo ){ | |
| 1461 | - int nHost; | |
| 1462 | - int jj; | |
| 1463 | - char *zUser = zAltRepo; | |
| 1464 | - login_check_credentials(); | |
| 1465 | - for(jj=0; zAltRepo[jj] && zAltRepo[jj]!=':'; jj++){} | |
| 1466 | - if( zAltRepo[jj]==':' ){ | |
| 1467 | - zAltRepo[jj] = 0; | |
| 1468 | - zAltRepo += jj+1; | |
| 1469 | - }else{ | |
| 1470 | - zUser = "nobody"; | |
| 1471 | - } | |
| 1472 | - if( g.zLogin==0 || g.zLogin[0]==0 ) zUser = "nobody"; | |
| 1473 | - if( zAltRepo[0]!='/' ){ | |
| 1474 | - zAltRepo = mprintf("%s/../%s", g.zRepositoryName, zAltRepo); | |
| 1475 | - file_simplify_name(zAltRepo, -1, 0); | |
| 1476 | - } | |
| 1477 | - db_close(1); | |
| 1478 | - db_open_repository(zAltRepo); | |
| 1479 | - login_as_user(zUser); | |
| 1480 | - g.perm.Password = 0; | |
| 1481 | - zPath += i; | |
| 1482 | - nHost = g.zTop - g.zBaseURL; | |
| 1483 | - g.zBaseURL = mprintf("%z/%s", g.zBaseURL, g.zPath); | |
| 1484 | - g.zTop = g.zBaseURL + nHost; | |
| 1485 | - continue; | |
| 1486 | - } | |
| 1487 | -#endif /* FOSSIL_ENABLE_SUBREPOSITORY */ | |
| 1488 | 1544 | }else{ |
| 1489 | 1545 | g.zExtra = 0; |
| 1490 | 1546 | } |
| 1491 | 1547 | break; |
| 1492 | 1548 | } |
| @@ -2228,11 +2284,12 @@ | ||
| 2228 | 2284 | ** the REPOSITORY can only be a directory if the --notfound option is |
| 2229 | 2285 | ** also present. |
| 2230 | 2286 | ** |
| 2231 | 2287 | ** For the special case REPOSITORY name of "/", the list global configuration |
| 2232 | 2288 | ** database is consulted for a list of all known repositories. The --repolist |
| 2233 | -** option is implied by this special case. | |
| 2289 | +** option is implied by this special case. See also the "fossil all ui" | |
| 2290 | +** command. | |
| 2234 | 2291 | ** |
| 2235 | 2292 | ** By default, the "ui" command provides full administrative access without |
| 2236 | 2293 | ** having to log in. This can be disabled by turning off the "localauth" |
| 2237 | 2294 | ** setting. Automatic login for the "server" command is available if the |
| 2238 | 2295 | ** --localauth option is present and the "localauth" setting is off and the |
| 2239 | 2296 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -1199,17 +1199,30 @@ | |
| 1199 | |
| 1200 | assert( g.db==0 ); |
| 1201 | if( fossil_strcmp(g.zRepositoryName,"/")==0 ){ |
| 1202 | /* For the special case of the "repository directory" being "/", |
| 1203 | ** show all of the repositories named in the ~/.fossil database. |
| 1204 | */ |
| 1205 | db_open_config(1, 0); |
| 1206 | db_multi_exec( |
| 1207 | "CREATE TEMP VIEW sfile AS" |
| 1208 | " SELECT substr(name,7) AS 'pathname' FROM global_config" |
| 1209 | " WHERE name GLOB 'repo:*'" |
| 1210 | ); |
| 1211 | }else{ |
| 1212 | /* The default case: All repositories under the g.zRepositoryName |
| 1213 | ** directory. |
| 1214 | */ |
| 1215 | blob_init(&base, g.zRepositoryName, -1); |
| @@ -1233,11 +1246,11 @@ | |
| 1233 | db_prepare(&q, "SELECT pathname, substr(pathname,-7,-100000)||'/home'" |
| 1234 | " FROM sfile ORDER BY pathname COLLATE nocase;"); |
| 1235 | while( db_step(&q)==SQLITE_ROW ){ |
| 1236 | const char *zName = db_column_text(&q, 0); |
| 1237 | const char *zUrl = db_column_text(&q, 1); |
| 1238 | @ <li><a href="%R/%h(zUrl)" target="_blank">%h(zName)</a></li> |
| 1239 | } |
| 1240 | @ </ol> |
| 1241 | }else{ |
| 1242 | @ <h1>No Repositories Found</h1> |
| 1243 | } |
| @@ -1274,27 +1287,25 @@ | |
| 1274 | static void process_one_web_page( |
| 1275 | const char *zNotFound, /* Redirect here on a 404 if not NULL */ |
| 1276 | Glob *pFileGlob, /* Deliver static files matching */ |
| 1277 | int allowRepoList /* Send repo list for "/" URL */ |
| 1278 | ){ |
| 1279 | const char *zPathInfo; |
| 1280 | const char *zDirPathInfo; |
| 1281 | char *zPath = NULL; |
| 1282 | int i; |
| 1283 | const CmdOrPage *pCmd = 0; |
| 1284 | |
| 1285 | /* Handle universal query parameters */ |
| 1286 | if( PB("utc") ){ |
| 1287 | g.fTimeFormat = 1; |
| 1288 | }else if( PB("localtime") ){ |
| 1289 | g.fTimeFormat = 2; |
| 1290 | } |
| 1291 | |
| 1292 | /* If the repository has not been opened already, then find the |
| 1293 | ** repository based on the first element of PATH_INFO and open it. |
| 1294 | */ |
| 1295 | zDirPathInfo = zPathInfo = PD("PATH_INFO",""); |
| 1296 | /* For the PATH_INFO that will be used to help build the final |
| 1297 | ** g.zBaseURL and g.zTop (only), skip over the initial directory |
| 1298 | ** portion of PATH_INFO; otherwise, it may be duplicated. |
| 1299 | */ |
| 1300 | if( g.zTop ){ |
| @@ -1301,79 +1312,155 @@ | |
| 1301 | int nTop = strlen(g.zTop); |
| 1302 | if ( strncmp(zDirPathInfo, g.zTop, nTop)==0 ){ |
| 1303 | zDirPathInfo += nTop; |
| 1304 | } |
| 1305 | } |
| 1306 | if( !g.repositoryOpen ){ |
| 1307 | char *zRepo, *zToFree; |
| 1308 | const char *zOldScript = PD("SCRIPT_NAME", ""); |
| 1309 | char *zNewScript; |
| 1310 | int j, k; |
| 1311 | i64 szFile; |
| 1312 | |
| 1313 | i = zPathInfo[0]!=0; |
| 1314 | while( 1 ){ |
| 1315 | while( zPathInfo[i] && zPathInfo[i]!='/' ){ i++; } |
| 1316 | zRepo = zToFree = mprintf("%s%.*s.fossil",g.zRepositoryName,i,zPathInfo); |
| 1317 | |
| 1318 | /* To avoid mischief, make sure the repository basename contains no |
| 1319 | ** characters other than alphanumerics, "/", "_", "-", and ".", and |
| 1320 | ** that "-" never occurs immediately after a "/" and that "." is always |
| 1321 | ** surrounded by two alphanumerics. Any character that does not |
| 1322 | ** satisfy these constraints is converted into "_". |
| 1323 | */ |
| 1324 | szFile = 0; |
| 1325 | for(j=strlen(g.zRepositoryName)+1, k=0; zRepo[j] && k<i-1; j++, k++){ |
| 1326 | char c = zRepo[j]; |
| 1327 | if( fossil_isalnum(c) ) continue; |
| 1328 | if( c=='/' ) continue; |
| 1329 | if( c=='_' ) continue; |
| 1330 | if( c=='-' && zRepo[j-1]!='/' ) continue; |
| 1331 | if( c=='.' && fossil_isalnum(zRepo[j-1]) && fossil_isalnum(zRepo[j+1])){ |
| 1332 | continue; |
| 1333 | } |
| 1334 | szFile = 1; |
| 1335 | break; |
| 1336 | } |
| 1337 | if( szFile==0 && sqlite3_strglob("*/.fossil",zRepo)!=0 ){ |
| 1338 | if( zRepo[0]=='/' && zRepo[1]=='/' ){ zRepo++; j--; } |
| 1339 | szFile = file_size(zRepo); |
| 1340 | /* this should only be set from the --baseurl option, not CGI */ |
| 1341 | if( g.zBaseURL && g.zBaseURL[0]!=0 && g.zTop && g.zTop[0]!=0 && |
| 1342 | file_isdir(g.zRepositoryName)==1 ){ |
| 1343 | if( zPathInfo==zDirPathInfo ){ |
| 1344 | g.zBaseURL = mprintf("%s%.*s", g.zBaseURL, i, zPathInfo); |
| 1345 | g.zTop = mprintf("%s%.*s", g.zTop, i, zPathInfo); |
| 1346 | } |
| 1347 | } |
| 1348 | } |
| 1349 | if( szFile<0 && i>0 ){ |
| 1350 | const char *zMimetype; |
| 1351 | assert( fossil_strcmp(&zRepo[j], ".fossil")==0 ); |
| 1352 | zRepo[j] = 0; |
| 1353 | if( zPathInfo[i]=='/' && file_isdir(zRepo)==1 ){ |
| 1354 | fossil_free(zToFree); |
| 1355 | i++; |
| 1356 | continue; |
| 1357 | } |
| 1358 | if( pFileGlob!=0 |
| 1359 | && file_isfile(zRepo) |
| 1360 | && glob_match(pFileGlob, zRepo) |
| 1361 | && sqlite3_strglob("*.fossil*",zRepo)!=0 |
| 1362 | && (zMimetype = mimetype_from_name(zRepo))!=0 |
| 1363 | && strcmp(zMimetype, "application/x-fossil-artifact")!=0 |
| 1364 | ){ |
| 1365 | Blob content; |
| 1366 | blob_read_from_file(&content, zRepo); |
| 1367 | cgi_set_content_type(zMimetype); |
| 1368 | cgi_set_content(&content); |
| 1369 | cgi_reply(); |
| 1370 | return; |
| 1371 | } |
| 1372 | zRepo[j] = '.'; |
| 1373 | } |
| 1374 | |
| 1375 | if( szFile<1024 ){ |
| 1376 | set_base_url(0); |
| 1377 | if( strcmp(zPathInfo,"/")==0 |
| 1378 | && allowRepoList |
| 1379 | && repo_list_page() ){ |
| @@ -1393,34 +1480,49 @@ | |
| 1393 | } |
| 1394 | return; |
| 1395 | } |
| 1396 | break; |
| 1397 | } |
| 1398 | zNewScript = mprintf("%s%.*s", zOldScript, i, zPathInfo); |
| 1399 | cgi_replace_parameter("PATH_INFO", &zPathInfo[i+1]); |
| 1400 | zPathInfo += i; |
| 1401 | cgi_replace_parameter("SCRIPT_NAME", zNewScript); |
| 1402 | db_open_repository(zRepo); |
| 1403 | if( g.fHttpTrace ){ |
| 1404 | fprintf(stderr, |
| 1405 | "# repository: [%s]\n" |
| 1406 | "# new PATH_INFO = [%s]\n" |
| 1407 | "# new SCRIPT_NAME = [%s]\n", |
| 1408 | zRepo, zPathInfo, zNewScript); |
| 1409 | } |
| 1410 | } |
| 1411 | |
| 1412 | /* Find the page that the user has requested, construct and deliver that |
| 1413 | ** page. |
| 1414 | */ |
| 1415 | if( g.zContentType && |
| 1416 | strncmp(g.zContentType, "application/x-fossil", 20)==0 ){ |
| 1417 | zPathInfo = "/xfer"; |
| 1418 | } |
| 1419 | set_base_url(0); |
| 1420 | if( zPathInfo==0 || zPathInfo[0]==0 |
| 1421 | || (zPathInfo[0]=='/' && zPathInfo[1]==0) ){ |
| 1422 | #ifdef FOSSIL_ENABLE_JSON |
| 1423 | if(g.json.isJsonMode){ |
| 1424 | json_err(FSL_JSON_E_RESOURCE_NOT_FOUND,NULL,1); |
| 1425 | fossil_exit(0); |
| 1426 | } |
| @@ -1437,56 +1539,10 @@ | |
| 1437 | g.zPath = &zPath[1]; |
| 1438 | for(i=1; zPath[i] && zPath[i]!='/'; i++){} |
| 1439 | if( zPath[i]=='/' ){ |
| 1440 | zPath[i] = 0; |
| 1441 | g.zExtra = &zPath[i+1]; |
| 1442 | |
| 1443 | #ifdef FOSSIL_ENABLE_SUBREPOSITORY |
| 1444 | char *zAltRepo = 0; |
| 1445 | /* 2016-09-21: Subrepos are undocumented and apparently no longer work. |
| 1446 | ** So they are now removed unless the -DFOSSIL_ENABLE_SUBREPOSITORY |
| 1447 | ** compile-time option is used. If there are no complaints after |
| 1448 | ** a while, we can delete the code entirely. |
| 1449 | */ |
| 1450 | /* Look for sub-repositories. A sub-repository is another repository |
| 1451 | ** that accepts the login credentials of the current repository. A |
| 1452 | ** subrepository is identified by a CONFIG table entry "subrepo:NAME" |
| 1453 | ** where NAME is the first component of the path. The value of the |
| 1454 | ** the CONFIG entries is the string "USER:FILENAME" where USER is the |
| 1455 | ** USER name to log in as in the subrepository and FILENAME is the |
| 1456 | ** repository filename. |
| 1457 | */ |
| 1458 | zAltRepo = db_text(0, "SELECT value FROM config WHERE name='subrepo:%q'", |
| 1459 | g.zPath); |
| 1460 | if( zAltRepo ){ |
| 1461 | int nHost; |
| 1462 | int jj; |
| 1463 | char *zUser = zAltRepo; |
| 1464 | login_check_credentials(); |
| 1465 | for(jj=0; zAltRepo[jj] && zAltRepo[jj]!=':'; jj++){} |
| 1466 | if( zAltRepo[jj]==':' ){ |
| 1467 | zAltRepo[jj] = 0; |
| 1468 | zAltRepo += jj+1; |
| 1469 | }else{ |
| 1470 | zUser = "nobody"; |
| 1471 | } |
| 1472 | if( g.zLogin==0 || g.zLogin[0]==0 ) zUser = "nobody"; |
| 1473 | if( zAltRepo[0]!='/' ){ |
| 1474 | zAltRepo = mprintf("%s/../%s", g.zRepositoryName, zAltRepo); |
| 1475 | file_simplify_name(zAltRepo, -1, 0); |
| 1476 | } |
| 1477 | db_close(1); |
| 1478 | db_open_repository(zAltRepo); |
| 1479 | login_as_user(zUser); |
| 1480 | g.perm.Password = 0; |
| 1481 | zPath += i; |
| 1482 | nHost = g.zTop - g.zBaseURL; |
| 1483 | g.zBaseURL = mprintf("%z/%s", g.zBaseURL, g.zPath); |
| 1484 | g.zTop = g.zBaseURL + nHost; |
| 1485 | continue; |
| 1486 | } |
| 1487 | #endif /* FOSSIL_ENABLE_SUBREPOSITORY */ |
| 1488 | }else{ |
| 1489 | g.zExtra = 0; |
| 1490 | } |
| 1491 | break; |
| 1492 | } |
| @@ -2228,11 +2284,12 @@ | |
| 2228 | ** the REPOSITORY can only be a directory if the --notfound option is |
| 2229 | ** also present. |
| 2230 | ** |
| 2231 | ** For the special case REPOSITORY name of "/", the list global configuration |
| 2232 | ** database is consulted for a list of all known repositories. The --repolist |
| 2233 | ** option is implied by this special case. |
| 2234 | ** |
| 2235 | ** By default, the "ui" command provides full administrative access without |
| 2236 | ** having to log in. This can be disabled by turning off the "localauth" |
| 2237 | ** setting. Automatic login for the "server" command is available if the |
| 2238 | ** --localauth option is present and the "localauth" setting is off and the |
| 2239 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -1199,17 +1199,30 @@ | |
| 1199 | |
| 1200 | assert( g.db==0 ); |
| 1201 | if( fossil_strcmp(g.zRepositoryName,"/")==0 ){ |
| 1202 | /* For the special case of the "repository directory" being "/", |
| 1203 | ** show all of the repositories named in the ~/.fossil database. |
| 1204 | ** |
| 1205 | ** On unix systems, then entries are of the form "repo:/home/..." |
| 1206 | ** and on Windows systems they are like "repo:C:/Users/...". We want |
| 1207 | ** to skip the first 6 characters on unix and the first 5 characters |
| 1208 | ** on Windows. |
| 1209 | */ |
| 1210 | db_open_config(1, 0); |
| 1211 | #ifdef _WIN32 |
| 1212 | db_multi_exec( |
| 1213 | "CREATE TEMP VIEW sfile AS" |
| 1214 | " SELECT substr(name,6) AS 'pathname' FROM global_config" |
| 1215 | " WHERE name GLOB 'repo:*'" |
| 1216 | ); |
| 1217 | #else |
| 1218 | db_multi_exec( |
| 1219 | "CREATE TEMP VIEW sfile AS" |
| 1220 | " SELECT substr(name,7) AS 'pathname' FROM global_config" |
| 1221 | " WHERE name GLOB 'repo:*'" |
| 1222 | ); |
| 1223 | #endif |
| 1224 | }else{ |
| 1225 | /* The default case: All repositories under the g.zRepositoryName |
| 1226 | ** directory. |
| 1227 | */ |
| 1228 | blob_init(&base, g.zRepositoryName, -1); |
| @@ -1233,11 +1246,11 @@ | |
| 1246 | db_prepare(&q, "SELECT pathname, substr(pathname,-7,-100000)||'/home'" |
| 1247 | " FROM sfile ORDER BY pathname COLLATE nocase;"); |
| 1248 | while( db_step(&q)==SQLITE_ROW ){ |
| 1249 | const char *zName = db_column_text(&q, 0); |
| 1250 | const char *zUrl = db_column_text(&q, 1); |
| 1251 | @ <li><a href="%R/%T(zUrl)" target="_blank">%h(zName)</a></li> |
| 1252 | } |
| 1253 | @ </ol> |
| 1254 | }else{ |
| 1255 | @ <h1>No Repositories Found</h1> |
| 1256 | } |
| @@ -1274,27 +1287,25 @@ | |
| 1287 | static void process_one_web_page( |
| 1288 | const char *zNotFound, /* Redirect here on a 404 if not NULL */ |
| 1289 | Glob *pFileGlob, /* Deliver static files matching */ |
| 1290 | int allowRepoList /* Send repo list for "/" URL */ |
| 1291 | ){ |
| 1292 | const char *zPathInfo = PD("PATH_INFO", ""); |
| 1293 | /* const char *zDirPathInfo; **** refactor needed */ |
| 1294 | char *zPath = NULL; |
| 1295 | int i; |
| 1296 | const CmdOrPage *pCmd = 0; |
| 1297 | const char *zBase = g.zRepositoryName; |
| 1298 | |
| 1299 | /* Handle universal query parameters */ |
| 1300 | if( PB("utc") ){ |
| 1301 | g.fTimeFormat = 1; |
| 1302 | }else if( PB("localtime") ){ |
| 1303 | g.fTimeFormat = 2; |
| 1304 | } |
| 1305 | |
| 1306 | #if 0 /* Refactor needed */ |
| 1307 | /* For the PATH_INFO that will be used to help build the final |
| 1308 | ** g.zBaseURL and g.zTop (only), skip over the initial directory |
| 1309 | ** portion of PATH_INFO; otherwise, it may be duplicated. |
| 1310 | */ |
| 1311 | if( g.zTop ){ |
| @@ -1301,79 +1312,155 @@ | |
| 1312 | int nTop = strlen(g.zTop); |
| 1313 | if ( strncmp(zDirPathInfo, g.zTop, nTop)==0 ){ |
| 1314 | zDirPathInfo += nTop; |
| 1315 | } |
| 1316 | } |
| 1317 | #endif |
| 1318 | |
| 1319 | /* If the repository has not been opened already, then find the |
| 1320 | ** repository based on the first element of PATH_INFO and open it. |
| 1321 | */ |
| 1322 | if( !g.repositoryOpen ){ |
| 1323 | char *zRepo; /* Candidate repository name */ |
| 1324 | char *zToFree = 0; /* Malloced memory that needs to be freed */ |
| 1325 | const char *zCleanRepo; /* zRepo with surplus leading "/" removed */ |
| 1326 | const char *zOldScript = PD("SCRIPT_NAME", ""); /* Original SCRIPT_NAME */ |
| 1327 | char *zNewScript; /* Revised SCRIPT_NAME after processing */ |
| 1328 | int j, k; /* Loop variables */ |
| 1329 | i64 szFile; /* File size of the candidate repository */ |
| 1330 | |
| 1331 | i = zPathInfo[0]!=0; |
| 1332 | if( fossil_strcmp(g.zRepositoryName, "/")==0 ){ |
| 1333 | zBase++; |
| 1334 | #ifdef _WIN32 |
| 1335 | if( sqlite3_strglob("/[a-zA-Z]:/*", zPathInfo)==0 ) i = 4; |
| 1336 | #endif |
| 1337 | } |
| 1338 | while( 1 ){ |
| 1339 | while( zPathInfo[i] && zPathInfo[i]!='/' ){ i++; } |
| 1340 | |
| 1341 | /* The candidate repository name is some prefix of the PATH_INFO |
| 1342 | ** with ".fossil" appended */ |
| 1343 | zRepo = zToFree = mprintf("%s%.*s.fossil",zBase,i,zPathInfo); |
| 1344 | if( g.fHttpTrace ){ |
| 1345 | @ <!-- Looking for repository named "%h(zRepo)" --> |
| 1346 | fprintf(stderr, "# looking for repository named \"%s\"\n", zRepo); |
| 1347 | } |
| 1348 | |
| 1349 | |
| 1350 | /* For safety -- to prevent an attacker from accessing arbitrary disk |
| 1351 | ** files by sending a maliciously crafted request URI to a public |
| 1352 | ** server -- make sure the repository basename contains no |
| 1353 | ** characters other than alphanumerics, "/", "_", "-", and ".", and |
| 1354 | ** that "-" never occurs immediately after a "/" and that "." is always |
| 1355 | ** surrounded by two alphanumerics. Any character that does not |
| 1356 | ** satisfy these constraints is converted into "_". |
| 1357 | */ |
| 1358 | szFile = 0; |
| 1359 | for(j=strlen(zBase)+1, k=0; zRepo[j] && k<i-1; j++, k++){ |
| 1360 | char c = zRepo[j]; |
| 1361 | if( fossil_isalnum(c) ) continue; |
| 1362 | #ifdef _WIN32 |
| 1363 | /* Allow names to begin with "/X:/" on windows */ |
| 1364 | if( c==':' && j==2 && sqlite3_strglob("/[a-zA-Z]:/*", zRepo)==0 ){ |
| 1365 | continue; |
| 1366 | } |
| 1367 | #endif |
| 1368 | if( c=='/' ) continue; |
| 1369 | if( c=='_' ) continue; |
| 1370 | if( c=='-' && zRepo[j-1]!='/' ) continue; |
| 1371 | if( c=='.' && fossil_isalnum(zRepo[j-1]) && fossil_isalnum(zRepo[j+1])){ |
| 1372 | continue; |
| 1373 | } |
| 1374 | /* If we reach this point, it means that the request URI contains |
| 1375 | ** an illegal character or character combination. Provoke a |
| 1376 | ** "Not Found" error. */ |
| 1377 | szFile = 1; |
| 1378 | if( g.fHttpTrace ){ |
| 1379 | @ <!-- Unsafe pathname rejected: "%h(zRepo)" --> |
| 1380 | fprintf(stderr, "# unsafe pathname rejected: %s\n", zRepo); |
| 1381 | } |
| 1382 | break; |
| 1383 | } |
| 1384 | |
| 1385 | /* Check to see if a file name zRepo exists. If a file named zRepo |
| 1386 | ** does not exist, szFile will become -1. If the file does exist, |
| 1387 | ** then szFile will become zero (for an empty file) or positive. |
| 1388 | ** Special case: Assume any file with a basename of ".fossil" does |
| 1389 | ** not exist. |
| 1390 | */ |
| 1391 | zCleanRepo = file_cleanup_fullpath(zRepo); |
| 1392 | if( szFile==0 && sqlite3_strglob("*/.fossil",zRepo)!=0 ){ |
| 1393 | szFile = file_size(zCleanRepo); |
| 1394 | if( g.fHttpTrace ){ |
| 1395 | @ <!-- file_size(%h(zCleanRepo)) is %lld(szFile) --> |
| 1396 | fprintf(stderr, "# file_size(%s) = %lld\n", zCleanRepo, szFile); |
| 1397 | } |
| 1398 | |
| 1399 | #if 0 |
| 1400 | /* This logic for handling --baseurl is confused and needs to be |
| 1401 | ** completely rethought. */ |
| 1402 | if( g.zBaseURL && g.zBaseURL[0]!=0 && g.zTop && g.zTop[0]!=0 && |
| 1403 | file_isdir(g.zRepositoryName)==1 ){ |
| 1404 | if( zPathInfo==zDirPathInfo ){ |
| 1405 | g.zBaseURL = mprintf("%s%.*s", g.zBaseURL, i, zPathInfo); |
| 1406 | g.zTop = mprintf("%s%.*s", g.zTop, i, zPathInfo); |
| 1407 | } |
| 1408 | } |
| 1409 | #endif |
| 1410 | } |
| 1411 | |
| 1412 | /* If no file named by zRepo exists, remove the added ".fossil" suffix |
| 1413 | ** and check to see if there is a file or directory with the same |
| 1414 | ** name as the raw PATH_INFO text. |
| 1415 | */ |
| 1416 | if( szFile<0 && i>0 ){ |
| 1417 | const char *zMimetype; |
| 1418 | assert( fossil_strcmp(&zRepo[j], ".fossil")==0 ); |
| 1419 | zRepo[j] = 0; /* Remove the ".fossil" suffix */ |
| 1420 | |
| 1421 | /* The PATH_INFO prefix seen so far is a valid directory. |
| 1422 | ** Continue the loop with the next element of the PATH_INFO */ |
| 1423 | if( zPathInfo[i]=='/' && file_isdir(zCleanRepo)==1 ){ |
| 1424 | fossil_free(zToFree); |
| 1425 | i++; |
| 1426 | continue; |
| 1427 | } |
| 1428 | |
| 1429 | /* If zRepo is the name of an ordinary file that matches the |
| 1430 | ** "--file GLOB" pattern, then the CGI reply is the text of |
| 1431 | ** of the file. |
| 1432 | ** |
| 1433 | ** For safety, do not allow any file whose name contains ".fossil" |
| 1434 | ** to be returned this way, to prevent complete repositories from |
| 1435 | ** being delivered accidently. This is not intended to be a |
| 1436 | ** general-purpose web server. The "--file GLOB" mechanism is |
| 1437 | ** designed to allow the delivery of a few static images or HTML |
| 1438 | ** pages. |
| 1439 | */ |
| 1440 | if( pFileGlob!=0 |
| 1441 | && file_isfile(zCleanRepo) |
| 1442 | && glob_match(pFileGlob, file_cleanup_fullpath(zRepo)) |
| 1443 | && sqlite3_strglob("*.fossil*",zRepo)!=0 |
| 1444 | && (zMimetype = mimetype_from_name(zRepo))!=0 |
| 1445 | && strcmp(zMimetype, "application/x-fossil-artifact")!=0 |
| 1446 | ){ |
| 1447 | Blob content; |
| 1448 | blob_read_from_file(&content, file_cleanup_fullpath(zRepo)); |
| 1449 | cgi_set_content_type(zMimetype); |
| 1450 | cgi_set_content(&content); |
| 1451 | cgi_reply(); |
| 1452 | return; |
| 1453 | } |
| 1454 | zRepo[j] = '.'; |
| 1455 | } |
| 1456 | |
| 1457 | /* If we reach this point, it means that the search of the PATH_INFO |
| 1458 | ** string is finished. Either zRepo contains the name of the |
| 1459 | ** repository to be used, or else no repository could be found an |
| 1460 | ** some kind of error response is required. |
| 1461 | */ |
| 1462 | if( szFile<1024 ){ |
| 1463 | set_base_url(0); |
| 1464 | if( strcmp(zPathInfo,"/")==0 |
| 1465 | && allowRepoList |
| 1466 | && repo_list_page() ){ |
| @@ -1393,34 +1480,49 @@ | |
| 1480 | } |
| 1481 | return; |
| 1482 | } |
| 1483 | break; |
| 1484 | } |
| 1485 | |
| 1486 | /* Add the repository name (without the ".fossil" suffix) to the end |
| 1487 | ** of SCRIPT_NAME and remove the repository name from the beginning |
| 1488 | ** of PATH_INFO. |
| 1489 | */ |
| 1490 | zNewScript = mprintf("%s%.*s", zOldScript, i, zPathInfo); |
| 1491 | cgi_replace_parameter("PATH_INFO", &zPathInfo[i+1]); |
| 1492 | zPathInfo += i; |
| 1493 | cgi_replace_parameter("SCRIPT_NAME", zNewScript); |
| 1494 | db_open_repository(file_cleanup_fullpath(zRepo)); |
| 1495 | if( g.fHttpTrace ){ |
| 1496 | @ <!-- repository: "%h(zRepo)" --> |
| 1497 | @ <!-- new PATH_INFO: "%h(zPathInfo)" --> |
| 1498 | @ <!-- new SCRIPT_NAME: "%h(zNewScript)" --> |
| 1499 | fprintf(stderr, |
| 1500 | "# repository: [%s]\n" |
| 1501 | "# new PATH_INFO = [%s]\n" |
| 1502 | "# new SCRIPT_NAME = [%s]\n", |
| 1503 | zRepo, zPathInfo, zNewScript); |
| 1504 | } |
| 1505 | } |
| 1506 | |
| 1507 | /* At this point, the appropriate repository database file will have |
| 1508 | ** been opened. Use the first element of PATH_INFO as the page name |
| 1509 | ** and deliver the appropriate page back to the user. |
| 1510 | */ |
| 1511 | if( g.zContentType && |
| 1512 | strncmp(g.zContentType, "application/x-fossil", 20)==0 ){ |
| 1513 | /* Special case: If the content mimetype shows that it is "fossil sync" |
| 1514 | ** payload, then pretend that the PATH_INFO is /xfer so that we always |
| 1515 | ** invoke the sync page. */ |
| 1516 | zPathInfo = "/xfer"; |
| 1517 | } |
| 1518 | set_base_url(0); |
| 1519 | if( zPathInfo==0 || zPathInfo[0]==0 |
| 1520 | || (zPathInfo[0]=='/' && zPathInfo[1]==0) ){ |
| 1521 | /* Second special case: If the PATH_INFO is blank, issue a redirect to |
| 1522 | ** the home page identified by the "index-page" setting in the repository |
| 1523 | ** CONFIG table, to "/index" if there no "index-page" setting. */ |
| 1524 | #ifdef FOSSIL_ENABLE_JSON |
| 1525 | if(g.json.isJsonMode){ |
| 1526 | json_err(FSL_JSON_E_RESOURCE_NOT_FOUND,NULL,1); |
| 1527 | fossil_exit(0); |
| 1528 | } |
| @@ -1437,56 +1539,10 @@ | |
| 1539 | g.zPath = &zPath[1]; |
| 1540 | for(i=1; zPath[i] && zPath[i]!='/'; i++){} |
| 1541 | if( zPath[i]=='/' ){ |
| 1542 | zPath[i] = 0; |
| 1543 | g.zExtra = &zPath[i+1]; |
| 1544 | }else{ |
| 1545 | g.zExtra = 0; |
| 1546 | } |
| 1547 | break; |
| 1548 | } |
| @@ -2228,11 +2284,12 @@ | |
| 2284 | ** the REPOSITORY can only be a directory if the --notfound option is |
| 2285 | ** also present. |
| 2286 | ** |
| 2287 | ** For the special case REPOSITORY name of "/", the list global configuration |
| 2288 | ** database is consulted for a list of all known repositories. The --repolist |
| 2289 | ** option is implied by this special case. See also the "fossil all ui" |
| 2290 | ** command. |
| 2291 | ** |
| 2292 | ** By default, the "ui" command provides full administrative access without |
| 2293 | ** having to log in. This can be disabled by turning off the "localauth" |
| 2294 | ** setting. Automatic login for the "server" command is available if the |
| 2295 | ** --localauth option is present and the "localauth" setting is off and the |
| 2296 |