Fossil

Changes On Branch synclog
Login

Changes On Branch synclog

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Changes In Branch synclog Excluding Merge-Ins

This is equivalent to a diff from 3f94d1bbb5 to 5bb01585bc

2021-12-20
20:35
Add the "public_url()" internal interface for computing the canonical URL used to access the repository. Add a report about the canonical URL to the security_audit page. ... (check-in: 1865cf4ce2 user: drh tags: trunk)
17:02
Merge the diff enhancement from trunk. <b>Closed:</b> This branch was an experiment in ways of keeping better track of a network of repositories on different servers are set up to synchronize with one another. The experiment did not work out. ... (Closed-Leaf check-in: 5bb01585bc user: drh tags: synclog)
17:01
Futher improvements to longestCommonSubsequence that finds better matches using the faster heuristic before reverting to the optimal solver. ... (check-in: 3f94d1bbb5 user: drh tags: trunk)
16:10
In the diff generator, allow optimial-LCS runs to be 4 times as large. Fix for the issue reported at [forum:/forumpost/298bcd17cd|forum post 298bcd17cd] ... (check-in: 7a93baffa2 user: drh tags: trunk)
13:43
Make sure the synclog table exists when running the synclog command. ... (check-in: ce5802534c user: drh tags: synclog)

Changes to auto.def.

30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
    no-opt=0             => {Build without optimization}
    json=0               => {Build with fossil JSON API enabled}
}

# Update the minimum required SQLite version number here, and also
# in src/main.c near the sqlite3_libversion_number() call.  Take care
# that both places agree!
define MINIMUM_SQLITE_VERSION "3.37.0"

# This is useful for people wanting Fossil to use an external SQLite library
# to compare the one they have against the minimum required
if {[opt-bool print-minimum-sqlite-version]} {
    puts [get-define MINIMUM_SQLITE_VERSION]
    exit 0
}







|







30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
    no-opt=0             => {Build without optimization}
    json=0               => {Build with fossil JSON API enabled}
}

# Update the minimum required SQLite version number here, and also
# in src/main.c near the sqlite3_libversion_number() call.  Take care
# that both places agree!
define MINIMUM_SQLITE_VERSION "3.38.0"

# This is useful for people wanting Fossil to use an external SQLite library
# to compare the one they have against the minimum required
if {[opt-bool print-minimum-sqlite-version]} {
    puts [get-define MINIMUM_SQLITE_VERSION]
    exit 0
}

Changes to src/allrepo.c.

96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
**    git CMD     Do the "git export" or "git status" command (which every
**                is specified by CMD) on all repositories for which
**                a Git mirror has been previously established.
**
**    info        Run the "info" command on all repositories.
**
**    pull        Run a "pull" operation on all repositories.  Only the
**                --verbose option is supported.
**
**    push        Run a "push" on all repositories.  Only the --verbose
**                option is supported.
**
**    rebuild     Rebuild on all repositories.  The command line options
**                supported by the rebuild command itself, if any are
**                present, are passed along verbatim.  The --force and
**                --randomize options are not supported.
**
**    sync        Run a "sync" on all repositories.  Only the --verbose
**                and --unversioned options are supported.
**
**    set         Run the "setting" or "set" commands on all
**                repositories.  These command are particularly useful in
**                conjunction with the "max-loadavg" setting which cannot
**                otherwise be set globally.
**
**    unset       Run the "unset" command on all repositories







|


|






|
|







96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
**    git CMD     Do the "git export" or "git status" command (which every
**                is specified by CMD) on all repositories for which
**                a Git mirror has been previously established.
**
**    info        Run the "info" command on all repositories.
**
**    pull        Run a "pull" operation on all repositories.  Only the
**                --verbose and --synclog options are supported.
**
**    push        Run a "push" on all repositories.  Only the --verbose
**                and --synclog options is supported.
**
**    rebuild     Rebuild on all repositories.  The command line options
**                supported by the rebuild command itself, if any are
**                present, are passed along verbatim.  The --force and
**                --randomize options are not supported.
**
**    sync        Run a "sync" on all repositories.  Only the --verbose,
**                --unversioned, and --synclog options are supported.
**
**    set         Run the "setting" or "set" commands on all
**                repositories.  These command are particularly useful in
**                conjunction with the "max-loadavg" setting which cannot
**                otherwise be set globally.
**
**    unset       Run the "unset" command on all repositories
262
263
264
265
266
267
268

269
270
271

