Fossil SCM

Add the "fossil get" command. This command is still experimental. Its name might change. It might go away completely. To remove the new command, just remove the "get_cmd()" function in checkout.c. All the other changes in this merge are infrastructure improvements and fixes that ought to stay even if we decide to forego the "fossil get" command.

drh 2025-10-15 23:34 trunk merge
Commit 2f9920a151c7986defc7005787f7ad2dbb91b0987a134679bfff02724c76f386
+3
--- src/cgi.c
+++ src/cgi.c
@@ -2363,12 +2363,14 @@
23632363
23642364
for(i=0; zToken[i] && zToken[i]!='?'; i++){}
23652365
if( zToken[i] ) zToken[i++] = 0;
23662366
if( nCycles==0 ){
23672367
cgi_setenv("PATH_INFO", zToken);
2368
+ cgi_setenv("QUERY_STRING",&zToken[i]);
23682369
}else{
23692370
cgi_replace_parameter("PATH_INFO", fossil_strdup(zToken));
2371
+ cgi_replace_parameter("QUERY_STRING",fossil_strdup(&zToken[i]));
23702372
}
23712373
23722374
/* Get all the optional fields that follow the first line.
23732375
*/
23742376
while( fgets(zLine,sizeof(zLine),g.httpIn) ){
@@ -2428,10 +2430,11 @@
24282430
blob_read_from_channel(&g.cgiIn, g.httpIn, content_length);
24292431
}else if( fossil_strcmp(zType, "application/x-fossil-uncompressed")==0 ){
24302432
blob_read_from_channel(&g.cgiIn, g.httpIn, content_length);
24312433
}
24322434
}
2435
+ cgi_init();
24332436
cgi_trace(0);
24342437
nCycles++;
24352438
}
24362439
24372440
/*
24382441
--- src/cgi.c
+++ src/cgi.c
@@ -2363,12 +2363,14 @@
2363
2364 for(i=0; zToken[i] && zToken[i]!='?'; i++){}
2365 if( zToken[i] ) zToken[i++] = 0;
2366 if( nCycles==0 ){
2367 cgi_setenv("PATH_INFO", zToken);
 
2368 }else{
2369 cgi_replace_parameter("PATH_INFO", fossil_strdup(zToken));
 
2370 }
2371
2372 /* Get all the optional fields that follow the first line.
2373 */
2374 while( fgets(zLine,sizeof(zLine),g.httpIn) ){
@@ -2428,10 +2430,11 @@
2428 blob_read_from_channel(&g.cgiIn, g.httpIn, content_length);
2429 }else if( fossil_strcmp(zType, "application/x-fossil-uncompressed")==0 ){
2430 blob_read_from_channel(&g.cgiIn, g.httpIn, content_length);
2431 }
2432 }
 