272
273
274
275
276
277
278
      }else{
        usage("git (export|status)");
      }
    }
  }else if( strncmp(zCmd, "push", n)==0 ){
    zCmd = "push -autourl -R";
    collect_argument(&extra, "verbose","v");

  }else if( strncmp(zCmd, "pull", n)==0 ){
    zCmd = "pull -autourl -R";
    collect_argument(&extra, "verbose","v");

  }else if( strncmp(zCmd, "rebuild", n)==0 ){
    zCmd = "rebuild";
    collect_argument(&extra, "cluster",0);
    collect_argument(&extra, "compress",0);
    collect_argument(&extra, "compress-only",0);
    collect_argument(&extra, "noverify",0);
    collect_argument_value(&extra, "pagesize");







>



>







262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
      }else{
        usage("git (export|status)");
      }
    }
  }else if( strncmp(zCmd, "push", n)==0 ){
    zCmd = "push -autourl -R";
    collect_argument(&extra, "verbose","v");
    collect_argument(&extra, "synclog",0);
  }else if( strncmp(zCmd, "pull", n)==0 ){
    zCmd = "pull -autourl -R";
    collect_argument(&extra, "verbose","v");
    collect_argument(&extra, "synclog",0);
  }else if( strncmp(zCmd, "rebuild", n)==0 ){
    zCmd = "rebuild";
    collect_argument(&extra, "cluster",0);
    collect_argument(&extra, "compress",0);
    collect_argument(&extra, "compress-only",0);
    collect_argument(&extra, "noverify",0);
    collect_argument_value(&extra, "pagesize");
293
294
295
296
297
298
299

300
301
302
303
304
305
306
  }else if( strncmp(zCmd, "fts-config", n)==0 ){
    zCmd = "fts-config -R";
    collect_argv(&extra, 3);
  }else if( strncmp(zCmd, "sync", n)==0 ){
    zCmd = "sync -autourl -R";
    collect_argument(&extra, "verbose","v");
    collect_argument(&extra, "unversioned","u");

  }else if( strncmp(zCmd, "test-integrity", n)==0 ){
    collect_argument(&extra, "db-only", "d");
    collect_argument(&extra, "parse", 0);
    collect_argument(&extra, "quick", "q");
    zCmd = "test-integrity";
  }else if( strncmp(zCmd, "test-orphans", n)==0 ){
    zCmd = "test-orphans -R";







>







295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
  }else if( strncmp(zCmd, "fts-config", n)==0 ){
    zCmd = "fts-config -R";
    collect_argv(&extra, 3);
  }else if( strncmp(zCmd, "sync", n)==0 ){
    zCmd = "sync -autourl -R";
    collect_argument(&extra, "verbose","v");
    collect_argument(&extra, "unversioned","u");
    collect_argument(&extra, "synclog",0);
  }else if( strncmp(zCmd, "test-integrity", n)==0 ){
    collect_argument(&extra, "db-only", "d");
    collect_argument(&extra, "parse", 0);
    collect_argument(&extra, "quick", "q");
    zCmd = "test-integrity";
  }else if( strncmp(zCmd, "test-orphans", n)==0 ){
    zCmd = "test-orphans -R";

Changes to src/capabilities.c.

98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
** if "u" is present and "developer" if "v" is present.
*/
void capability_expand(CapabilityString *pIn){
  static char *zNobody = 0;
  static char *zAnon = 0;
  static char *zReader = 0;
  static char *zDev = 0;
  static char *zAdmin = "bcdefghijklmnopqrtwz234567AD";
  int doneV = 0;

  if( pIn==0 ){
    fossil_free(zNobody); zNobody = 0;
    fossil_free(zAnon);   zAnon = 0;
    fossil_free(zReader); zReader = 0;
    fossil_free(zDev);    zDev = 0;







|







98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
** if "u" is present and "developer" if "v" is present.
*/
void capability_expand(CapabilityString *pIn){
  static char *zNobody = 0;
  static char *zAnon = 0;
  static char *zReader = 0;
  static char *zDev = 0;
  static char *zAdmin = "bcdefghijklmnopqrtwz234567ADEF";
  int doneV = 0;

  if( pIn==0 ){
    fossil_free(zNobody); zNobody = 0;
    fossil_free(zAnon);   zAnon = 0;
    fossil_free(zReader); zReader = 0;
    fossil_free(zDev);    zDev = 0;
306
307
308
309
310
311
312




313
314
315
316
317
318
319
    "Alerts", "Sign up for email alerts" },
  { 'A', CAPCLASS_ALERT|CAPCLASS_SUPER, 0,
    "Announce", "Send announcements to all subscribers" },
  { 'C', CAPCLASS_FORUM, 0,
    "Chat",  "Read and/or writes messages in the chatroom" },
  { 'D', CAPCLASS_OTHER, 0,
    "Debug", "Enable debugging features" },




};

/*
** Populate the aCap[].nUser values based on the current content
** of the USER table.
*/
void capabilities_count(void){







>
>
>
>







306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
    "Alerts", "Sign up for email alerts" },
  { 'A', CAPCLASS_ALERT|CAPCLASS_SUPER, 0,
    "Announce", "Send announcements to all subscribers" },
  { 'C', CAPCLASS_FORUM, 0,
    "Chat",  "Read and/or writes messages in the chatroom" },
  { 'D', CAPCLASS_OTHER, 0,
    "Debug", "Enable debugging features" },
  { 'E', CAPCLASS_SUPER, 0,
    "Read SyncLog", "Read the SyncLog" },
  { 'F', CAPCLASS_SUPER, 0,
    "Update SyncLog", "Add or modify entries in the SyncLog" },
};

/*
** Populate the aCap[].nUser values based on the current content
** of the USER table.
*/
void capabilities_count(void){

Changes to src/configure.c.

511
512
513
514
515
516
517

518
519
520
521
522
523
524
  int groupMask,           /* Mask of groups to be send */
  sqlite3_int64 iStart     /* Only write values changed since this time */
){
  Stmt q;
  Blob rec;
  int ii;
  int nCard = 0;


  blob_zero(&rec);
  if( groupMask & CONFIGSET_SHUN ){
    db_prepare(&q, "SELECT mtime, quote(uuid), quote(scom) FROM shun"
                   " WHERE mtime>=%lld", iStart);
    while( db_step(&q)==SQLITE_ROW ){
      blob_appendf(&rec,"%s %s scom %s",







>







511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
  int groupMask,           /* Mask of groups to be send */
  sqlite3_int64 iStart     /* Only write values changed since this time */
){
  Stmt q;
  Blob rec;
  int ii;
  int nCard = 0;
  int bPrepped;

  blob_zero(&rec);
  if( groupMask & CONFIGSET_SHUN ){
    db_prepare(&q, "SELECT mtime, quote(uuid), quote(scom) FROM shun"
                   " WHERE mtime>=%lld", iStart);
    while( db_step(&q)==SQLITE_ROW ){
      blob_appendf(&rec,"%s %s scom %s",
645
646
647
648
649
650
651
652
653
654
655





656
657
658
659
660
661
662
663
664
665
666
667
668
669
670

671

672
673
674
675
676
677
678
      blob_appendf(pOut, "config /subscriber %d\n%s\n",
                   blob_size(&rec), blob_str(&rec));
      nCard++;
      blob_reset(&rec);
    }
    db_finalize(&q);
  }
  db_prepare(&q, "SELECT mtime, quote(name), quote(value) FROM config"
                 " WHERE name=:name AND mtime>=%lld", iStart);
  for(ii=0; ii<count(aConfig); ii++){
    if( (aConfig[ii].groupMask & groupMask)!=0 && aConfig[ii].zName[0]!='@' ){





      db_bind_text(&q, ":name", aConfig[ii].zName);
      while( db_step(&q)==SQLITE_ROW ){
        blob_appendf(&rec,"%s %s value %s",
          db_column_text(&q, 0),
          db_column_text(&q, 1),
          db_column_text(&q, 2)
        );
        blob_appendf(pOut, "config /config %d\n%s\n",
                     blob_size(&rec), blob_str(&rec));
        nCard++;
        blob_reset(&rec);
      }
      db_reset(&q);
    }
  }

  db_finalize(&q);

  return nCard;
}

/*
** Identify a configuration group by name.  Return its mask.
** Throw an error if no match.
*/







|
<


>
>
>
>
>















>
|
>







646
647
648
649
650
651
652
653

654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
      blob_appendf(pOut, "config /subscriber %d\n%s\n",
                   blob_size(&rec), blob_str(&rec));
      nCard++;
      blob_reset(&rec);
    }
    db_finalize(&q);
  }
  bPrepped = 0;

  for(ii=0; ii<count(aConfig); ii++){
    if( (aConfig[ii].groupMask & groupMask)!=0 && aConfig[ii].zName[0]!='@' ){
      if( !bPrepped ){
        db_prepare(&q, "SELECT mtime, quote(name), quote(value) FROM config"
                       " WHERE name=:name AND mtime>=%lld", iStart);
        bPrepped = 1;
      }
      db_bind_text(&q, ":name", aConfig[ii].zName);
      while( db_step(&q)==SQLITE_ROW ){
        blob_appendf(&rec,"%s %s value %s",
          db_column_text(&q, 0),
          db_column_text(&q, 1),
          db_column_text(&q, 2)
        );
        blob_appendf(pOut, "config /config %d\n%s\n",
                     blob_size(&rec), blob_str(&rec));
        nCard++;
        blob_reset(&rec);
      }
      db_reset(&q);
    }
  }
  if( bPrepped ){
    db_finalize(&q);
  }
  return nCard;
}

/*
** Identify a configuration group by name.  Return its mask.
** Throw an error if no match.
*/

Changes to src/export.c.

1368
1369
1370
1371
1372
1373
1374

1375
1376
1377
1378
1379
1380
1381
** Implementation of the "fossil git export" command.
*/
void gitmirror_export_command(void){
  const char *zLimit;             /* Text of the --limit flag */
  int nLimit = 0x7fffffff;        /* Numeric value of the --limit flag */
  int nTotal = 0;                 /* Total number of check-ins to export */
  char *zMirror;                  /* Name of the mirror */

  char *z;                        /* Generic string */
  char *zCmd;                     /* git command to run as a subprocess */
  const char *zDebug = 0;         /* Value of the --debug flag */
  const char *zAutoPush = 0;      /* Value of the --autopush flag */
  char *zMainBr = 0;              /* Value of the --mainbranch flag */
  char *zPushUrl;                 /* URL to sync the mirror to */
  double rEnd;                    /* time of most recent export */







>







1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
** Implementation of the "fossil git export" command.
*/
void gitmirror_export_command(void){
  const char *zLimit;             /* Text of the --limit flag */
  int nLimit = 0x7fffffff;        /* Numeric value of the --limit flag */
  int nTotal = 0;                 /* Total number of check-ins to export */
  char *zMirror;                  /* Name of the mirror */
  char *zMirrorAbs;               /* Canonicalized name of the mirror */
  char *z;                        /* Generic string */
  char *zCmd;                     /* git command to run as a subprocess */
  const char *zDebug = 0;         /* Value of the --debug flag */
  const char *zAutoPush = 0;      /* Value of the --autopush flag */
  char *zMainBr = 0;              /* Value of the --mainbranch flag */
  char *zPushUrl;                 /* URL to sync the mirror to */
  double rEnd;                    /* time of most recent export */
1694
1695
1696
1697
1698
1699
1700





1701
1702
1703
1704
1705

1706
1707
1708
1709
1710
1711
1712

1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723

1724
1725
1726
1727
1728
1729
1730

  /* Maybe run a git repack */
  if( bNeedRepack ){
    const char *zRepack = "git repack -adf";
    gitmirror_message(VERB_NORMAL, "%s\n", zRepack);
    fossil_system(zRepack);
  }






  /* Optionally do a "git push" */
  zPushUrl = db_text(0, "SELECT value FROM mconfig WHERE key='autopush'");
  if( zPushUrl ){
    char *zPushCmd;

    UrlData url;
    if( sqlite3_strglob("http*", zPushUrl)==0 ){
      url_parse_local(zPushUrl, 0, &url);
      zPushCmd = mprintf("git push --mirror %s", url.canonical);
    }else{
      zPushCmd = mprintf("git push --mirror %s", zPushUrl);
    }

    gitmirror_message(VERB_NORMAL, "%s\n", zPushCmd);
    fossil_free(zPushCmd);
    zPushCmd = mprintf("git push --mirror %$", zPushUrl);
    rc = fossil_system(zPushCmd);
    if( rc ){
      fossil_fatal("cannot push content using: %s", zPushCmd);
    }else if( db_is_writeable("repository") ){
      db_unprotect(PROTECT_CONFIG);
      db_multi_exec("REPLACE INTO config(name,value,mtime)"
                    "VALUES('gitpush:%q',1,now())", zPushUrl);
      db_protect_pop();

    }
    fossil_free(zPushCmd);
  }
}

/*
** Implementation of the "fossil git status" command.







>
>
>
>
>





>



|

|

>











>







1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739

  /* Maybe run a git repack */
  if( bNeedRepack ){
    const char *zRepack = "git repack -adf";
    gitmirror_message(VERB_NORMAL, "%s\n", zRepack);
    fossil_system(zRepack);
  }

  /* Record this export into the sync log */
  zMirrorAbs = file_canonical_name_dup(zMirror);
  sync_log_entry("this", zMirrorAbs, 0, "git");
  fossil_free(zMirrorAbs);

  /* Optionally do a "git push" */
  zPushUrl = db_text(0, "SELECT value FROM mconfig WHERE key='autopush'");
  if( zPushUrl ){
    char *zPushCmd;
    const char *zUrl;
    UrlData url;
    if( sqlite3_strglob("http*", zPushUrl)==0 ){
      url_parse_local(zPushUrl, 0, &url);
      zUrl = url.canonical;
    }else{
      zUrl = zPushUrl;
    }
    zPushCmd = mprintf("git push --mirror %s", url.canonical);
    gitmirror_message(VERB_NORMAL, "%s\n", zPushCmd);
    fossil_free(zPushCmd);
    zPushCmd = mprintf("git push --mirror %$", zPushUrl);
    rc = fossil_system(zPushCmd);
    if( rc ){
      fossil_fatal("cannot push content using: %s", zPushCmd);
    }else if( db_is_writeable("repository") ){
      db_unprotect(PROTECT_CONFIG);
      db_multi_exec("REPLACE INTO config(name,value,mtime)"
                    "VALUES('gitpush:%q',1,now())", zPushUrl);
      db_protect_pop();
      sync_log_entry("this", zUrl, 0, "git-push");
    }
    fossil_free(zPushCmd);
  }
}

/*
** Implementation of the "fossil git status" command.

Changes to src/login.c.

1272
1273
1274
1275
1276
1277
1278
1279

1280
1281
1282
1283
1284
1285
1286
                             p->RdWiki = p->WrWiki = p->NewWiki =
                             p->ApndWiki = p->Hyperlink = p->Clone =
                             p->NewTkt = p->Password = p->RdAddr =
                             p->TktFmt = p->Attach = p->ApndTkt =
                             p->ModWiki = p->ModTkt =
                             p->RdForum = p->WrForum = p->ModForum =
                             p->WrTForum = p->AdminForum = p->Chat = 
                             p->EmailAlert = p->Announce = p->Debug = 1;

                             /* Fall thru into Read/Write */
      case 'i':   p->Read = p->Write = 1;                      break;
      case 'o':   p->Read = 1;                                 break;
      case 'z':   p->Zip = 1;                                  break;

      case 'h':   p->Hyperlink = 1;                            break;
      case 'g':   p->Clone = 1;                                break;







|
>







1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
                             p->RdWiki = p->WrWiki = p->NewWiki =
                             p->ApndWiki = p->Hyperlink = p->Clone =
                             p->NewTkt = p->Password = p->RdAddr =
                             p->TktFmt = p->Attach = p->ApndTkt =
                             p->ModWiki = p->ModTkt =
                             p->RdForum = p->WrForum = p->ModForum =
                             p->WrTForum = p->AdminForum = p->Chat = 
                             p->EmailAlert = p->Announce = p->Debug =
                             p->RdSLog = p->WrSLog = 1;
                             /* Fall thru into Read/Write */
      case 'i':   p->Read = p->Write = 1;                      break;
      case 'o':   p->Read = 1;                                 break;
      case 'z':   p->Zip = 1;                                  break;

      case 'h':   p->Hyperlink = 1;                            break;
      case 'g':   p->Clone = 1;                                break;
1310
1311
1312
1313
1314
1315
1316


1317
1318
1319
1320
1321
1322
1323
      case '3':   p->WrForum = 1;
      case '2':   p->RdForum = 1;                              break;

      case '7':   p->EmailAlert = 1;                           break;
      case 'A':   p->Announce = 1;                             break;
      case 'C':   p->Chat = 1;                                 break;
      case 'D':   p->Debug = 1;                                break;



      /* The "u" privilege recursively
      ** inherits all privileges of the user named "reader" */
      case 'u': {
        if( p->XReader==0 ){
          const char *zUser;
          p->XReader = 1;







>
>







1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
      case '3':   p->WrForum = 1;
      case '2':   p->RdForum = 1;                              break;

      case '7':   p->EmailAlert = 1;                           break;
      case 'A':   p->Announce = 1;                             break;
      case 'C':   p->Chat = 1;                                 break;
      case 'D':   p->Debug = 1;                                break;
      case 'E':   p->RdSLog = 1;                               break;
      case 'F':   p->WrSLog = 1;                               break;

      /* The "u" privilege recursively
      ** inherits all privileges of the user named "reader" */
      case 'u': {
        if( p->XReader==0 ){
          const char *zUser;
          p->XReader = 1;
1397
1398
1399
1400
1401
1402
1403


1404
1405
1406
1407
1408
1409
1410
      case '4':  rc = p->WrTForum;  break;
      case '5':  rc = p->ModForum;  break;
      case '6':  rc = p->AdminForum;break;
      case '7':  rc = p->EmailAlert;break;
      case 'A':  rc = p->Announce;  break;
      case 'C':  rc = p->Chat;      break;
      case 'D':  rc = p->Debug;     break;


      case 'L':  rc = g.zLogin && *g.zLogin; break;
      /* Mainenance reminder: '@' should not be used because
         it would semantically collide with the @ in the
         capexpr TH1 command. */
      default:   rc = 0;            break;
    }
  }







>
>







1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
      case '4':  rc = p->WrTForum;  break;
      case '5':  rc = p->ModForum;  break;
      case '6':  rc = p->AdminForum;break;
      case '7':  rc = p->EmailAlert;break;
      case 'A':  rc = p->Announce;  break;
      case 'C':  rc = p->Chat;      break;
      case 'D':  rc = p->Debug;     break;
      case 'E':  rc = p->RdSLog;    break;
      case 'F':  rc = p->WrSLog;    break;
      case 'L':  rc = g.zLogin && *g.zLogin; break;
      /* Mainenance reminder: '@' should not be used because
         it would semantically collide with the @ in the
         capexpr TH1 command. */
      default:   rc = 0;            break;
    }
  }

Changes to src/main.c.

107
108
109
110
111
112
113


114
115
116
117
118
119
120
  char WrTForum;         /* 4: Post to forums not subject to moderation */
  char ModForum;         /* 5: Moderate (approve or reject) forum posts */
  char AdminForum;       /* 6: Grant capability 4 to other users */
  char EmailAlert;       /* 7: Sign up for email notifications */
  char Announce;         /* A: Send announcements */
  char Chat;             /* C: read or write the chatroom */
  char Debug;            /* D: show extra Fossil debugging features */


  /* These last two are included to block infinite recursion */
  char XReader;          /* u: Inherit all privileges of "reader" */
  char XDeveloper;       /* v: Inherit all privileges of "developer" */
};

#ifdef FOSSIL_ENABLE_TCL
/*







>
>







107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
  char WrTForum;         /* 4: Post to forums not subject to moderation */
  char ModForum;         /* 5: Moderate (approve or reject) forum posts */
  char AdminForum;       /* 6: Grant capability 4 to other users */
  char EmailAlert;       /* 7: Sign up for email notifications */
  char Announce;         /* A: Send announcements */
  char Chat;             /* C: read or write the chatroom */
  char Debug;            /* D: show extra Fossil debugging features */
  char RdSLog;           /* E: read the synclog */
  char WrSLog;           /* F: add to the synclog */
  /* These last two are included to block infinite recursion */
  char XReader;          /* u: Inherit all privileges of "reader" */
  char XDeveloper;       /* v: Inherit all privileges of "developer" */
};

#ifdef FOSSIL_ENABLE_TCL
/*
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
  fossil_printf_selfcheck();
  fossil_limit_memory(1);

  /* When updating the minimum SQLite version, change the number here,
  ** and also MINIMUM_SQLITE_VERSION value set in ../auto.def.  Take
  ** care that both places agree! */
  if( sqlite3_libversion_number()<3037000 ){
    fossil_panic("Unsuitable SQLite version %s, must be at least 3.37.0",
                 sqlite3_libversion());
  }

  sqlite3_config(SQLITE_CONFIG_MULTITHREAD);
  sqlite3_config(SQLITE_CONFIG_LOG, fossil_sqlite_log, 0);
  memset(&g, 0, sizeof(g));
  g.now = time(0);







|







699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
  fossil_printf_selfcheck();
  fossil_limit_memory(1);

  /* When updating the minimum SQLite version, change the number here,
  ** and also MINIMUM_SQLITE_VERSION value set in ../auto.def.  Take
  ** care that both places agree! */
  if( sqlite3_libversion_number()<3037000 ){
    fossil_panic("Unsuitable SQLite version %s, must be at least 3.38.0",
                 sqlite3_libversion());
  }

  sqlite3_config(SQLITE_CONFIG_MULTITHREAD);
  sqlite3_config(SQLITE_CONFIG_LOG, fossil_sqlite_log, 0);
  memset(&g, 0, sizeof(g));
  g.now = time(0);

Changes to src/rebuild.c.

390
391
392
393
394
395
396
397
398
399

400
401
402
403
404
405
406
  db_prepare(&q,
     "SELECT name FROM sqlite_schema /*scan*/"
     " WHERE type='table'"
     " AND name NOT IN ('admin_log', 'blob','delta','rcvfrom','user','alias',"
                       "'config','shun','private','reportfmt',"
                       "'concealed','accesslog','modreq',"
                       "'purgeevent','purgeitem','unversioned',"
                       "'subscriber','pending_alert','chat')"
     " AND name NOT GLOB 'sqlite_*'"
     " AND name NOT GLOB 'fx_*'"

  );
  while( db_step(&q)==SQLITE_ROW ){
    blob_appendf(&sql, "DROP TABLE IF EXISTS \"%w\";\n", db_column_text(&q,0));
  }
  db_finalize(&q);
  db_multi_exec("%s", blob_str(&sql)/*safe-for-%s*/);
  blob_reset(&sql);







|


>







390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
  db_prepare(&q,
     "SELECT name FROM sqlite_schema /*scan*/"
     " WHERE type='table'"
     " AND name NOT IN ('admin_log', 'blob','delta','rcvfrom','user','alias',"
                       "'config','shun','private','reportfmt',"
                       "'concealed','accesslog','modreq',"
                       "'purgeevent','purgeitem','unversioned',"
                       "'subscriber','pending_alert','chat','synclog')"
     " AND name NOT GLOB 'sqlite_*'"
     " AND name NOT GLOB 'fx_*'"
     " AND name NOT GLOB 'ftsidx_*'"
  );
  while( db_step(&q)==SQLITE_ROW ){
    blob_appendf(&sql, "DROP TABLE IF EXISTS \"%w\";\n", db_column_text(&q,0));
  }
  db_finalize(&q);
  db_multi_exec("%s", blob_str(&sql)/*safe-for-%s*/);
  blob_reset(&sql);
943
944
945
946
947
948
949

950
951
952
953
954
955
956
        "DROP TABLE IF EXISTS accesslog;\n"
        "UPDATE user SET photo=NULL, info='';\n"
        "DROP TABLE IF EXISTS purgeevent;\n"
        "DROP TABLE IF EXISTS purgeitem;\n"
        "DROP TABLE IF EXISTS admin_log;\n"
        "DROP TABLE IF EXISTS vcache;\n"
        "DROP TABLE IF EXISTS chat;\n"

      );
    }
    db_protect_pop();
  }
  if( !bNeedRebuild ){
    db_end_transaction(0);
    db_unprotect(PROTECT_ALL);







>







944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
        "DROP TABLE IF EXISTS accesslog;\n"
        "UPDATE user SET photo=NULL, info='';\n"
        "DROP TABLE IF EXISTS purgeevent;\n"
        "DROP TABLE IF EXISTS purgeitem;\n"
        "DROP TABLE IF EXISTS admin_log;\n"
        "DROP TABLE IF EXISTS vcache;\n"
        "DROP TABLE IF EXISTS chat;\n"
        "DROP TABLE IF EXISTS synclog;\n"
      );
    }
    db_protect_pop();
  }
  if( !bNeedRebuild ){
    db_end_transaction(0);
    db_unprotect(PROTECT_ALL);

Changes to src/schema.c.

616
617
618
619
620
621
622























/* Create the forum-post schema if it does not already exist */
void schema_forum(void){
  if( !db_table_exists("repository","forumpost") ){
    db_multi_exec("%s",zForumSchema/*safe-for-%s*/);
  }
}





























>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644

/* Create the forum-post schema if it does not already exist */
void schema_forum(void){
  if( !db_table_exists("repository","forumpost") ){
    db_multi_exec("%s",zForumSchema/*safe-for-%s*/);
  }
}

/*
** The following table holds information about servers with which
** this repository has synced, as well as servers with which those 
** servers have synced, and so forth.
*/
static const char zSynclogSchema[] =
@ CREATE TABLE repository.synclog(
@   sfrom TEXT,          -- Sync client. "self" means this repo
@   sto TEXT,            -- Sync server
@   stime INT NOT NULL,  -- Time of transfer (unixepoch)
@   stype TEXT,          -- "push", "pull", "git", "backup", "import", etc...
@   PRIMARY KEY(sfrom,sto)
@ ) WITHOUT ROWID;
;

/* Create the forum-post schema if it does not already exist */
void schema_synclog(void){
  if( !db_table_exists("repository","synclog") ){
    db_multi_exec("%s",zSynclogSchema/*safe-for-%s*/);
  }
}

Changes to src/security_audit.c.

97
98
99
100
101
102
103

104
105
106
107
108
109
110
void secaudit0_page(void){
  const char *zAnonCap;      /* Capabilities of user "anonymous" and "nobody" */
  const char *zDevCap;       /* Capabilities of user group "developer" */
  const char *zReadCap;      /* Capabilities of user group "reader" */
  const char *zPubPages;     /* GLOB pattern for public pages */
  const char *zSelfCap;      /* Capabilities of self-registered users */
  int hasSelfReg = 0;        /* True if able to self-register */

  char *z;
  int n;
  CapabilityString *pCap;
  char **azCSP;              /* Parsed content security policy */

  login_check_credentials();
  if( !g.perm.Admin ){







>







97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
void secaudit0_page(void){
  const char *zAnonCap;      /* Capabilities of user "anonymous" and "nobody" */
  const char *zDevCap;       /* Capabilities of user group "developer" */
  const char *zReadCap;      /* Capabilities of user group "reader" */
  const char *zPubPages;     /* GLOB pattern for public pages */
  const char *zSelfCap;      /* Capabilities of self-registered users */
  int hasSelfReg = 0;        /* True if able to self-register */
  const char *zPublicUrl;    /* Canonical access URL */
  char *z;
  int n;
  CapabilityString *pCap;
  char **azCSP;              /* Parsed content security policy */

  login_check_credentials();
  if( !g.perm.Admin ){
199
200
201
202
203
204
205






























206
207
208
209
210
211
212
    }
    @ </ul>
    if( zPubPages && zPubPages[0] ){
      @ <p>Change GLOB patterns exceptions using the "Public pages" setting
      @ on the <a href="setup_access">Access Settings</a> page.</p>
    }
  }































  /* Make sure the HTTPS is required for login, at least, so that the
  ** password does not go across the Internet in the clear.
  */
  if( db_get_int("redirect-to-https",0)==0 ){
    @ <li><p><b>WARNING:</b>
    @ Sensitive material such as login passwords can be sent over an







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
    }
    @ </ul>
    if( zPubPages && zPubPages[0] ){
      @ <p>Change GLOB patterns exceptions using the "Public pages" setting
      @ on the <a href="setup_access">Access Settings</a> page.</p>
    }
  }

  zPublicUrl = public_url();
  if( zPublicUrl!=0 ){
    int nOther = db_int(0, "SELECT count(*) FROM config"
                           " WHERE name GLOB 'baseurl:*'"
                           " AND name<>'baseurl:%q'", zPublicUrl);
    @ <li><p>The canonical URL for this repository is
    @ <a href="%s(zPublicUrl)">%h(zPublicUrl)</a>.
    if( nOther==1 ){
      @ This is also <a href="urllist?urlonly">1 other URL</a> that has
      @ been used to access this repository.
    }else if( nOther>=2 ){
      @ There are also
      @ <a href="urllist?all&urlonly">%d(nOther) other URLs</a> that have
      @ been used to access this repository.
    }
  }else{
    int nUrl = db_int(0, "SELECT count(*) FROM config"
                         " WHERE name GLOB 'baseurl:*'");
    @ <li><p>This repository does not have a canonical access URL.
    if( nUrl==1 ){
      @ There 1
      @ <a href="urllist?urlonly">1 non-canonical URLs</a>
      @ that has been used to access this repository.
    }else if( nUrl>=2 ){
      @ There are
      @ <a href="urllist?all&urlonly">%d(nUrl) non-canonical URLs</a>
      @ that have been used to access this repository.
    }
  }

  /* Make sure the HTTPS is required for login, at least, so that the
  ** password does not go across the Internet in the clear.
  */
  if( db_get_int("redirect-to-https",0)==0 ){
    @ <li><p><b>WARNING:</b>
    @ Sensitive material such as login passwords can be sent over an

Changes to src/setupuser.c.

689
690
691
692
693
694
695




696
697
698
699
700
701
702
  @  Email Alerts%s(B('7'))</label>
  @  <li><label><input type="checkbox" name="aA"%s(oa['A']) />
  @  Send Announcements%s(B('A'))</label>
  @  <li><label><input type="checkbox" name="aC"%s(oa['C']) />
  @  Chatroom%s(B('C'))</label>
  @  <li><label><input type="checkbox" name="aD"%s(oa['D']) />
  @  Enable Debug%s(B('D'))</label>




  @ </ul></div>
  @   </td>
  @ </tr>
  @ <tr>
  @   <td class="usetupEditLabel">Selected Cap:</td>
  @   <td>
  @     <span id="usetupEditCapability">(missing JS?)</span>







>
>
>
>







689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
  @  Email Alerts%s(B('7'))</label>
  @  <li><label><input type="checkbox" name="aA"%s(oa['A']) />
  @  Send Announcements%s(B('A'))</label>
  @  <li><label><input type="checkbox" name="aC"%s(oa['C']) />
  @  Chatroom%s(B('C'))</label>
  @  <li><label><input type="checkbox" name="aD"%s(oa['D']) />
  @  Enable Debug%s(B('D'))</label>
  @  <li><label><input type="checkbox" name="aE"%s(oa['E']) />
  @  Read SyncLog%s(B('E'))</label>
  @  <li><label><input type="checkbox" name="aF"%s(oa['F']) />
  @  Update SyncLog%s(B('F'))</label>
  @ </ul></div>
  @   </td>
  @ </tr>
  @ <tr>
  @   <td class="usetupEditLabel">Selected Cap:</td>
  @   <td>
  @     <span id="usetupEditCapability">(missing JS?)</span>

Changes to src/stat.c.

470
471
472
473
474
475
476





















477
478
479
480
481
482
483
      g.argv = newArgv;
      g.argc = 2;
      fossil_print("Full repository verification follows:\n");
      test_integrity();
    }
  }
}






















/*
** WEBPAGE: urllist
**
** Show ways in which this repository has been accessed
*/
void urllist_page(void){







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
      g.argv = newArgv;
      g.argc = 2;
      fossil_print("Full repository verification follows:\n");
      test_integrity();
    }
  }
}

/*
** Return a string which is the public URL used to access this repository.
** Or return a NULL pointer if this repository does not have a public
** access URL.
**
** Algorithm:
**
** The public URL is given by the email-url property.  But it is only
** returned if there have been one more more accesses (as recorded by
** "baseurl:URL" entries in the CONFIG table).
*/
const char *public_url(void){
  const char *zUrl = db_get("email-url", 0);
  if( zUrl==0 ) return 0;
  if( !db_exists("SELECT 1 FROM config WHERE name='baseurl:%q'", zUrl) ){
    return 0;
  }
  return zUrl;
}


/*
** WEBPAGE: urllist
**
** Show ways in which this repository has been accessed
*/
void urllist_page(void){
514
515
516
517
518
519
520


























































































521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539


540
541
542
543
544
545
546
  db_finalize(&q);
  if( cnt==0 ){
    @ <tr><td>(none)</td>
  }else if( nOmitted ){
    @ <tr><td><a href="urllist?all"><i>Show %d(nOmitted) more...</i></a>
  }
  @ </table>


























































































  db_prepare(&q, "SELECT substr(name,7), datetime(mtime,'unixepoch')"
                 "  FROM config WHERE name GLOB 'ckout:*' ORDER BY 2 DESC");
  cnt = 0;
  while( db_step(&q)==SQLITE_ROW ){
    const char *zPath = db_column_text(&q,0);
    if( vfile_top_of_checkout(zPath) ){
      if( cnt==0 ){
        @ <div class="section">Checkouts</div>
        @ <table border="0" width='100%%'>
      }
      @ <tr><td width='100%%'>%h(zPath)</td>
      @ <td><nobr>%h(db_column_text(&q,1))</nobr></td></tr>
      cnt++;
    }
  }
  db_finalize(&q);
  if( cnt ){
    @ </table>
  }


  cnt = 0;
  db_prepare(&q,
    "SELECT substr(name,10), datetime(mtime,'unixepoch')"
    "  FROM config WHERE name GLOB 'syncwith:*'"
    "UNION ALL "
    "SELECT substr(name,10), datetime(mtime,'unixepoch')"
    "  FROM config WHERE name GLOB 'syncfrom:*'"







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>



















>
>







535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
  db_finalize(&q);
  if( cnt==0 ){
    @ <tr><td>(none)</td>
  }else if( nOmitted ){
    @ <tr><td><a href="urllist?all"><i>Show %d(nOmitted) more...</i></a>
  }
  @ </table>
  if( P("urlonly") ){
    style_finish_page();
    return;
  }

  if( db_table_exists("repository","synclog") ){
    /* This code derived from the "synclog" command in "sync.c" */
    const int nIndent = 2;
    db_prepare(&q,
      "WITH allpull(xfrom,xto,xtime) AS MATERIALIZED (\n"
      "  SELECT sfrom, sto, max(stime) FROM synclog GROUP BY 1\n"
      "),\n"
      "pull(level, url, mtime, ex) AS (\n"
      "  SELECT 0, xfrom, xtime, '|this|' || xfrom || '|'\n"
      "    FROM allpull WHERE xto='this'\n"
      "  UNION\n"
      "  SELECT level+1, xfrom, xtime, ex || xfrom || '|'\n"
      "    FROM pull, allpull\n"
      "   WHERE xto=url\n"
      "     AND ex NOT GLOB ('*|' || xfrom || '|*')\n"
      "   ORDER BY 1 DESC, 3 DESC\n"
      ")\n"
      "SELECT level, url, datetime(mtime,'auto') FROM pull"
    );
    cnt = 0;
    while( db_step(&q)==SQLITE_ROW ){
      int iLevel = db_column_int(&q,0)*nIndent;
      const char *zUrl = db_column_text(&q,1);
      const char *zDate = db_column_text(&q,2);
      if( cnt==0 ){
        @ <div class="section">Pulls</div>
        @ <table border="0" width='100%%'>
      }
      if( strncmp(zUrl,"http",4)==0 ){
        @ <tr><td width='100%%'><span style='margin-left:%d(iLevel)em'>\
        @ <a href='%s(zUrl)'>%h(zUrl)</a></td>\
        @ <td><nobr>%h(zDate)</nobr></td></tr>
      }else{
        @ <tr><td width='100%%'><span style='margin-left:%d(iLevel)em'>\
        @ %h(zUrl)</td><td><nobr>%h(zDate)</nobr></td></tr>
      }
      cnt++;
    }
    db_finalize(&q);
    if( cnt ){
      @ </table>
    }

    db_prepare(&q,
      "WITH allpush(xfrom,xto,xtime) AS MATERIALIZED (\n"
      "  SELECT sfrom, sto, max(stime) FROM synclog GROUP BY 2\n"
      "),\n"
      "push(level, url, mtime, ex) AS (\n"
      "  SELECT 0, xto, xtime, '|this|' || xto || '|'\n"
      "    FROM allpush WHERE xfrom='this'\n"
      "  UNION\n"
      "  SELECT level+1, xto, xtime, ex || xto || '|'\n"
      "    FROM push, allpush\n"
      "   WHERE xfrom=url\n"
      "     AND ex NOT GLOB ('*|' || xto || '|*')\n"
      "   ORDER BY 1 DESC, 3 DESC\n"
      ")\n"
      "SELECT level, url, datetime(mtime,'auto') FROM push"
    );
    cnt = 0;
    while( db_step(&q)==SQLITE_ROW ){
      int iLevel = db_column_int(&q,0)*nIndent;
      const char *zUrl = db_column_text(&q,1);
      const char *zDate = db_column_text(&q,2);
      if( cnt==0 ){
        @ <div class="section">Pushes</div>
        @ <table border="0" width='100%%'>
      }
      if( strncmp(zUrl,"http",4)==0 ){
        @ <tr><td width='100%%'><span style='margin-left:%d(iLevel)em'>\
        @ <a href='%s(zUrl)'>%h(zUrl)</a></td>\
        @ <td><nobr>%h(zDate)</nobr></td></tr>
      }else{
        @ <tr><td width='100%%'><span style='margin-left:%d(iLevel)em'>\
        @ %h(zUrl)</td><td><nobr>%h(zDate)</nobr></td></tr>
      }
      cnt++;
    }
    db_finalize(&q);
    if( cnt ){
      @ </table>
    }
  }


  db_prepare(&q, "SELECT substr(name,7), datetime(mtime,'unixepoch')"
                 "  FROM config WHERE name GLOB 'ckout:*' ORDER BY 2 DESC");
  cnt = 0;
  while( db_step(&q)==SQLITE_ROW ){
    const char *zPath = db_column_text(&q,0);
    if( vfile_top_of_checkout(zPath) ){
      if( cnt==0 ){
        @ <div class="section">Checkouts</div>
        @ <table border="0" width='100%%'>
      }
      @ <tr><td width='100%%'>%h(zPath)</td>
      @ <td><nobr>%h(db_column_text(&q,1))</nobr></td></tr>
      cnt++;
    }
  }
  db_finalize(&q);
  if( cnt ){
    @ </table>
  }


  cnt = 0;
  db_prepare(&q,
    "SELECT substr(name,10), datetime(mtime,'unixepoch')"
    "  FROM config WHERE name GLOB 'syncwith:*'"
    "UNION ALL "
    "SELECT substr(name,10), datetime(mtime,'unixepoch')"
    "  FROM config WHERE name GLOB 'syncfrom:*'"

Changes to src/sync.c.

90
91
92
93
94
95
96
































































97
98
99
100
101
102
103
    fossil_free(azOther[i]);
    azOther[i] = 0;
  }
  fossil_free(azOther);
  return nErr;
}


































































/*
** If the repository is configured for autosyncing, then do an
** autosync.  Bits of the "flags" parameter determine details of behavior:
**
**   SYNC_PULL           Pull content from the server to the local repo
**   SYNC_PUSH           Push content from local up to the server







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
    fossil_free(azOther[i]);
    azOther[i] = 0;
  }
  fossil_free(azOther);
  return nErr;
}

/*
** Modify the URL to remove the username/password.
*/
static void remove_url_username(char *z){
  int i, j;
  for(i=0; z[i] && z[i]!='/'; i++){}
  if( z[i+1]!='/' ) return;
  i += 2;
  for(j=i; z[j] && z[j]!='@'; j++){}
  if( z[j]==0 ) return;
  j++;
  do{
    z[i++] = z[j];
  }while( z[j++]!=0 );
}

/*
** Make a new entry, or update an existing entry, in the SYNCLOG table.
**
** For an ordinary push/pull, zType is NULL.  But it may also be a string
** describing non-standard operations.  For example zType might be "git"
** when doing a "fossil git export", or zType might be "import" when doing
** a "fossil pull --from-parent-project".
**
** Usernames are stripped from the zFrom and zTo URLs
*/
void sync_log_entry(
  const char *zFrom,        /* Content comes from this machine */
  const char *zTo,          /* Content goes to this machine */
  i64 iTime,                /* Transfer time, or 0 for "now" */  
  const char *zType         /* Type of sync.  NULL for normal */
){
  char *zFree1 = 0;
  char *zFree2 = 0;
  schema_synclog();
  if( sqlite3_strglob("http*://*@*", zFrom)==0 ){
    zFree1 = fossil_strdup(zFrom);
    remove_url_username(zFree1);
    zFrom = zFree1;
  }
  if( sqlite3_strglob("http*://*@*", zTo)==0 ){
    zFree2 = fossil_strdup(zTo);
    remove_url_username(zFree2);
    zTo = zFree2;
  }
  if( iTime<=0 ){
    db_multi_exec(
      "INSERT INTO repository.synclog(sfrom,sto,stime,stype)"
      " VALUES(%Q,%Q,unixepoch(),%Q)"
      " ON CONFLICT DO UPDATE SET stime=unixepoch()",
      zFrom, zTo, zType
    );
  }else{
    db_multi_exec(
      "INSERT INTO repository.synclog(sfrom,sto,stime,stype)"
      " VALUES(%Q,%Q,%lld,%Q)"
      " ON CONFLICT DO UPDATE SET stime=%lld WHERE stime<%lld",
      zFrom, zTo, iTime, zType, iTime, iTime
    );
  }
  fossil_free(zFree1);
  fossil_free(zFree2);
}


/*
** If the repository is configured for autosyncing, then do an
** autosync.  Bits of the "flags" parameter determine details of behavior:
**
**   SYNC_PULL           Pull content from the server to the local repo
**   SYNC_PUSH           Push content from local up to the server
230
231
232
233
234
235
236



237
238
239
240
241
242
243
  }
  if( find_option("no-http-compression",0,0)!=0 ){
    *pSyncFlags |= SYNC_NOHTTPCOMPRESS;
  }
  if( find_option("all",0,0)!=0 ){
    *pSyncFlags |= SYNC_ALLURL;
  }



  url_proxy_options();
  clone_ssh_find_options();
  if( !uvOnly ) db_find_and_open_repository(0, 0);
  db_open_config(0, 1);
  if( g.argc==2 ){
    if( db_get_boolean("auto-shun",0) ) configSync = CONFIGSET_SHUN;
  }else if( g.argc==3 ){







>
>
>







294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
  }
  if( find_option("no-http-compression",0,0)!=0 ){
    *pSyncFlags |= SYNC_NOHTTPCOMPRESS;
  }
  if( find_option("all",0,0)!=0 ){
    *pSyncFlags |= SYNC_ALLURL;
  }
  if( find_option("synclog",0,0)!=0 ){
    *pSyncFlags |= SYNC_PUSH_SYNCLOG;
  }
  url_proxy_options();
  clone_ssh_find_options();
  if( !uvOnly ) db_find_and_open_repository(0, 0);
  db_open_config(0, 1);
  if( g.argc==2 ){
    if( db_get_boolean("auto-shun",0) ) configSync = CONFIGSET_SHUN;
  }else if( g.argc==3 ){
294
295
296
297
298
299
300

301
302
303
304
305
306
307
**   --once                     Do not remember URL for subsequent syncs
**   --private                  Pull private branches too
**   --project-code CODE        Use CODE as the project code
**   --proxy PROXY              Use the specified HTTP proxy
**   -R|--repository REPO       Local repository to pull into
**   --ssl-identity FILE        Local SSL credentials, if requested by remote
**   --ssh-command SSH          Use SSH as the "ssh" command

**   -v|--verbose               Additional (debugging) output
**   --verily                   Exchange extra information with the remote
**                              to ensure no content is overlooked
**
** See also: [[clone]], [[config]], [[push]], [[remote]], [[sync]]
*/
void pull_cmd(void){







>







361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
**   --once                     Do not remember URL for subsequent syncs
**   --private                  Pull private branches too
**   --project-code CODE        Use CODE as the project code
**   --proxy PROXY              Use the specified HTTP proxy
**   -R|--repository REPO       Local repository to pull into
**   --ssl-identity FILE        Local SSL credentials, if requested by remote
**   --ssh-command SSH          Use SSH as the "ssh" command
**   --synclog                  Push local synclog information to the server
**   -v|--verbose               Additional (debugging) output
**   --verily                   Exchange extra information with the remote
**                              to ensure no content is overlooked
**
** See also: [[clone]], [[config]], [[push]], [[remote]], [[sync]]
*/
void pull_cmd(void){
345
346
347
348
349
350
351

352
353
354
355
356
357
358
**   --no-http-compression      Do not compress HTTP traffic
**   --once                     Do not remember URL for subsequent syncs
**   --proxy PROXY              Use the specified HTTP proxy
**   --private                  Push private branches too
**   -R|--repository REPO       Local repository to push from
**   --ssl-identity FILE        Local SSL credentials, if requested by remote
**   --ssh-command SSH          Use SSH as the "ssh" command

**   -v|--verbose               Additional (debugging) output
**   --verily                   Exchange extra information with the remote
**                              to ensure no content is overlooked
**
** See also: [[clone]], [[config]], [[pull]], [[remote]], [[sync]]
*/
void push_cmd(void){







>







413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
**   --no-http-compression      Do not compress HTTP traffic
**   --once                     Do not remember URL for subsequent syncs
**   --proxy PROXY              Use the specified HTTP proxy
**   --private                  Push private branches too
**   -R|--repository REPO       Local repository to push from
**   --ssl-identity FILE        Local SSL credentials, if requested by remote
**   --ssh-command SSH          Use SSH as the "ssh" command
**   --synclog                  Push local synclog information to the server
**   -v|--verbose               Additional (debugging) output
**   --verily                   Exchange extra information with the remote
**                              to ensure no content is overlooked
**
** See also: [[clone]], [[config]], [[pull]], [[remote]], [[sync]]
*/
void push_cmd(void){
392
393
394
395
396
397
398

399
400
401
402
403
404
405
**   --no-http-compression      Do not compress HTTP traffic
**   --once                     Do not remember URL for subsequent syncs
**   --proxy PROXY              Use the specified HTTP proxy
**   --private                  Sync private branches too
**   -R|--repository REPO       Local repository to sync with
**   --ssl-identity FILE        Local SSL credentials, if requested by remote
**   --ssh-command SSH          Use SSH as the "ssh" command

**   -u|--unversioned           Also sync unversioned content
**   -v|--verbose               Additional (debugging) output
**   --verily                   Exchange extra information with the remote
**                              to ensure no content is overlooked
**
** See also: [[clone]], [[pull]], [[push]], [[remote]]
*/







>







461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
**   --no-http-compression      Do not compress HTTP traffic
**   --once                     Do not remember URL for subsequent syncs
**   --proxy PROXY              Use the specified HTTP proxy
**   --private                  Sync private branches too
**   -R|--repository REPO       Local repository to sync with
**   --ssl-identity FILE        Local SSL credentials, if requested by remote
**   --ssh-command SSH          Use SSH as the "ssh" command
**   --synclog                  Push local synclog information to the server
**   -u|--unversioned           Also sync unversioned content
**   -v|--verbose               Additional (debugging) output
**   --verily                   Exchange extra information with the remote
**                              to ensure no content is overlooked
**
** See also: [[clone]], [[pull]], [[push]], [[remote]]
*/
680
681
682
683
684
685
686

687
688
689
690
691
692
693
**
**    --overwrite              OK to overwrite an existing file
**    -R NAME                  Filename of the repository to backup
*/
void backup_cmd(void){
  char *zDest;
  int bOverwrite = 0;

  db_find_and_open_repository(OPEN_ANY_SCHEMA, 0);
  bOverwrite = find_option("overwrite",0,0)!=0;
  verify_all_options();
  if( g.argc!=3 ){
    usage("FILE|DIRECTORY");
  }
  zDest = g.argv[2];







>







750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
**
**    --overwrite              OK to overwrite an existing file
**    -R NAME                  Filename of the repository to backup
*/
void backup_cmd(void){
  char *zDest;
  int bOverwrite = 0;
  char *zFullName;
  db_find_and_open_repository(OPEN_ANY_SCHEMA, 0);
  bOverwrite = find_option("overwrite",0,0)!=0;
  verify_all_options();
  if( g.argc!=3 ){
    usage("FILE|DIRECTORY");
  }
  zDest = g.argv[2];
701
702
703
704
705
706
707



708




















































































      }
    }else{
      fossil_fatal("backup \"%s\" already exists", zDest);
    }
  }
  db_unprotect(PROTECT_ALL);
  db_multi_exec("VACUUM repository INTO %Q", zDest);



}



























































































>
>
>

>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
      }
    }else{
      fossil_fatal("backup \"%s\" already exists", zDest);
    }
  }
  db_unprotect(PROTECT_ALL);
  db_multi_exec("VACUUM repository INTO %Q", zDest);
  zFullName = file_canonical_name_dup(zDest);
  sync_log_entry("this", zFullName, 0, "backup");
  fossil_free(zFullName);
}