2433 cgi_trace(0);
2434 nCycles++;
2435 }
2436
2437 /*
2438
--- src/cgi.c
+++ src/cgi.c
@@ -2363,12 +2363,14 @@
2363
2364 for(i=0; zToken[i] && zToken[i]!='?'; i++){}
2365 if( zToken[i] ) zToken[i++] = 0;
2366 if( nCycles==0 ){
2367 cgi_setenv("PATH_INFO", zToken);
2368 cgi_setenv("QUERY_STRING",&zToken[i]);
2369 }else{
2370 cgi_replace_parameter("PATH_INFO", fossil_strdup(zToken));
2371 cgi_replace_parameter("QUERY_STRING",fossil_strdup(&zToken[i]));
2372 }
2373
2374 /* Get all the optional fields that follow the first line.
2375 */
2376 while( fgets(zLine,sizeof(zLine),g.httpIn) ){
@@ -2428,10 +2430,11 @@
2430 blob_read_from_channel(&g.cgiIn, g.httpIn, content_length);
2431 }else if( fossil_strcmp(zType, "application/x-fossil-uncompressed")==0 ){
2432 blob_read_from_channel(&g.cgiIn, g.httpIn, content_length);
2433 }
2434 }
2435 cgi_init();
2436 cgi_trace(0);
2437 nCycles++;
2438 }
2439
2440 /*
2441
--- src/checkout.c
+++ src/checkout.c
@@ -19,10 +19,11 @@
1919
** from the local repository.
2020
*/
2121
#include "config.h"
2222
#include "checkout.h"
2323
#include <assert.h>
24
+#include <zlib.h>
2425
2526
/*
2627
** Check to see if there is an existing check-out that has been
2728
** modified. Return values:
2829
**
@@ -429,5 +430,229 @@
429430
}
430431
unlink_local_database(1);
431432
db_close(1);
432433
unlink_local_database(0);
433434
}
435
+
436
+
437
+/*
438
+** COMMAND: get
439
+**
440
+** Usage: %fossil get URL ?VERSION? ?OPTIONS?
441
+**
442
+** Download a single check-in from a remote repository named URL and
443
+** unpack all of the files locally. The check-in is identified by VERSION.
444
+**
445
+** URL can be a traditional URL like one of:
446
+**
447
+** * https://domain.com/project
448
+** * ssh://my-server/project.fossil
449
+** * file:/home/user/Fossils/project.fossil
450
+**
451
+** Or URL can be just the name of a local repository without the "file:"
452
+** prefix.
453
+**
454
+** This command works by downloading an SQL archive of the requested
455
+** check-in and then extracting all the files from the archive.
456
+**
457
+** Options:
458
+** --dest DIRECTORY Extract files into DIRECTORY. Use "--dest ."
459
+** to extract into the local directory.
460
+**
461
+** -f|--force Overwrite existing files
462
+**
463
+** --list List all the files that would have been checked
464
+** out but do not actually write anything to the
465
+** filesystem.
466
+**
467
+** --sqlar ARCHIVE Store the check-out in an SQL-archive rather
468
+** than unpacking them into separate files.
469
+**
470
+** -v|--verbose Show all files as they are extracted
471
+*/
472
+void get_cmd(void){
473
+ int forceFlag = find_option("force","f",0)!=0;
474
+ int bVerbose = find_option("verbose","v",0)!=0;
475
+ int bQuiet = find_option("quiet","q",0)!=0;
476
+ int bDebug = find_option("debug",0,0)!=0;
477
+ int bList = find_option("list",0,0)!=0;
478
+ const char *zSqlArchive = find_option("sqlar",0,1);
479
+ const char *z;
480
+ char *zDest = 0; /* Where to store results */
481
+ char *zSql; /* SQL used to query the results */
482
+ const char *zUrl; /* Url to get */
483
+ const char *zVers; /* Version name to get */
484
+ unsigned int mHttpFlags = HTTP_GENERIC|HTTP_NOCOMPRESS;
485
+ Blob in, out; /* I/O for the HTTP request */
486
+ Blob file; /* A file to extract */
487
+ sqlite3 *db; /* Database containing downloaded sqlar */
488
+ sqlite3_stmt *pStmt; /* Statement for querying the database */
489
+ int rc; /* Result of subroutine calls */
490
+ int nFile = 0; /* Number of files written */
491
+ int nDir = 0; /* Number of directories written */
492
+ i64 nByte = 0; /* Number of bytes written */
493
+
494
+ z = find_option("dest",0,1);
495
+ if( z ) zDest = fossil_strdup(z);
496
+ verify_all_options();
497
+ if( g.argc<3 || g.argc>4 ){
498
+ usage("get URL ?VERSION? ?OPTIONS?");
499
+ }
500
+ zUrl = g.argv[2];
501
+ zVers = g.argc==4 ? g.argv[3] : "trunk";
502
+
503
+ /* Parse the URL of the repository */
504
+ url_parse(zUrl, 0);
505
+
506
+ /* Construct an appropriate name for the destination directory */
507
+ if( zDest==0 ){
508
+ int i;
509
+ const char *zTail;
510
+ const char *zDot;
511
+ int n;
512
+ if( g.url.isFile ){
513
+ zTail = file_tail(g.url.name);
514
+ }else{
515
+ zTail = file_tail(g.url.path);
516
+ }
517
+ zDot = strchr(zTail,'.');
518
+ if( zDot==0 ) zDot = zTail+strlen(zTail);
519
+ n = (int)(zDot - zTail);
520
+ zDest = mprintf("%.*s-%s", n, zTail, zVers);
521
+ for(i=0; zDest[i]; i++){
522
+ char c = zDest[i];
523
+ if( !fossil_isalnum(c) && c!='-' && c!='^' && c!='~' && c!='_' ){
524
+ zDest[i] = '-';
525
+ }
526
+ }
527
+ }
528
+ if( bDebug ){
529
+ fossil_print("dest = %s\n", zDest);
530
+ }
531
+
532
+ /* Error checking */
533
+ if( zDest!=file_tail(zDest) ){
534
+ fossil_fatal("--dest must be a simple directory name, not a path");
535
+ }
536
+ if( zVers!=file_tail(zVers) ){
537
+ fossil_fatal("The \"fossil get\" command does not current work with"
538
+ " version names that contain \"/\". This will be fixed in"
539
+ " a future release.");
540
+ }
541
+ /* To relax the restrictions above, change the subpath URL formula below
542
+ ** to use query parameters. Ex: /sqlar?r=%t&name=%t */
543
+
544
+ if( !forceFlag ){
545
+ if( zSqlArchive ){
546
+ if( file_isdir(zSqlArchive, ExtFILE)>0 ){
547
+ fossil_fatal("file already exists: \"%s\"", zSqlArchive);
548
+ }
549
+ }else if( file_isdir(zDest, ExtFILE)>0 ){
550
+ if( fossil_strcmp(zDest,".")==0 ){
551
+ if( file_directory_size(zDest,0,1) ){
552
+ fossil_fatal("current directory is not empty");
553
+ }
554
+ }else{
555
+ fossil_fatal("\"%s\" already exists", zDest);
556
+ }
557
+ }
558
+ }
559
+
560
+ /* Construct a subpath on the URL if necessary */
561
+ if( g.url.isSsh || g.url.isFile ){
562
+ g.url.subpath = mprintf("/sqlar/%t/%t.sqlar", zVers, zDest);
563
+ }else{
564
+ g.url.subpath = mprintf("%s/sqlar/%t/%t.sqlar", g.url.path, zVers, zDest);
565
+ }
566
+
567
+ if( bDebug ){
568
+ urlparse_print(0);
569
+ }
570
+
571
+ /* Fetch the ZIP archive for the requested check-in */
572
+ blob_init(&in, 0, 0);
573
+ blob_init(&out, 0, 0);
574
+ if( bDebug ) mHttpFlags |= HTTP_VERBOSE;
575
+ if( bQuiet ) mHttpFlags |= HTTP_QUIET;
576
+ rc = http_exchange(&in, &out, mHttpFlags, 4, 0);
577
+ if( rc
578
+ || out.nUsed<512
579
+ || (out.nUsed%512)!=0
580
+ || memcmp(out.aData,"SQLite format 3",16)!=0
581
+ ){
582
+ fossil_fatal("Server did not return the requested check-in.");
583
+ }
584
+
585
+ if( zSqlArchive ){
586
+ blob_write_to_file(&out, zSqlArchive);
587
+ if( bVerbose ) fossil_print("%s\n", zSqlArchive);
588
+ return;
589
+ }
590
+
591
+ rc = sqlite3_open(":memory:", &db);
592
+ if( rc==SQLITE_OK ){
593
+ int sz = blob_size(&out);
594
+ rc = sqlite3_deserialize(db, 0, (unsigned char*)blob_buffer(&out), sz, sz,
595
+ SQLITE_DESERIALIZE_READONLY);
596
+ }
597
+ if( rc!=SQLITE_OK ){
598
+ fossil_fatal("Cannot create an in-memory database: %s",
599
+ sqlite3_errmsg(db));
600
+ }
601
+ zSql = mprintf("SELECT name, mode, sz, data FROM sqlar"
602
+ " WHERE name GLOB '%q*'", zDest);
603
+ rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
604
+ fossil_free(zSql);
605
+ if( rc!=0 ){
606
+ fossil_fatal("SQL error: %s\n", sqlite3_errmsg(db));
607
+ }
608
+ blob_init(&file, 0, 0);
609
+ while( sqlite3_step(pStmt)==SQLITE_ROW ){
610
+ const char *zFilename = (const char*)sqlite3_column_text(pStmt, 0);
611
+ int mode = sqlite3_column_int(pStmt, 1);
612
+ int sz = sqlite3_column_int(pStmt, 2);
613
+ if( bList ){
614
+ fossil_print("%s\n", zFilename);
615
+ }else if( mode & 0x4000 ){
616
+ /* A directory name */
617
+ nDir++;
618
+ file_mkdir(zFilename, ExtFILE, 1);
619
+ }else{
620
+ /* A file */
621
+ unsigned char *inBuf = (unsigned char*)sqlite3_column_blob(pStmt,3);
622
+ unsigned int nIn = (unsigned int)sqlite3_column_bytes(pStmt,3);
623
+ unsigned long int nOut2 = (unsigned long int)sz;
624
+ nFile++;
625
+ nByte += sz;
626
+ blob_resize(&file, sz);
627
+ if( nIn<sz ){
628
+ rc = uncompress((unsigned char*)blob_buffer(&file), &nOut2,
629
+ inBuf, nIn);
630
+ if( rc!=Z_OK ){
631
+ fossil_fatal("Failed to uncompress file %s", zFilename);
632
+ }
633
+ }else{
634
+ memcpy(blob_buffer(&file), inBuf, sz);
635
+ }
636
+ blob_write_to_file(&file, zFilename);
637
+ if( mode & 0x40 ){
638
+ file_setexe(zFilename, 1);
639
+ }
640
+ blob_zero(&file);
641
+ if( bVerbose ){
642
+ fossil_print("%s\n", zFilename);
643
+ }
644
+ }
645
+ }
646
+ sqlite3_finalize(pStmt);
647
+ sqlite3_close(db);
648
+ blob_zero(&out);
649
+ if( !bVerbose && !bQuiet && nFile>0 && zDest ){
650
+ fossil_print("%d files (%,lld bytes) written into %s",
651
+ nFile, nByte, zDest);
652
+ if( nDir>1 ){
653
+ fossil_print(" and %d subdirectories\n", nDir-1);
654
+ }else{
655
+ fossil_print("\n");
656
+ }
657
+ }
658
+}
434659
--- src/checkout.c
+++ src/checkout.c
@@ -19,10 +19,11 @@
19 ** from the local repository.
20 */
21 #include "config.h"
22 #include "checkout.h"
23 #include <assert.h>
 
24
25 /*
26 ** Check to see if there is an existing check-out that has been
27 ** modified. Return values:
28 **
@@ -429,5 +430,229 @@
429 }
430 unlink_local_database(1);
431 db_close(1);
432 unlink_local_database(0);
433 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
434
--- src/checkout.c
+++ src/checkout.c
@@ -19,10 +19,11 @@
19 ** from the local repository.
20 */
21 #include "config.h"
22 #include "checkout.h"
23 #include <assert.h>
24 #include <zlib.h>
25
26 /*
27 ** Check to see if there is an existing check-out that has been
28 ** modified. Return values:
29 **
@@ -429,5 +430,229 @@
430 }
431 unlink_local_database(1);
432 db_close(1);
433 unlink_local_database(0);
434 }
435
436
437 /*
438 ** COMMAND: get
439 **
440 ** Usage: %fossil get URL ?VERSION? ?OPTIONS?
441 **
442 ** Download a single check-in from a remote repository named URL and
443 ** unpack all of the files locally. The check-in is identified by VERSION.
444 **
445 ** URL can be a traditional URL like one of:
446 **
447 ** * https://domain.com/project
448 ** * ssh://my-server/project.fossil
449 ** * file:/home/user/Fossils/project.fossil
450 **
451 ** Or URL can be just the name of a local repository without the "file:"
452 ** prefix.
453 **
454 ** This command works by downloading an SQL archive of the requested
455 ** check-in and then extracting all the files from the archive.
456 **
457 ** Options:
458 ** --dest DIRECTORY Extract files into DIRECTORY. Use "--dest ."
459 ** to extract into the local directory.
460 **
461 ** -f|--force Overwrite existing files
462 **
463 ** --list List all the files that would have been checked
464 ** out but do not actually write anything to the
465 ** filesystem.
466 **
467 ** --sqlar ARCHIVE Store the check-out in an SQL-archive rather
468 ** than unpacking them into separate files.
469 **
470 ** -v|--verbose Show all files as they are extracted
471 */
472 void get_cmd(void){
473 int forceFlag = find_option("force","f",0)!=0;
474 int bVerbose = find_option("verbose","v",0)!=0;
475 int bQuiet = find_option("quiet","q",0)!=0;
476 int bDebug = find_option("debug",0,0)!=0;
477 int bList = find_option("list",0,0)!=0;
478 const char *zSqlArchive = find_option("sqlar",0,1);
479 const char *z;
480 char *zDest = 0; /* Where to store results */
481 char *zSql; /* SQL used to query the results */
482 const char *zUrl; /* Url to get */
483 const char *zVers; /* Version name to get */
484 unsigned int mHttpFlags = HTTP_GENERIC|HTTP_NOCOMPRESS;
485 Blob in, out; /* I/O for the HTTP request */
486 Blob file; /* A file to extract */
487 sqlite3 *db; /* Database containing downloaded sqlar */
488 sqlite3_stmt *pStmt; /* Statement for querying the database */
489 int rc; /* Result of subroutine calls */
490 int nFile = 0; /* Number of files written */
491 int nDir = 0; /* Number of directories written */
492 i64 nByte = 0; /* Number of bytes written */
493
494 z = find_option("dest",0,1);
495 if( z ) zDest = fossil_strdup(z);
496 verify_all_options();
497 if( g.argc<3 || g.argc>4 ){
498 usage("get URL ?VERSION? ?OPTIONS?");
499 }
500 zUrl = g.argv[2];
501 zVers = g.argc==4 ? g.argv[3] : "trunk";
502
503 /* Parse the URL of the repository */
504 url_parse(zUrl, 0);
505
506 /* Construct an appropriate name for the destination directory */
507 if( zDest==0 ){
508 int i;
509 const char *zTail;
510 const char *zDot;
511 int n;
512 if( g.url.isFile ){
513 zTail = file_tail(g.url.name);
514 }else{
515 zTail = file_tail(g.url.path);
516 }
517 zDot = strchr(zTail,'.');
518 if( zDot==0 ) zDot = zTail+strlen(zTail);
519 n = (int)(zDot - zTail);
520 zDest = mprintf("%.*s-%s", n, zTail, zVers);
521 for(i=0; zDest[i]; i++){
522 char c = zDest[i];
523 if( !fossil_isalnum(c) && c!='-' && c!='^' && c!='~' && c!='_' ){
524 zDest[i] = '-';
525 }
526 }
527 }
528 if( bDebug ){
529 fossil_print("dest = %s\n", zDest);
530 }
531
532 /* Error checking */
533 if( zDest!=file_tail(zDest) ){
534 fossil_fatal("--dest must be a simple directory name, not a path");
535 }
536 if( zVers!=file_tail(zVers) ){
537 fossil_fatal("The \"fossil get\" command does not current work with"
538 " version names that contain \"/\". This will be fixed in"
539 " a future release.");
540 }
541 /* To relax the restrictions above, change the subpath URL formula below
542 ** to use query parameters. Ex: /sqlar?r=%t&name=%t */
543
544 if( !forceFlag ){
545 if( zSqlArchive ){
546 if( file_isdir(zSqlArchive, ExtFILE)>0 ){
547 fossil_fatal("file already exists: \"%s\"", zSqlArchive);
548 }
549 }else if( file_isdir(zDest, ExtFILE)>0 ){
550 if( fossil_strcmp(zDest,".")==0 ){
551 if( file_directory_size(zDest,0,1) ){
552 fossil_fatal("current directory is not empty");
553 }
554 }else{
555 fossil_fatal("\"%s\" already exists", zDest);
556 }
557 }
558 }
559
560 /* Construct a subpath on the URL if necessary */
561 if( g.url.isSsh || g.url.isFile ){
562 g.url.subpath = mprintf("/sqlar/%t/%t.sqlar", zVers, zDest);
563 }else{
564 g.url.subpath = mprintf("%s/sqlar/%t/%t.sqlar", g.url.path, zVers, zDest);
565 }
566
567 if( bDebug ){
568 urlparse_print(0);
569 }
570
571 /* Fetch the ZIP archive for the requested check-in */
572 blob_init(&in, 0, 0);
573 blob_init(&out, 0, 0);
574 if( bDebug ) mHttpFlags |= HTTP_VERBOSE;
575 if( bQuiet ) mHttpFlags |= HTTP_QUIET;
576 rc = http_exchange(&in, &out, mHttpFlags, 4, 0);
577 if( rc
578 || out.nUsed<512
579 || (out.nUsed%512)!=0
580 || memcmp(out.aData,"SQLite format 3",16)!=0
581 ){
582 fossil_fatal("Server did not return the requested check-in.");
583 }
584
585 if( zSqlArchive ){
586 blob_write_to_file(&out, zSqlArchive);
587 if( bVerbose ) fossil_print("%s\n", zSqlArchive);
588 return;
589 }
590
591 rc = sqlite3_open(":memory:", &db);
592 if( rc==SQLITE_OK ){
593 int sz = blob_size(&out);
594 rc = sqlite3_deserialize(db, 0, (unsigned char*)blob_buffer(&out), sz, sz,
595 SQLITE_DESERIALIZE_READONLY);
596 }
597 if( rc!=SQLITE_OK ){
598 fossil_fatal("Cannot create an in-memory database: %s",
599 sqlite3_errmsg(db));
600 }
601 zSql = mprintf("SELECT name, mode, sz, data FROM sqlar"
602 " WHERE name GLOB '%q*'", zDest);
603 rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
604 fossil_free(zSql);
605 if( rc!=0 ){
606 fossil_fatal("SQL error: %s\n", sqlite3_errmsg(db));
607 }
608 blob_init(&file, 0, 0);
609 while( sqlite3_step(pStmt)==SQLITE_ROW ){
610 const char *zFilename = (const char*)sqlite3_column_text(pStmt, 0);
611 int mode = sqlite3_column_int(pStmt, 1);
612 int sz = sqlite3_column_int(pStmt, 2);
613 if( bList ){
614 fossil_print("%s\n", zFilename);
615 }else if( mode & 0x4000 ){
616 /* A directory name */
617 nDir++;
618 file_mkdir(zFilename, ExtFILE, 1);
619 }else{
620 /* A file */
621 unsigned char *inBuf = (unsigned char*)sqlite3_column_blob(pStmt,3);
622 unsigned int nIn = (unsigned int)sqlite3_column_bytes(pStmt,3);
623 unsigned long int nOut2 = (unsigned long int)sz;
624 nFile++;
625 nByte += sz;
626 blob_resize(&file, sz);
627 if( nIn<sz ){
628 rc = uncompress((unsigned char*)blob_buffer(&file), &nOut2,
629 inBuf, nIn);
630 if( rc!=Z_OK ){
631 fossil_fatal("Failed to uncompress file %s", zFilename);
632 }
633 }else{
634 memcpy(blob_buffer(&file), inBuf, sz);
635 }
636 blob_write_to_file(&file, zFilename);
637 if( mode & 0x40 ){
638 file_setexe(zFilename, 1);
639 }
640 blob_zero(&file);
641 if( bVerbose ){
642 fossil_print("%s\n", zFilename);
643 }
644 }
645 }
646 sqlite3_finalize(pStmt);
647 sqlite3_close(db);
648 blob_zero(&out);
649 if( !bVerbose && !bQuiet && nFile>0 && zDest ){
650 fossil_print("%d files (%,lld bytes) written into %s",
651 nFile, nByte, zDest);
652 if( nDir>1 ){
653 fossil_print(" and %d subdirectories\n", nDir-1);
654 }else{
655 fossil_print("\n");
656 }
657 }
658 }
659
+27 -10
--- src/http.c
+++ src/http.c
@@ -141,15 +141,22 @@
141141
Blob *pHdr, /* construct the header here */
142142
Blob *pLogin, /* Login card header value or NULL */
143143
const char *zAltMimetype /* Alternative mimetype */
144144
){
145145
int nPayload = pPayload ? blob_size(pPayload) : 0;
146
+ const char *zPath;
146147
147148
blob_zero(pHdr);
149
+ if( g.url.subpath ){
150
+ zPath = g.url.subpath;
151
+ }else if( g.url.path==0 || g.url.path[0]==0 ){
152
+ zPath = "/";
153
+ }else{
154
+ zPath = g.url.path;
155
+ }
148156
blob_appendf(pHdr, "%s %s HTTP/1.0\r\n",
149
- nPayload>0 ? "POST" : "GET",
150
- (g.url.path && g.url.path[0]) ? g.url.path : "/");
157
+ nPayload>0 ? "POST" : "GET", zPath);
151158
if( g.url.proxyAuth ){
152159
blob_appendf(pHdr, "Proxy-Authorization: %s\r\n", g.url.proxyAuth);
153160
}
154161
if( g.zHttpAuth && g.zHttpAuth[0] ){
155162
const char *zCredentials = g.zHttpAuth;
@@ -459,10 +466,11 @@
459466
/* Activate the PATH= auxiliary argument to the ssh command if that
460467
** is called for.
461468
*/
462469
if( g.url.isSsh
463470
&& (g.url.flags & URL_SSH_RETRY)==0
471
+ && g.db!=0
464472
&& ssh_needs_path_argument(g.url.hostname, -1)
465473
){
466474
g.url.flags |= URL_SSH_PATH;
467475
}
468476
@@ -678,19 +686,21 @@
678686
&& (g.url.flags & URL_SSH_EXE)==0 /* Does not have ?fossil=.... */
679687
&& (g.url.flags & URL_SSH_RETRY)==0 /* Not retried already */
680688
){
681689
/* Retry after flipping the SSH_PATH setting */
682690
transport_close(&g.url);
683
- fossil_print(
684
- "First attempt to run fossil on %s using SSH failed.\n"
685
- "Retrying %s the PATH= argument.\n",
686
- g.url.hostname,
687
- (g.url.flags & URL_SSH_PATH)!=0 ? "without" : "with"
688
- );
691
+ if( (mHttpFlags & HTTP_QUIET)==0 ){
692
+ fossil_print(
693
+ "First attempt to run fossil on %s using SSH failed.\n"
694
+ "Retrying %s the PATH= argument.\n",
695
+ g.url.hostname,
696
+ (g.url.flags & URL_SSH_PATH)!=0 ? "without" : "with"
697
+ );
698
+ }
689699
g.url.flags ^= URL_SSH_PATH|URL_SSH_RETRY;
690700
rc = http_exchange(pSend,pReply,mHttpFlags,0,zAltMimetype);
691
- if( rc==0 ){
701
+ if( rc==0 && g.db!=0 ){
692702
(void)ssh_needs_path_argument(g.url.hostname,
693703
(g.url.flags & URL_SSH_PATH)!=0);
694704
}
695705
return rc;
696706
}else{
@@ -808,17 +818,19 @@
808818
** Options:
809819
** --compress Use ZLIB compression on the payload
810820
** --mimetype TYPE Mimetype of the payload
811821
** --no-cert-verify Disable TLS cert verification
812822
** --out FILE Store the reply in FILE
823
+** --subpath PATH HTTP request path for ssh: and file: URLs
813824
** -v Verbose output
814825
** --xfer PAYLOAD in a Fossil xfer protocol message
815826
*/
816827
void test_httpmsg_command(void){
817828
const char *zMimetype;
818829
const char *zInFile;
819830
const char *zOutFile;
831
+ const char *zSubpath;
820832
Blob in, out;
821833
unsigned int mHttpFlags = HTTP_GENERIC|HTTP_NOCOMPRESS;
822834
823835
zMimetype = find_option("mimetype",0,1);
824836
zOutFile = find_option("out","o",1);
@@ -832,10 +844,11 @@
832844
if( find_option("xfer",0,0)!=0 ){
833845
mHttpFlags |= HTTP_USE_LOGIN;
834846
mHttpFlags &= ~HTTP_GENERIC;
835847
}
836848
if( find_option("ipv4",0,0) ) g.fIPv4 = 1;
849
+ zSubpath = find_option("subpath",0,1);
837850
verify_all_options();
838851
if( g.argc<3 || g.argc>5 ){
839852
usage("URL ?PAYLOAD? ?OUTPUT?");
840853
}
841854
zInFile = g.argc>=4 ? g.argv[3] : 0;
@@ -846,11 +859,15 @@
846859
}
847860
zOutFile = g.argv[4];
848861
}
849862
url_parse(g.argv[2], 0);
850863
if( g.url.protocol[0]!='h' ){
851
- fossil_fatal("the %s command supports only http: and https:", g.argv[1]);
864
+ if( zSubpath==0 ){
865
+ fossil_fatal("the --subpath option is required for %s://",g.url.protocol);
866
+ }else{
867
+ g.url.subpath = fossil_strdup(zSubpath);
868
+ }
852869
}
853870
if( zInFile ){
854871
blob_read_from_file(&in, zInFile, ExtFILE);
855872
if( zMimetype==0 && (mHttpFlags & HTTP_GENERIC)!=0 ){
856873
if( fossil_strcmp(zInFile,"-")==0 ){
857874
--- src/http.c
+++ src/http.c
@@ -141,15 +141,22 @@
141 Blob *pHdr, /* construct the header here */
142 Blob *pLogin, /* Login card header value or NULL */
143 const char *zAltMimetype /* Alternative mimetype */
144 ){
145 int nPayload = pPayload ? blob_size(pPayload) : 0;
 
146
147 blob_zero(pHdr);
 
 
 
 
 
 
 
148 blob_appendf(pHdr, "%s %s HTTP/1.0\r\n",
149 nPayload>0 ? "POST" : "GET",
150 (g.url.path && g.url.path[0]) ? g.url.path : "/");
151 if( g.url.proxyAuth ){
152 blob_appendf(pHdr, "Proxy-Authorization: %s\r\n", g.url.proxyAuth);
153 }
154 if( g.zHttpAuth && g.zHttpAuth[0] ){
155 const char *zCredentials = g.zHttpAuth;
@@ -459,10 +466,11 @@
459 /* Activate the PATH= auxiliary argument to the ssh command if that
460 ** is called for.
461 */
462 if( g.url.isSsh
463 && (g.url.flags & URL_SSH_RETRY)==0
 
464 && ssh_needs_path_argument(g.url.hostname, -1)
465 ){
466 g.url.flags |= URL_SSH_PATH;
467 }
468
@@ -678,19 +686,21 @@
678 && (g.url.flags & URL_SSH_EXE)==0 /* Does not have ?fossil=.... */
679 && (g.url.flags & URL_SSH_RETRY)==0 /* Not retried already */
680 ){
681 /* Retry after flipping the SSH_PATH setting */
682 transport_close(&g.url);
683 fossil_print(
684 "First attempt to run fossil on %s using SSH failed.\n"
685 "Retrying %s the PATH= argument.\n",
686 g.url.hostname,
687 (g.url.flags & URL_SSH_PATH)!=0 ? "without" : "with"
688 );
 
 
689 g.url.flags ^= URL_SSH_PATH|URL_SSH_RETRY;
690 rc = http_exchange(pSend,pReply,mHttpFlags,0,zAltMimetype);
691 if( rc==0 ){
692 (void)ssh_needs_path_argument(g.url.hostname,
693 (g.url.flags & URL_SSH_PATH)!=0);
694 }
695 return rc;
696 }else{
@@ -808,17 +818,19 @@
808 ** Options:
809 ** --compress Use ZLIB compression on the payload
810 ** --mimetype TYPE Mimetype of the payload
811 ** --no-cert-verify Disable TLS cert verification
812 ** --out FILE Store the reply in FILE
 
813 ** -v Verbose output
814 ** --xfer PAYLOAD in a Fossil xfer protocol message
815 */
816 void test_httpmsg_command(void){
817 const char *zMimetype;
818 const char *zInFile;
819 const char *zOutFile;
 
820 Blob in, out;
821 unsigned int mHttpFlags = HTTP_GENERIC|HTTP_NOCOMPRESS;
822
823 zMimetype = find_option("mimetype",0,1);
824 zOutFile = find_option("out","o",1);
@@ -832,10 +844,11 @@
832 if( find_option("xfer",0,0)!=0 ){
833 mHttpFlags |= HTTP_USE_LOGIN;
834 mHttpFlags &= ~HTTP_GENERIC;
835 }
836 if( find_option("ipv4",0,0) ) g.fIPv4 = 1;
 
837 verify_all_options();
838 if( g.argc<3 || g.argc>5 ){
839 usage("URL ?PAYLOAD? ?OUTPUT?");
840 }
841 zInFile = g.argc>=4 ? g.argv[3] : 0;
@@ -846,11 +859,15 @@
846 }
847 zOutFile = g.argv[4];
848 }
849 url_parse(g.argv[2], 0);
850 if( g.url.protocol[0]!='h' ){
851 fossil_fatal("the %s command supports only http: and https:", g.argv[1]);
 
 
 
 
852 }
853 if( zInFile ){
854 blob_read_from_file(&in, zInFile, ExtFILE);
855 if( zMimetype==0 && (mHttpFlags & HTTP_GENERIC)!=0 ){
856 if( fossil_strcmp(zInFile,"-")==0 ){
857
--- src/http.c
+++ src/http.c
@@ -141,15 +141,22 @@
141 Blob *pHdr, /* construct the header here */
142 Blob *pLogin, /* Login card header value or NULL */
143 const char *zAltMimetype /* Alternative mimetype */
144 ){
145 int nPayload = pPayload ? blob_size(pPayload) : 0;
146 const char *zPath;
147
148 blob_zero(pHdr);
149 if( g.url.subpath ){
150 zPath = g.url.subpath;
151 }else if( g.url.path==0 || g.url.path[0]==0 ){
152 zPath = "/";
153 }else{
154 zPath = g.url.path;
155 }
156 blob_appendf(pHdr, "%s %s HTTP/1.0\r\n",
157 nPayload>0 ? "POST" : "GET", zPath);
 
158 if( g.url.proxyAuth ){
159 blob_appendf(pHdr, "Proxy-Authorization: %s\r\n", g.url.proxyAuth);
160 }
161 if( g.zHttpAuth && g.zHttpAuth[0] ){
162 const char *zCredentials = g.zHttpAuth;
@@ -459,10 +466,11 @@
466 /* Activate the PATH= auxiliary argument to the ssh command if that
467 ** is called for.
468 */
469 if( g.url.isSsh
470 && (g.url.flags & URL_SSH_RETRY)==0
471 && g.db!=0
472 && ssh_needs_path_argument(g.url.hostname, -1)
473 ){
474 g.url.flags |= URL_SSH_PATH;
475 }
476
@@ -678,19 +686,21 @@
686 && (g.url.flags & URL_SSH_EXE)==0 /* Does not have ?fossil=.... */
687 && (g.url.flags & URL_SSH_RETRY)==0 /* Not retried already */
688 ){
689 /* Retry after flipping the SSH_PATH setting */
690 transport_close(&g.url);
691 if( (mHttpFlags & HTTP_QUIET)==0 ){
692 fossil_print(
693 "First attempt to run fossil on %s using SSH failed.\n"
694 "Retrying %s the PATH= argument.\n",
695 g.url.hostname,
696 (g.url.flags & URL_SSH_PATH)!=0 ? "without" : "with"
697 );
698 }
699 g.url.flags ^= URL_SSH_PATH|URL_SSH_RETRY;
700 rc = http_exchange(pSend,pReply,mHttpFlags,0,zAltMimetype);
701 if( rc==0 && g.db!=0 ){
702 (void)ssh_needs_path_argument(g.url.hostname,
703 (g.url.flags & URL_SSH_PATH)!=0);
704 }
705 return rc;
706 }else{
@@ -808,17 +818,19 @@
818 ** Options:
819 ** --compress Use ZLIB compression on the payload
820 ** --mimetype TYPE Mimetype of the payload
821 ** --no-cert-verify Disable TLS cert verification
822 ** --out FILE Store the reply in FILE
823 ** --subpath PATH HTTP request path for ssh: and file: URLs
824 ** -v Verbose output
825 ** --xfer PAYLOAD in a Fossil xfer protocol message
826 */
827 void test_httpmsg_command(void){
828 const char *zMimetype;
829 const char *zInFile;
830 const char *zOutFile;
831 const char *zSubpath;
832 Blob in, out;
833 unsigned int mHttpFlags = HTTP_GENERIC|HTTP_NOCOMPRESS;
834
835 zMimetype = find_option("mimetype",0,1);
836 zOutFile = find_option("out","o",1);
@@ -832,10 +844,11 @@
844 if( find_option("xfer",0,0)!=0 ){
845 mHttpFlags |= HTTP_USE_LOGIN;
846 mHttpFlags &= ~HTTP_GENERIC;
847 }
848 if( find_option("ipv4",0,0) ) g.fIPv4 = 1;
849 zSubpath = find_option("subpath",0,1);
850 verify_all_options();
851 if( g.argc<3 || g.argc>5 ){
852 usage("URL ?PAYLOAD? ?OUTPUT?");
853 }
854 zInFile = g.argc>=4 ? g.argv[3] : 0;
@@ -846,11 +859,15 @@
859 }
860 zOutFile = g.argv[4];
861 }
862 url_parse(g.argv[2], 0);
863 if( g.url.protocol[0]!='h' ){
864 if( zSubpath==0 ){
865 fossil_fatal("the --subpath option is required for %s://",g.url.protocol);
866 }else{
867 g.url.subpath = fossil_strdup(zSubpath);
868 }
869 }
870 if( zInFile ){
871 blob_read_from_file(&in, zInFile, ExtFILE);
872 if( zMimetype==0 && (mHttpFlags & HTTP_GENERIC)!=0 ){
873 if( fossil_strcmp(zInFile,"-")==0 ){
874
+4
--- src/main.c
+++ src/main.c
@@ -3101,10 +3101,11 @@
31013101
** breaking legacy.
31023102
**
31033103
** Options:
31043104
** --csrf-safe N Set cgi_csrf_safe() to to return N
31053105
** --nobody Pretend to be user "nobody"
3106
+** --ssh-sim Pretend to be over an SSH connection
31063107
** --test Do not do special "sync" processing when operating
31073108
** over an SSH link
31083109
** --th-trace Trace TH1 execution (for debugging purposes)
31093110
** --usercap CAP User capability string (Default: "sxy")
31103111
*/
@@ -3112,10 +3113,13 @@
31123113
const char *zIpAddr; /* IP address of remote client */
31133114
const char *zUserCap;
31143115
int bTest = 0;
31153116
const char *zCsrfSafe = find_option("csrf-safe",0,1);
31163117
3118
+ if( find_option("ssh-sim",0,0)!=0 ){
3119
+ putenv("SSH_CONNECTION=127.0.0.1 12345 127.0.0.2 23456");
3120
+ }
31173121
Th_InitTraceLog();
31183122
if( zCsrfSafe ) g.okCsrf = atoi(zCsrfSafe);
31193123
zUserCap = find_option("usercap",0,1);
31203124
if( !find_option("nobody",0,0) ){
31213125
if( zUserCap==0 ){
31223126
--- src/main.c
+++ src/main.c
@@ -3101,10 +3101,11 @@
3101 ** breaking legacy.
3102 **
3103 ** Options:
3104 ** --csrf-safe N Set cgi_csrf_safe() to to return N
3105 ** --nobody Pretend to be user "nobody"
 
3106 ** --test Do not do special "sync" processing when operating
3107 ** over an SSH link
3108 ** --th-trace Trace TH1 execution (for debugging purposes)
3109 ** --usercap CAP User capability string (Default: "sxy")
3110 */
@@ -3112,10 +3113,13 @@
3112 const char *zIpAddr; /* IP address of remote client */
3113 const char *zUserCap;
3114 int bTest = 0;
3115 const char *zCsrfSafe = find_option("csrf-safe",0,1);
3116
 
 
 
3117 Th_InitTraceLog();
3118 if( zCsrfSafe ) g.okCsrf = atoi(zCsrfSafe);
3119 zUserCap = find_option("usercap",0,1);
3120 if( !find_option("nobody",0,0) ){
3121 if( zUserCap==0 ){
3122
--- src/main.c
+++ src/main.c
@@ -3101,10 +3101,11 @@
3101 ** breaking legacy.
3102 **
3103 ** Options:
3104 ** --csrf-safe N Set cgi_csrf_safe() to to return N
3105 ** --nobody Pretend to be user "nobody"
3106 ** --ssh-sim Pretend to be over an SSH connection
3107 ** --test Do not do special "sync" processing when operating
3108 ** over an SSH link
3109 ** --th-trace Trace TH1 execution (for debugging purposes)
3110 ** --usercap CAP User capability string (Default: "sxy")
3111 */
@@ -3112,10 +3113,13 @@
3113 const char *zIpAddr; /* IP address of remote client */
3114 const char *zUserCap;
3115 int bTest = 0;
3116 const char *zCsrfSafe = find_option("csrf-safe",0,1);
3117
3118 if( find_option("ssh-sim",0,0)!=0 ){
3119 putenv("SSH_CONNECTION=127.0.0.1 12345 127.0.0.2 23456");
3120 }
3121 Th_InitTraceLog();
3122 if( zCsrfSafe ) g.okCsrf = atoi(zCsrfSafe);
3123 zUserCap = find_option("usercap",0,1);
3124 if( !find_option("nobody",0,0) ){
3125 if( zUserCap==0 ){
3126
+3
--- src/url.c
+++ src/url.c
@@ -58,10 +58,11 @@
5858
char *user; /* User id for http: */
5959
char *passwd; /* Password for http: */
6060
char *canonical; /* Canonical representation of the URL */
6161
char *proxyAuth; /* Proxy-Authorizer: string */
6262
char *fossil; /* The fossil query parameter on ssh: */
63
+ char *subpath; /* Secondary HTTP request path for ssh: and file: */
6364
char *pwConfig; /* CONFIG table entry that gave us the password */
6465
unsigned flags; /* Boolean flags controlling URL processing */
6566
int useProxy; /* Used to remember that a proxy is in use */
6667
int proxyOrigPort; /* Tunneled port number for https through proxy */
6768
char *proxyUrlPath; /* Remember path when proxy is use */
@@ -406,10 +407,11 @@
406407
fossil_free(p->name);
407408
fossil_free(p->path);
408409
fossil_free(p->user);
409410
fossil_free(p->passwd);
410411
fossil_free(p->fossil);
412
+ fossil_free(p->subpath);
411413
fossil_free(p->pwConfig);
412414
memset(p, 0, sizeof(*p));
413415
}
414416
415417
/*
@@ -483,10 +485,11 @@
483485
fossil_print("g.url.passwd = ************\n");
484486
}
485487
fossil_print("g.url.pwConfig = %s\n", g.url.pwConfig);
486488
fossil_print("g.url.canonical = %s\n", g.url.canonical);
487489
fossil_print("g.url.fossil = %s\n", g.url.fossil);
490
+ fossil_print("g.url.subpath = %s\n", g.url.subpath);
488491
fossil_print("g.url.flags = 0x%04x\n", g.url.flags);
489492
fossil_print("url_full(g.url) = %z\n", url_full(&g.url));
490493
}
491494
492495
/*
493496
--- src/url.c
+++ src/url.c
@@ -58,10 +58,11 @@
58 char *user; /* User id for http: */
59 char *passwd; /* Password for http: */
60 char *canonical; /* Canonical representation of the URL */
61 char *proxyAuth; /* Proxy-Authorizer: string */
62 char *fossil; /* The fossil query parameter on ssh: */
 
63 char *pwConfig; /* CONFIG table entry that gave us the password */
64 unsigned flags; /* Boolean flags controlling URL processing */
65 int useProxy; /* Used to remember that a proxy is in use */
66 int proxyOrigPort; /* Tunneled port number for https through proxy */
67 char *proxyUrlPath; /* Remember path when proxy is use */
@@ -406,10 +407,11 @@
406 fossil_free(p->name);
407 fossil_free(p->path);
408 fossil_free(p->user);
409 fossil_free(p->passwd);
410 fossil_free(p->fossil);
 
411 fossil_free(p->pwConfig);
412 memset(p, 0, sizeof(*p));
413 }
414
415 /*
@@ -483,10 +485,11 @@
483 fossil_print("g.url.passwd = ************\n");
484 }
485 fossil_print("g.url.pwConfig = %s\n", g.url.pwConfig);
486 fossil_print("g.url.canonical = %s\n", g.url.canonical);
487 fossil_print("g.url.fossil = %s\n", g.url.fossil);
 
488 fossil_print("g.url.flags = 0x%04x\n", g.url.flags);
489 fossil_print("url_full(g.url) = %z\n", url_full(&g.url));
490 }
491
492 /*
493
--- src/url.c
+++ src/url.c
@@ -58,10 +58,11 @@
58 char *user; /* User id for http: */
59 char *passwd; /* Password for http: */
60 char *canonical; /* Canonical representation of the URL */
61 char *proxyAuth; /* Proxy-Authorizer: string */
62 char *fossil; /* The fossil query parameter on ssh: */
63 char *subpath; /* Secondary HTTP request path for ssh: and file: */
64 char *pwConfig; /* CONFIG table entry that gave us the password */
65 unsigned flags; /* Boolean flags controlling URL processing */
66 int useProxy; /* Used to remember that a proxy is in use */
67 int proxyOrigPort; /* Tunneled port number for https through proxy */
68 char *proxyUrlPath; /* Remember path when proxy is use */
@@ -406,10 +407,11 @@
407 fossil_free(p->name);
408 fossil_free(p->path);
409 fossil_free(p->user);
410 fossil_free(p->passwd);
411 fossil_free(p->fossil);
412 fossil_free(p->subpath);
413 fossil_free(p->pwConfig);
414 memset(p, 0, sizeof(*p));
415 }
416
417 /*
@@ -483,10 +485,11 @@
485 fossil_print("g.url.passwd = ************\n");
486 }
487 fossil_print("g.url.pwConfig = %s\n", g.url.pwConfig);
488 fossil_print("g.url.canonical = %s\n", g.url.canonical);
489 fossil_print("g.url.fossil = %s\n", g.url.fossil);
490 fossil_print("g.url.subpath = %s\n", g.url.subpath);
491 fossil_print("g.url.flags = 0x%04x\n", g.url.flags);
492 fossil_print("url_full(g.url) = %z\n", url_full(&g.url));
493 }
494
495 /*
496
+6 -9
--- src/zip.c
+++ src/zip.c
@@ -406,16 +406,16 @@
406406
sqlite3_exec(p->db,
407407
"PRAGMA page_size=512;"
408408
"PRAGMA journal_mode = off;"
409409
"PRAGMA cache_spill = off;"
410410
"BEGIN;"
411
- "CREATE TABLE sqlar("
412
- "name TEXT PRIMARY KEY, -- name of the file\n"
413
- "mode INT, -- access permissions\n"
414
- "mtime INT, -- last modification time\n"
415
- "sz INT, -- original file size\n"
416
- "data BLOB -- compressed content\n"
411
+ "CREATE TABLE sqlar(\n"
412
+ " name TEXT PRIMARY KEY, -- name of the file\n"
413
+ " mode INT, -- access permissions\n"
414
+ " mtime INT, -- last modification time\n"
415
+ " sz INT, -- original file size\n"
416
+ " data BLOB -- compressed content\n"
417417
");", 0, 0, 0
418418
);
419419
sqlite3_prepare(p->db,
420420
"INSERT INTO sqlar VALUES(?, ?, ?, ?, ?)", -1,
421421
&p->pInsert, 0
@@ -1031,13 +1031,10 @@
10311031
if( !g.perm.Zip ){ login_needed(g.anon.Zip); return; }
10321032
if( robot_restrict("zip") ) return;
10331033
if( fossil_strcmp(g.zPath, "sqlar")==0 ){
10341034
eType = ARCHIVE_SQLAR;
10351035
zType = "SQL";
1036
- /* For some reason, SQL-archives are like catnip for robots. So
1037
- ** don't allow them to be downloaded by user "nobody" */
1038
- if( g.zLogin==0 ){ login_needed(g.anon.Zip); return; }
10391036
}else{
10401037
eType = ARCHIVE_ZIP;
10411038
zType = "ZIP";
10421039
}
10431040
fossil_nice_default();
10441041
--- src/zip.c
+++ src/zip.c
@@ -406,16 +406,16 @@
406 sqlite3_exec(p->db,
407 "PRAGMA page_size=512;"
408 "PRAGMA journal_mode = off;"
409 "PRAGMA cache_spill = off;"
410 "BEGIN;"
411 "CREATE TABLE sqlar("
412 "name TEXT PRIMARY KEY, -- name of the file\n"
413 "mode INT, -- access permissions\n"
414 "mtime INT, -- last modification time\n"
415 "sz INT, -- original file size\n"
416 "data BLOB -- compressed content\n"
417 ");", 0, 0, 0
418 );
419 sqlite3_prepare(p->db,
420 "INSERT INTO sqlar VALUES(?, ?, ?, ?, ?)", -1,
421 &p->pInsert, 0
@@ -1031,13 +1031,10 @@
1031 if( !g.perm.Zip ){ login_needed(g.anon.Zip); return; }
1032 if( robot_restrict("zip") ) return;
1033 if( fossil_strcmp(g.zPath, "sqlar")==0 ){
1034 eType = ARCHIVE_SQLAR;
1035 zType = "SQL";
1036 /* For some reason, SQL-archives are like catnip for robots. So
1037 ** don't allow them to be downloaded by user "nobody" */
1038 if( g.zLogin==0 ){ login_needed(g.anon.Zip); return; }
1039 }else{
1040 eType = ARCHIVE_ZIP;
1041 zType = "ZIP";
1042 }
1043 fossil_nice_default();
1044
--- src/zip.c
+++ src/zip.c
@@ -406,16 +406,16 @@
406 sqlite3_exec(p->db,
407 "PRAGMA page_size=512;"
408 "PRAGMA journal_mode = off;"
409 "PRAGMA cache_spill = off;"
410 "BEGIN;"
411 "CREATE TABLE sqlar(\n"
412 " name TEXT PRIMARY KEY, -- name of the file\n"
413 " mode INT, -- access permissions\n"
414 " mtime INT, -- last modification time\n"
415 " sz INT, -- original file size\n"
416 " data BLOB -- compressed content\n"
417 ");", 0, 0, 0
418 );
419 sqlite3_prepare(p->db,
420 "INSERT INTO sqlar VALUES(?, ?, ?, ?, ?)", -1,
421 &p->pInsert, 0
@@ -1031,13 +1031,10 @@
1031 if( !g.perm.Zip ){ login_needed(g.anon.Zip); return; }
1032 if( robot_restrict("zip") ) return;
1033 if( fossil_strcmp(g.zPath, "sqlar")==0 ){
1034 eType = ARCHIVE_SQLAR;
1035 zType = "SQL";
 
 
 
1036 }else{
1037 eType = ARCHIVE_ZIP;
1038 zType = "ZIP";
1039 }
1040 fossil_nice_default();
1041

Keyboard Shortcuts

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