/*
** COMMAND: synclog
**
** Usage: %fossil synclog
**
** Show other repositories with which this repository has pushed or pulled,
** together with the time since the most recent push or pull.
*/
void synclog_cmd(void){
  Stmt q;
  int cnt;
  const int nIndent = 2;
  db_find_and_open_repository(0,0);
  schema_synclog();
  db_prepare(&q,
    "WITH allpull(xfrom,xto,xtime) AS MATERIALIZED (\n"
    "  SELECT sfrom, sto, max(stime) FROM synclog GROUP BY 1\n"
    "),\n"
    "pull(level, url, mtime, ex) AS (\n"
    "  SELECT 0, xfrom, xtime, '|this|' || xfrom || '|'\n"
    "    FROM allpull WHERE xto='this'\n"
    "  UNION\n"
    "  SELECT level+1, xfrom, xtime, ex || xfrom || '|'\n"
    "    FROM pull, allpull\n"
    "   WHERE xto=url\n"
    "     AND ex NOT GLOB ('*|' || xfrom || '|*')\n"
    "   ORDER BY 1 DESC, 3 DESC\n"
    ")\n"
    "SELECT level, url, julianday() - julianday(mtime,'auto') FROM pull"
  );
  cnt = 0;
  fossil_print("PULL:\n");
  while( db_step(&q)==SQLITE_ROW ){
    int iLevel = (db_column_int(&q,0)+1)*nIndent;
    const char *zUrl = db_column_text(&q,1);
    double rTimeAgo = db_column_double(&q,2);
    if( rTimeAgo*86400.0<=2.0 ){
      fossil_print("%.*c%s (current)\n", iLevel, ' ', zUrl);
    }else{
      char *zAgo = human_readable_age(rTimeAgo);
      fossil_print("%.*c%s (%z ago)\n", iLevel, ' ', zUrl, zAgo);
    }
    cnt++;
  }
  db_finalize(&q);
  if( cnt==0 ){
    fossil_print("  (none)\n");
  }
  db_prepare(&q,
    "WITH allpush(xfrom,xto,xtime) AS MATERIALIZED (\n"
    "  SELECT sfrom, sto, max(stime) FROM synclog GROUP BY 2\n"
    "),\n"
    "push(level, url, mtime, ex) AS (\n"
    "  SELECT 0, xto, xtime, '|this|' || xto || '|'\n"
    "    FROM allpush WHERE xfrom='this'\n"
    "  UNION\n"
    "  SELECT level+1, xto, xtime, ex || xto || '|'\n"
    "    FROM push, allpush\n"
    "   WHERE xfrom=url\n"
    "     AND ex NOT GLOB ('*|' || xto || '|*')\n"
    "   ORDER BY 1 DESC, 3 DESC\n"
    ")\n"
    "SELECT level, url, julianday() - julianday(mtime,'auto') FROM push"
  );
  cnt = 0;
  fossil_print("PUSH:\n");
  while( db_step(&q)==SQLITE_ROW ){
    int iLevel = (db_column_int(&q,0)+1)*nIndent;
    const char *zUrl = db_column_text(&q,1);
    double rTimeAgo = db_column_double(&q,2);
    if( rTimeAgo*86400.0<=2.0 ){
      fossil_print("%.*c%s (current)\n", iLevel, ' ', zUrl);
    }else{
      char *zAgo = human_readable_age(rTimeAgo);
      fossil_print("%.*c%s (%z ago)\n", iLevel, ' ', zUrl, zAgo);
    }
    cnt++;
  }
  db_finalize(&q);
  if( cnt==0 ){
    fossil_print("  (none)\n");
  }
}

Changes to src/xfer.c.

1148
1149
1150
1151
1152
1153
1154

































1155
1156
1157
1158
1159
1160
1161
**   # ... code here
**   set common_done 1
** }
*/
int xfer_run_common_script(void){
  return xfer_run_script(xfer_common_code(), 0, 0);
}


































/*
** If this variable is set, disable login checks.  Used for debugging
** only.
*/
static int disableLogin = 0;








>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
**   # ... code here
**   set common_done 1
** }
*/
int xfer_run_common_script(void){
  return xfer_run_script(xfer_common_code(), 0, 0);
}

/*
** The current line is a "pragma synclog".  Accept this line into
** the SYNCLOG table, if appropriate.
**
**    pragma synclog FROM TO MTIME ?TYPE?
**
** The name of the remote size is zRemote.  The value of "this" for
** either the FROM or TO fields is converted into zRemote.
**
** If either FROM or TO is an alias for the current repository, then
** silently reject the entry.
*/
static void xfer_accept_synclog_pragma(
  Xfer *pXfer,             /* Current line of xfer script */
  const char *zRemote      /* The name of the remote repository */
){
  const char *zFrom = blob_str(&pXfer->aToken[2]);
  const char *zTo = blob_str(&pXfer->aToken[3]);
  i64 iTime = strtoll(blob_str(&pXfer->aToken[4]), 0, 0);
  const char *zType = pXfer->nToken>=5 ? blob_str(&pXfer->aToken[5]) : 0;
  if( strcmp(zFrom, "this")==0 ){
    zFrom = zRemote;
  }else if( db_exists("SELECT 1 FROM config WHERE name='baseurl:%q'",zFrom) ){
    iTime = 0;
  }
  if( strcmp(zTo, "this")==0 ){
    zTo = zRemote;
  }else if( db_exists("SELECT 1 FROM config WHERE name='baseurl:%q'",zTo) ){
    iTime = 0;
  }
  if( iTime>0 ) sync_log_entry(zFrom, zTo, iTime, zType);
}

/*
** If this variable is set, disable login checks.  Used for debugging
** only.
*/
static int disableLogin = 0;

1185
1186
1187
1188
1189
1190
1191

1192
1193
1194
1195
1196
1197
1198
  int rc;
  const char *zScript = 0;
  char *zUuidList = 0;
  int nUuidList = 0;
  char **pzUuidList = 0;
  int *pnUuidList = 0;
  int uvCatalogSent = 0;


  if( fossil_strcmp(PD("REQUEST_METHOD","POST"),"POST") ){
     fossil_redirect_home();
  }
  g.zLogin = "anonymous";
  login_set_anon_nobody_capabilities();
  login_check_credentials();







>







1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
  int rc;
  const char *zScript = 0;
  char *zUuidList = 0;
  int nUuidList = 0;
  char **pzUuidList = 0;
  int *pnUuidList = 0;
  int uvCatalogSent = 0;
  const char *zClientUrl = 0;

  if( fossil_strcmp(PD("REQUEST_METHOD","POST"),"POST") ){
     fossil_redirect_home();
  }
  g.zLogin = "anonymous";
  login_set_anon_nobody_capabilities();
  login_check_credentials();
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
























































1711
1712
1713
1714
1715
1716
1717
            blob_str(&xfer.aToken[3])
          );
          db_protect_pop();
        }
        if( db_get_boolean("forbid-delta-manifests",0) ){
          @ pragma avoid-delta-manifests
        }
      }

      /*   pragma ci-unlock CLIENT-ID
      **
      ** Remove any locks previously held by CLIENT-ID.  Clients send this
      ** pragma with their own ID whenever they know that they no longer
      ** have any commits pending.
      */
      if( blob_eq(&xfer.aToken[1], "ci-unlock")
       && xfer.nToken==3
       && blob_is_hname(&xfer.aToken[2])
      ){
        db_unprotect(PROTECT_CONFIG);
        db_multi_exec(
          "DELETE FROM config"
          " WHERE name GLOB 'ci-lock-*'"
          "   AND json_extract(value,'$.clientid')=%Q",
          blob_str(&xfer.aToken[2])
        );
        db_protect_pop();
























































      }

    }else

    /* Unknown message
    */
    {







|



















>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
            blob_str(&xfer.aToken[3])
          );
          db_protect_pop();
        }
        if( db_get_boolean("forbid-delta-manifests",0) ){
          @ pragma avoid-delta-manifests
        }
      }else

      /*   pragma ci-unlock CLIENT-ID
      **
      ** Remove any locks previously held by CLIENT-ID.  Clients send this
      ** pragma with their own ID whenever they know that they no longer
      ** have any commits pending.
      */
      if( blob_eq(&xfer.aToken[1], "ci-unlock")
       && xfer.nToken==3
       && blob_is_hname(&xfer.aToken[2])
      ){
        db_unprotect(PROTECT_CONFIG);
        db_multi_exec(
          "DELETE FROM config"
          " WHERE name GLOB 'ci-lock-*'"
          "   AND json_extract(value,'$.clientid')=%Q",
          blob_str(&xfer.aToken[2])
        );
        db_protect_pop();
      }else

      /*   pragma req-synclog ?CLIENT-URL?
      **
      ** Request synclog data.  If the CLIENT-URL  argument is provided,
      ** it will be the canonical URL for the client.
      */
      if( blob_eq(&xfer.aToken[1], "req-synclog") 
       && g.perm.RdSLog
       && db_table_exists("repository","synclog")
      ){
        Stmt qSynclog;
        if( xfer.nToken>=2 ){
          zClientUrl = blob_str(&xfer.aToken[2]);
          if( zClientUrl!=0
           && sqlite3_strlike("http%//localhost%", zClientUrl, 0)==0
          ){
            zClientUrl = 0;
          }
        }
        db_prepare(&qSynclog,
          "SELECT sfrom, sto, unixepoch(stime,'auto'), stype FROM synclog"
        );
        while( db_step(&qSynclog)==SQLITE_ROW ){
          const char *zFrom = db_column_text(&qSynclog,0);
          const char *zTo = db_column_text(&qSynclog,1);
          const char *zTime = db_column_text(&qSynclog,2);
          const char *zType = db_column_text(&qSynclog,3);
          if( zClientUrl ){
            if( strcmp(zFrom, zClientUrl)==0 ) continue;
            if( strcmp(zTo, zClientUrl)==0 ) continue;
          }
          if( zType!=0 && zType[0]!=0 ){
            @ pragma synclog %s(zFrom) %s(zTo) %s(zTime) %s(zType)
          }else{
            @ pragma synclog %s(zFrom) %s(zTo) %s(zTime)
          }
        }
      }else

      /*   pragma synclog FROM TO MTIME ?TYPE?
      **
      ** The client is uploading an entry from its SYNCLOG table.  This
      ** will only be accepted if both:
      **
      **    (1) The client as WrSLog permission
      **    (2) A prior req-synclog pragma has identified the URL of the client
      **
      ** Insert the entry into the SYNC log if appropriate.
      */
      if( xfer.nToken>=4
       && blob_eq(&xfer.aToken[1], "synclog")
       && g.perm.WrSLog
       && zClientUrl!=0
      ){
        xfer_accept_synclog_pragma(&xfer, zClientUrl);
      }

    }else

    /* Unknown message
    */
    {
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826

1827
1828
1829
1830
1831
1832
1833
static const char zBriefFormat[] =
   "Round-trips: %d   Artifacts sent: %d  received: %d\r";

#if INTERFACE
/*
** Flag options for controlling client_sync()
*/
#define SYNC_PUSH           0x0001    /* push content client to server */
#define SYNC_PULL           0x0002    /* pull content server to client */
#define SYNC_CLONE          0x0004    /* clone the repository */
#define SYNC_PRIVATE        0x0008    /* Also transfer private content */
#define SYNC_VERBOSE        0x0010    /* Extra diagnostics */
#define SYNC_RESYNC         0x0020    /* --verily */
#define SYNC_FROMPARENT     0x0040    /* Pull from the parent project */
#define SYNC_UNVERSIONED    0x0100    /* Sync unversioned content */
#define SYNC_UV_REVERT      0x0200    /* Copy server unversioned to client */
#define SYNC_UV_TRACE       0x0400    /* Describe UV activities */
#define SYNC_UV_DRYRUN      0x0800    /* Do not actually exchange files */
#define SYNC_IFABLE         0x1000    /* Inability to sync is not fatal */
#define SYNC_CKIN_LOCK      0x2000    /* Lock the current check-in */
#define SYNC_NOHTTPCOMPRESS 0x4000    /* Do not compression HTTP messages */
#define SYNC_ALLURL         0x8000    /* The --all flag - sync to all URLs */

#endif

/*
** Floating-point absolute value
*/
static double fossil_fabs(double x){
  return x>0.0 ? x : -x;







|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
>







1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
static const char zBriefFormat[] =
   "Round-trips: %d   Artifacts sent: %d  received: %d\r";

#if INTERFACE
/*
** Flag options for controlling client_sync()
*/
#define SYNC_PUSH           0x00001    /* push content client to server */
#define SYNC_PULL           0x00002    /* pull content server to client */
#define SYNC_CLONE          0x00004    /* clone the repository */
#define SYNC_PRIVATE        0x00008    /* Also transfer private content */
#define SYNC_VERBOSE        0x00010    /* Extra diagnostics */
#define SYNC_RESYNC         0x00020    /* --verily */
#define SYNC_FROMPARENT     0x00040    /* Pull from the parent project */
#define SYNC_UNVERSIONED    0x00100    /* Sync unversioned content */
#define SYNC_UV_REVERT      0x00200    /* Copy server unversioned to client */
#define SYNC_UV_TRACE       0x00400    /* Describe UV activities */
#define SYNC_UV_DRYRUN      0x00800    /* Do not actually exchange files */
#define SYNC_IFABLE         0x01000    /* Inability to sync is not fatal */
#define SYNC_CKIN_LOCK      0x02000    /* Lock the current check-in */
#define SYNC_NOHTTPCOMPRESS 0x04000    /* Do not compression HTTP messages */
#define SYNC_ALLURL         0x08000    /* The --all flag - sync to all URLs */
#define SYNC_PUSH_SYNCLOG   0x10000    /* Uplink SYNCLOG info */
#endif

/*
** Floating-point absolute value
*/
static double fossil_fabs(double x){
  return x>0.0 ? x : -x;
1883
1884
1885
1886
1887
1888
1889
1890






1891
1892
1893
1894
1895
1896
1897
1898
1899
1900



1901
1902
1903
1904
1905
1906
1907
  int autopushFailed = 0; /* Autopush following commit failed if true */
  const char *zCkinLock;  /* Name of check-in to lock.  NULL for none */
  const char *zClientId;  /* A unique identifier for this check-out */
  unsigned int mHttpFlags;/* Flags for the http_exchange() subsystem */

  if( db_get_boolean("dont-push", 0) ) syncFlags &= ~SYNC_PUSH;
  if( (syncFlags & (SYNC_PUSH|SYNC_PULL|SYNC_CLONE|SYNC_UNVERSIONED))==0
     && configRcvMask==0 && configSendMask==0 ) return 0;






  if( syncFlags & SYNC_FROMPARENT ){
    configRcvMask = 0;
    configSendMask = 0;
    syncFlags &= ~(SYNC_PUSH);
    zPCode = db_get("parent-project-code", 0);
    if( zPCode==0 || db_get("parent-project-name",0)==0 ){
      fossil_fatal("there is no parent project: set the 'parent-project-code'"
                   " and 'parent-project-name' config parameters in order"
                   " to pull from a parent project");
    }



  }

  transport_stats(0, 0, 1);
  socket_global_init();
  memset(&xfer, 0, sizeof(xfer));
  xfer.pIn = &recv;
  xfer.pOut = &send;







|
>
>
>
>
>
>




|
|




>
>
>







1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
  int autopushFailed = 0; /* Autopush following commit failed if true */
  const char *zCkinLock;  /* Name of check-in to lock.  NULL for none */
  const char *zClientId;  /* A unique identifier for this check-out */
  unsigned int mHttpFlags;/* Flags for the http_exchange() subsystem */

  if( db_get_boolean("dont-push", 0) ) syncFlags &= ~SYNC_PUSH;
  if( (syncFlags & (SYNC_PUSH|SYNC_PULL|SYNC_CLONE|SYNC_UNVERSIONED))==0
     && configRcvMask==0 && configSendMask==0 ){
    return 0;
  }
  if( zPCode==0 && (syncFlags & SYNC_CLONE)==0 ){
    /* Every established repository must have a project-code */
    fossil_fatal("no project-code defined for this repository");
  }
  if( syncFlags & SYNC_FROMPARENT ){
    configRcvMask = 0;
    configSendMask = 0;
    syncFlags &= ~(SYNC_PUSH);
    zAltPCode = db_get("parent-project-code", 0);
    if( zAltPCode==0 || db_get("parent-project-name",0)==0 ){
      fossil_fatal("there is no parent project: set the 'parent-project-code'"
                   " and 'parent-project-name' config parameters in order"
                   " to pull from a parent project");
    }
  }
  if( zAltPCode!=0 && zPCode!=0 && sqlite3_stricmp(zPCode,zAltPCode)==0 ){
    zAltPCode = 0;
  }

  transport_stats(0, 0, 1);
  socket_global_init();
  memset(&xfer, 0, sizeof(xfer));
  xfer.pIn = &recv;
  xfer.pOut = &send;
2109
2110
2111
2112
2113
2114
2115






































2116
2117
2118
2119
2120
2121
2122
        db_lset("client-id", zClientId);
      }
      blob_appendf(&send, "pragma ci-lock %s %s\n", zCkinLock, zClientId);
      zCkinLock = 0;
    }else if( zClientId ){
      blob_appendf(&send, "pragma ci-unlock %s\n", zClientId);
    }







































    /* Append randomness to the end of the uplink message.  This makes all
    ** messages unique so that that the login-card nonce will always
    ** be unique.
    */
    zRandomness = db_text(0, "SELECT hex(randomblob(20))");
    blob_appendf(&send, "# %s\n", zRandomness);







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
        db_lset("client-id", zClientId);
      }
      blob_appendf(&send, "pragma ci-lock %s %s\n", zCkinLock, zClientId);
      zCkinLock = 0;
    }else if( zClientId ){
      blob_appendf(&send, "pragma ci-unlock %s\n", zClientId);
    }

    /* Transfer SYNCLOG data on the first roundtrip, if appropriate */
    if( nCycle==0 ){
      const char *zSelfUrl = public_url();
      if( zSelfUrl!=0
       && sqlite3_strlike("http%//localhost%", zSelfUrl, 0)==0
      ){
        zSelfUrl = 0;
      }
      if( zSelfUrl==0 ){
        blob_appendf(&send,"pragma req-synclog\n");
      }else{
        blob_appendf(&send,"pragma req-synclog %s\n", zSelfUrl);
        if( (syncFlags & SYNC_PUSH_SYNCLOG)!=0
         && db_table_exists("repository","synclog")
        ){
          Stmt qSynclog;
          db_prepare(&qSynclog,
            "SELECT sfrom, sto, unixepoch(stime), stype FROM synclog"
            " WHERE sfrom!=%Q AND sto!=%Q",
            g.url.canonical, g.url.canonical
          );
          while( db_step(&qSynclog)==SQLITE_ROW ){
            const char *zFrom = db_column_text(&qSynclog,0);
            const char *zTo = db_column_text(&qSynclog,1);
            const char *zTime = db_column_text(&qSynclog,2);
            const char *zType = db_column_text(&qSynclog,3);
            if( zType!=0 && zType[0]!=0 ){
              blob_appendf(&send,"pragma synclog %s %s %s %s\n",
                   zFrom, zTo, zTime, zType);
            }else{
              blob_appendf(&send,"pragma synclog %s %s %s\n",
                   zFrom, zTo, zTime);
            }
          }
        }
      }
    }

    /* Append randomness to the end of the uplink message.  This makes all
    ** messages unique so that that the login-card nonce will always
    ** be unique.
    */
    zRandomness = db_text(0, "SELECT hex(randomblob(20))");
    blob_appendf(&send, "# %s\n", zRandomness);
2511
2512
2513
2514
2515
2516
2517











2518
2519
2520
2521
2522
2523
2524
        if( xfer.nToken>=3 && blob_eq(&xfer.aToken[1], "server-version") ){
          xfer.remoteVersion = atoi(blob_str(&xfer.aToken[2]));
          if( xfer.nToken>=5 ){
            xfer.remoteDate = atoi(blob_str(&xfer.aToken[3]));
            xfer.remoteTime = atoi(blob_str(&xfer.aToken[4]));
          }
        }












        /*   pragma uv-pull-only
        **   pragma uv-push-ok
        **
        ** If the server is unwill to accept new unversioned content (because
        ** this client lacks the necessary permissions) then it sends a
        ** "uv-pull-only" pragma so that the client will know not to waste







>
>
>
>
>
>
>
>
>
>
>







2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
2665
2666
2667
2668
2669
2670
2671
2672
2673
        if( xfer.nToken>=3 && blob_eq(&xfer.aToken[1], "server-version") ){
          xfer.remoteVersion = atoi(blob_str(&xfer.aToken[2]));
          if( xfer.nToken>=5 ){
            xfer.remoteDate = atoi(blob_str(&xfer.aToken[3]));
            xfer.remoteTime = atoi(blob_str(&xfer.aToken[4]));
          }
        }

        /*   pragma synclog FROM TO MTIME ?TYPE?
        **
        ** The server is downloading an entry from its SYNCLOG table.  Merge
        ** this into the local SYNCLOG table if appropriate.
        ** is running.  The DATE and TIME are a pure numeric ISO8601 time
        ** for the specific check-in of the client.
        */
        if( xfer.nToken>=4 && blob_eq(&xfer.aToken[1], "synclog") ){
          xfer_accept_synclog_pragma(&xfer, g.url.canonical);
        }

        /*   pragma uv-pull-only
        **   pragma uv-push-ok
        **
        ** If the server is unwill to accept new unversioned content (because
        ** this client lacks the necessary permissions) then it sends a
        ** "uv-pull-only" pragma so that the client will know not to waste
2582
2583
2584
2585
2586
2587
2588
2589

2590
2591
2592
2593
2594
2595
2596
      ** Also ignore "not authorized to write" errors if this is an
      ** autopush following a commit.
      */
      if( blob_eq(&xfer.aToken[0],"error") && xfer.nToken==2 ){
        char *zMsg = blob_terminate(&xfer.aToken[1]);
        defossilize(zMsg);
        if( (syncFlags & SYNC_IFABLE)!=0
         && sqlite3_strlike("%not authorized to write%",zMsg,0)==0 ){

          autopushFailed = 1;
          nErr++;
        }else if( (syncFlags & SYNC_CLONE)==0 || nCycle>0 ){
          fossil_force_newline();
          fossil_print("Error: %s\n", zMsg);
          blob_appendf(&xfer.err, "server says: %s\n", zMsg);
          nErr++;







|
>







2731
2732
2733
2734
2735
2736
2737
2738
2739
2740
2741
2742
2743
2744
2745
2746
      ** Also ignore "not authorized to write" errors if this is an
      ** autopush following a commit.
      */
      if( blob_eq(&xfer.aToken[0],"error") && xfer.nToken==2 ){
        char *zMsg = blob_terminate(&xfer.aToken[1]);
        defossilize(zMsg);
        if( (syncFlags & SYNC_IFABLE)!=0
         && sqlite3_strlike("%not authorized to write%",zMsg,0)==0
        ){
          autopushFailed = 1;
          nErr++;
        }else if( (syncFlags & SYNC_CLONE)==0 || nCycle>0 ){
          fossil_force_newline();
          fossil_print("Error: %s\n", zMsg);
          blob_appendf(&xfer.err, "server says: %s\n", zMsg);
          nErr++;
2680
2681
2682
2683
2684
2685
2686













2687
2688
2689
2690
2691
2692
2693
                    db_timespan_name(rSkew));
     g.clockSkewSeen = 1;
  }else if( rSkew*24.0*3600.0 < -10.0 ){
     fossil_warning("*** time skew *** server is slow by %s",
                    db_timespan_name(-rSkew));
     g.clockSkewSeen = 1;
  }














  fossil_force_newline();
  fossil_print(
     "%s done, wire bytes sent: %lld  received: %lld  ip: %s\n",
     zOpType, nSent, nRcvd, g.zIpAddr);
  if( syncFlags & SYNC_VERBOSE ){
    fossil_print(







>
>
>
>
>
>
>
>
>
>
>
>
>







2830
2831
2832
2833
2834
2835
2836
2837
2838
2839
2840
2841
2842
2843
2844
2845
2846
2847
2848
2849
2850
2851
2852
2853
2854
2855
2856
                    db_timespan_name(rSkew));
     g.clockSkewSeen = 1;
  }else if( rSkew*24.0*3600.0 < -10.0 ){
     fossil_warning("*** time skew *** server is slow by %s",
                    db_timespan_name(-rSkew));
     g.clockSkewSeen = 1;
  }

  if( nErr==0 ){
    if( zAltPCode!=0 ){
      sync_log_entry(g.url.canonical, "this", 0, "import");
    }else{
      if( syncFlags & SYNC_PUSH ){
        sync_log_entry("this", g.url.canonical, 0, 0);
      }
      if( syncFlags & SYNC_PULL ){
        sync_log_entry(g.url.canonical, "this", 0, 0);
      }
    }
  }

  fossil_force_newline();
  fossil_print(
     "%s done, wire bytes sent: %lld  received: %lld  ip: %s\n",
     zOpType, nSent, nRcvd, g.zIpAddr);
  if( syncFlags & SYNC_VERBOSE ){
    fossil_print(