Fossil

Check-in [4f83d06275]
Login

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

Overview
Comment:Enhance the blob_append_escaped_args() routine with a new parameter to indicate if the argument is a filename. Only prepend "./" to filename arguments that begin with "-". The "%$" printf conversion assumes a filename, but the "%!$" conversion does not.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 4f83d06275804a8320e79fd90df1cc990fb4fd23a2b54b85927609f5f9c4efbc
User & Date: drh 2021-06-30 13:52:49.227
Context
2021-06-30
13:56
The argument to the "fossil ui" command is allowed to be a directory name instead of a repository filename. If a directory, repository associated with the checkout at that directory is used. check-in: 9ec744ed79 user: drh tags: trunk
13:52
Enhance the blob_append_escaped_args() routine with a new parameter to indicate if the argument is a filename. Only prepend "./" to filename arguments that begin with "-". The "%$" printf conversion assumes a filename, but the "%!$" conversion does not. check-in: 4f83d06275 user: drh tags: trunk
13:07
Remove legacy code that attempted to use -P instead of -p on "plink" for the port number of SSH. check-in: ffe2a84cec user: drh tags: trunk
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/backoffice.c.
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
        if( iNow
         && iNow>file_mtime(g.argv[i], ExtFILE)
         && (nMin<=0 || aLastRun[i]+nMin>iNow)
        ){
          continue;  /* Not yet time to run this one */
        }
        blob_init(&cmd, 0, 0);
        blob_append_escaped_arg(&cmd, g.nameOfExe);
        blob_append(&cmd, " backoffice --nodelay", -1);
        if( g.fAnyTrace ){
          blob_append(&cmd, " --trace", -1);
        }
        if( bDebug ){
          blob_append(&cmd, " --debug", -1);
        }
        if( nPoll>0 ){
          blob_append(&cmd, " --nolease", -1);
        }
        if( backofficeLogfile ){
          blob_append(&cmd, " --logfile", -1);
          blob_append_escaped_arg(&cmd, backofficeLogfile);
        }
        blob_append_escaped_arg(&cmd, g.argv[i]);
        nCmd++;
        if( bDebug ){
          fossil_print("COMMAND[%u]: %s\n", nCmd, blob_str(&cmd));
        }
        fossil_system(blob_str(&cmd));
        aLastRun[i] = iNext;
        blob_reset(&cmd);







|












|

|







752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
        if( iNow
         && iNow>file_mtime(g.argv[i], ExtFILE)
         && (nMin<=0 || aLastRun[i]+nMin>iNow)
        ){
          continue;  /* Not yet time to run this one */
        }
        blob_init(&cmd, 0, 0);
        blob_append_escaped_arg(&cmd, g.nameOfExe, 1);
        blob_append(&cmd, " backoffice --nodelay", -1);
        if( g.fAnyTrace ){
          blob_append(&cmd, " --trace", -1);
        }
        if( bDebug ){
          blob_append(&cmd, " --debug", -1);
        }
        if( nPoll>0 ){
          blob_append(&cmd, " --nolease", -1);
        }
        if( backofficeLogfile ){
          blob_append(&cmd, " --logfile", -1);
          blob_append_escaped_arg(&cmd, backofficeLogfile, 1);
        }
        blob_append_escaped_arg(&cmd, g.argv[i], 1);
        nCmd++;
        if( bDebug ){
          fossil_print("COMMAND[%u]: %s\n", nCmd, blob_str(&cmd));
        }
        fossil_system(blob_str(&cmd));
        aLastRun[i] = iNext;
        blob_reset(&cmd);
Changes to src/bisect.c.
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
                " WHERE name IN ('bisect-good','bisect-bad')")!=2 ){
    fossil_fatal("need good/bad boundaries to use \"fossil bisect run\"");
  }
  while( db_lget_int("bisect-complete",0)==0 ){
    int rc;
    Blob cmd;
    blob_init(&cmd, 0, 0);
    blob_append_escaped_arg(&cmd, g.nameOfExe);
    rc = fossil_unsafe_system(zCmd);
    if( isInteractive ){
      Blob in;
      fossil_print("test-command result: %d\n", rc);
      while(1){
        int n;
        char *z;







|







409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
                " WHERE name IN ('bisect-good','bisect-bad')")!=2 ){
    fossil_fatal("need good/bad boundaries to use \"fossil bisect run\"");
  }
  while( db_lget_int("bisect-complete",0)==0 ){
    int rc;
    Blob cmd;
    blob_init(&cmd, 0, 0);
    blob_append_escaped_arg(&cmd, g.nameOfExe, 1);
    rc = fossil_unsafe_system(zCmd);
    if( isInteractive ){
      Blob in;
      fossil_print("test-command result: %d\n", rc);
      while(1){
        int n;
        char *z;
Changes to src/blob.c.
1365
1366
1367
1368
1369
1370
1371

1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
** pBlob is a shell command under construction.  This routine safely
** appends filename argument zIn.
**
** The argument is escaped if it contains white space or other characters
** that need to be escaped for the shell.  If zIn contains characters
** that cannot be safely escaped, then throw a fatal error.
**

** The argument is expected to be a filename.  As shell commands
** commonly have command-line options that begin with "-" and since we
** do not want an attacker to be able to invoke these switches using
** filenames that begin with "-", if zIn begins with "-", prepend
** an additional "./" (or ".\\" on Windows).
*/
void blob_append_escaped_arg(Blob *pBlob, const char *zIn){
  int i;
  unsigned char c;
  int needEscape = 0;
  int n = blob_size(pBlob);
  char *z = blob_buffer(pBlob);

  /* Look for illegal byte-sequences and byte-sequences that require







>
|
|
|
|
|

|







1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
** pBlob is a shell command under construction.  This routine safely
** appends filename argument zIn.
**
** The argument is escaped if it contains white space or other characters
** that need to be escaped for the shell.  If zIn contains characters
** that cannot be safely escaped, then throw a fatal error.
**
** If the isFilename argument is true, then the argument is expected
** to be a filename.  As shell commands commonly have command-line
** options that begin with "-" and since we do not want an attacker
** to be able to invoke these switches using filenames that begin
** with "-", if zIn begins with "-", prepend an additional "./"
** (or ".\\" on Windows).
*/
void blob_append_escaped_arg(Blob *pBlob, const char *zIn, int isFilename){
  int i;
  unsigned char c;
  int needEscape = 0;
  int n = blob_size(pBlob);
  char *z = blob_buffer(pBlob);

  /* Look for illegal byte-sequences and byte-sequences that require
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
  /* Separate from the previous argument by a space */
  if( n>0 && !fossil_isspace(z[n-1]) ){
    blob_append_char(pBlob, ' ');
  }

  /* Check for characters that need quoting */
  if( !needEscape ){
    if( zIn[0]=='-' ){
      blob_append_char(pBlob, '.');
#if defined(_WIN32)
      blob_append_char(pBlob, '\\');
#else
      blob_append_char(pBlob, '/');
#endif
    }
    blob_append(pBlob, zIn, -1);
  }else{
#if defined(_WIN32)
    /* Quoting strategy for windows:
    ** Put the entire name inside of "...".  Any " characters within
    ** the name get doubled.
    */
    blob_append_char(pBlob, '"');
    if( zIn[0]=='-' ){
      blob_append_char(pBlob, '.');
      blob_append_char(pBlob, '\\');
    }else if( zIn[0]=='/' ){
      blob_append_char(pBlob, '.');
    }
    for(i=0; (c = (unsigned char)zIn[i])!=0; i++){
      blob_append_char(pBlob, (char)c);
      if( c=='"' ) blob_append_char(pBlob, '"');
    }
    blob_append_char(pBlob, '"');
#else
    /* Quoting strategy for unix:
    ** If the name does not contain ', then surround the whole thing
    ** with '...'.   If there is one or more ' characters within the
    ** name, then put \ before each special character.
    */
    if( strchr(zIn,'\'') ){
      if( zIn[0]=='-' ){
        blob_append_char(pBlob, '.');
        blob_append_char(pBlob, '/');
      }
      for(i=0; (c = (unsigned char)zIn[i])!=0; i++){
        if( aSafeChar[c] && aSafeChar[c]!=2 ) blob_append_char(pBlob, '\\');
        blob_append_char(pBlob, (char)c);
      }
    }else{
      blob_append_char(pBlob, '\'');
      if( zIn[0]=='-' ){
        blob_append_char(pBlob, '.');
        blob_append_char(pBlob, '/');
      }
      blob_append(pBlob, zIn, -1);
      blob_append_char(pBlob, '\'');
    }
#endif







|















|

















|









|







1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
  /* Separate from the previous argument by a space */
  if( n>0 && !fossil_isspace(z[n-1]) ){
    blob_append_char(pBlob, ' ');
  }

  /* Check for characters that need quoting */
  if( !needEscape ){
    if( isFilename && zIn[0]=='-' ){
      blob_append_char(pBlob, '.');
#if defined(_WIN32)
      blob_append_char(pBlob, '\\');
#else
      blob_append_char(pBlob, '/');
#endif
    }
    blob_append(pBlob, zIn, -1);
  }else{
#if defined(_WIN32)
    /* Quoting strategy for windows:
    ** Put the entire name inside of "...".  Any " characters within
    ** the name get doubled.
    */
    blob_append_char(pBlob, '"');
    if( isFilename && zIn[0]=='-' ){
      blob_append_char(pBlob, '.');
      blob_append_char(pBlob, '\\');
    }else if( zIn[0]=='/' ){
      blob_append_char(pBlob, '.');
    }
    for(i=0; (c = (unsigned char)zIn[i])!=0; i++){
      blob_append_char(pBlob, (char)c);
      if( c=='"' ) blob_append_char(pBlob, '"');
    }
    blob_append_char(pBlob, '"');
#else
    /* Quoting strategy for unix:
    ** If the name does not contain ', then surround the whole thing
    ** with '...'.   If there is one or more ' characters within the
    ** name, then put \ before each special character.
    */
    if( strchr(zIn,'\'') ){
      if( isFilename && zIn[0]=='-' ){
        blob_append_char(pBlob, '.');
        blob_append_char(pBlob, '/');
      }
      for(i=0; (c = (unsigned char)zIn[i])!=0; i++){
        if( aSafeChar[c] && aSafeChar[c]!=2 ) blob_append_char(pBlob, '\\');
        blob_append_char(pBlob, (char)c);
      }
    }else{
      blob_append_char(pBlob, '\'');
      if( isFilename && zIn[0]=='-' ){
        blob_append_char(pBlob, '.');
        blob_append_char(pBlob, '/');
      }
      blob_append(pBlob, zIn, -1);
      blob_append_char(pBlob, '\'');
    }
#endif
1482
1483
1484
1485
1486
1487
1488




1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505

1506
1507
1508
1509
1510
1511
1512
**
** Run each argument through blob_append_escaped_arg() and show the
** result.  Append each argument to "fossil test-echo" and run that
** using fossil_system() to verify that it really does get escaped
** correctly.
**
** Other options:




**
**    --hex HEX                 Skip the --hex flag and instead decode HEX
**                              into ascii.  This provides a way to insert
**                              unusual characters as an argument for testing.
**
**    --compare HEX ASCII       Verify that argument ASCII is identical to
**                              to decoded HEX.
**
**    --fuzz N                  Run N fuzz cases.  Each cases is a call
**                              to "fossil test-escaped-arg --compare HEX ARG"
**                              where HEX and ARG are the same argument.
**                              The argument is chosen at random.
*/
void test_escaped_arg_command(void){
  int i;
  Blob x;
  const char *zArg;

  char zBuf[100];
  blob_init(&x, 0, 0);
  for(i=2; i<g.argc; i++){
    zArg = g.argv[i];
    if( fossil_strcmp(zArg, "--hex")==0 && i+1<g.argc ){
      size_t n = strlen(g.argv[++i]);
      if( n>=(sizeof(zBuf)-1)*2 ){







>
>
>
>

















>







1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
**
** Run each argument through blob_append_escaped_arg() and show the
** result.  Append each argument to "fossil test-echo" and run that
** using fossil_system() to verify that it really does get escaped
** correctly.
**
** Other options:
**
**    --filename-args BOOL      Subsequent arguments are assumed to be
**                              filenames if BOOL is true, or not if BOOL
**                              is false.  Defaults on.
**
**    --hex HEX                 Skip the --hex flag and instead decode HEX
**                              into ascii.  This provides a way to insert
**                              unusual characters as an argument for testing.
**
**    --compare HEX ASCII       Verify that argument ASCII is identical to
**                              to decoded HEX.
**
**    --fuzz N                  Run N fuzz cases.  Each cases is a call
**                              to "fossil test-escaped-arg --compare HEX ARG"
**                              where HEX and ARG are the same argument.
**                              The argument is chosen at random.
*/
void test_escaped_arg_command(void){
  int i;
  Blob x;
  const char *zArg;
  int isFilename = 1;
  char zBuf[100];
  blob_init(&x, 0, 0);
  for(i=2; i<g.argc; i++){
    zArg = g.argv[i];
    if( fossil_strcmp(zArg, "--hex")==0 && i+1<g.argc ){
      size_t n = strlen(g.argv[++i]);
      if( n>=(sizeof(zBuf)-1)*2 ){
1584
1585
1586
1587
1588
1589
1590




1591


1592

1593



1594
1595
1596
1597
1598
1599
1600
        blob_appendf(&x, "%$ test-escaped-arg --compare %s %$",
                         g.nameOfExe, zBuf,zWord);
        rc = fossil_system(blob_str(&x));
        if( rc ) fossil_fatal("failed test (%d): %s\n", rc, blob_str(&x));
        blob_reset(&x);
      }
      continue;




    }


    fossil_print("%3d [%s]: ", i, zArg);

    blob_appendf(&x, "%$ test-echo %$", g.nameOfExe, zArg);



    fossil_print("%s\n", blob_str(&x));
    fossil_system(blob_str(&x));
    blob_reset(&x);
  }
}

/*







>
>
>
>
|
>
>

>
|
>
>
>







1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
        blob_appendf(&x, "%$ test-escaped-arg --compare %s %$",
                         g.nameOfExe, zBuf,zWord);
        rc = fossil_system(blob_str(&x));
        if( rc ) fossil_fatal("failed test (%d): %s\n", rc, blob_str(&x));
        blob_reset(&x);
      }
      continue;
    }else if( fossil_strcmp(zArg, "--filename-args")==0 ){
       if( i+1<g.argc ){
         i++;
         isFilename = is_truth(g.argv[i]);
       }
       continue;
    }
    fossil_print("%3d [%s]: ", i, zArg);
    if( isFilename ){
      blob_appendf(&x, "%$ test-echo %$", g.nameOfExe, zArg);
    }else{
      blob_appendf(&x, "%$ test-echo %!$", g.nameOfExe, zArg);
    }
    fossil_print("%s\n", blob_str(&x));
    fossil_system(blob_str(&x));
    blob_reset(&x);
  }
}

/*
Changes to src/clone.c.
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
  zPassword = db_text(0, "SELECT pw FROM user WHERE login=%Q", g.zLogin);
  fossil_print("admin-user: %s (password is \"%s\")\n", g.zLogin, zPassword);
  if( zWorkDir!=0 && zWorkDir[0]!=0 && !noOpen ){
    Blob cmd;
    fossil_print("opening the new %s repository in directory %s...\n",
       zRepo, zWorkDir);
    blob_init(&cmd, 0, 0);
    blob_append_escaped_arg(&cmd, g.nameOfExe);
    blob_append(&cmd, " open ", -1);
    blob_append_escaped_arg(&cmd, zRepo);
    blob_append(&cmd, " --workdir ", -1);
    blob_append_escaped_arg(&cmd, zWorkDir);
    if( allowNested ){
      blob_append(&cmd, " --nested", -1);
    }
    fossil_system(blob_str(&cmd));
    blob_reset(&cmd);
  }
}







|

|

|







288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
  zPassword = db_text(0, "SELECT pw FROM user WHERE login=%Q", g.zLogin);
  fossil_print("admin-user: %s (password is \"%s\")\n", g.zLogin, zPassword);
  if( zWorkDir!=0 && zWorkDir[0]!=0 && !noOpen ){
    Blob cmd;
    fossil_print("opening the new %s repository in directory %s...\n",
       zRepo, zWorkDir);
    blob_init(&cmd, 0, 0);
    blob_append_escaped_arg(&cmd, g.nameOfExe, 1);
    blob_append(&cmd, " open ", -1);
    blob_append_escaped_arg(&cmd, zRepo, 1);
    blob_append(&cmd, " --workdir ", -1);
    blob_append_escaped_arg(&cmd, zWorkDir, 1);
    if( allowNested ){
      blob_append(&cmd, " --nested", -1);
    }
    fossil_system(blob_str(&cmd));
    blob_reset(&cmd);
  }
}
Changes to src/db.c.
3597
3598
3599
3600
3601
3602
3603
3604
3605
3606
3607
3608
3609
3610
3611
3612
3613
3614
      fossil_fatal("unable to deduce a repository name from the url \"%s\"",
                   zUri);
    }
    if( zRepoDir==0 ) zRepoDir = zPwd;
    zRepo = mprintf("%s/%s.fossil", zRepoDir, zNewBase);
    fossil_free(zNewBase);
    blob_init(&cmd, 0, 0);
    blob_append_escaped_arg(&cmd, g.nameOfExe);
    blob_append(&cmd, " clone", -1);
    blob_append_escaped_arg(&cmd, zUri);
    blob_append_escaped_arg(&cmd, zRepo);
    zCmd = blob_str(&cmd);
    fossil_print("%s\n", zCmd);
    if( zWorkDir ) file_chdir(zPwd, 0);
    rc = fossil_system(zCmd);
    if( rc ){
      fossil_fatal("clone of %s failed", zUri);
    }







|

|
|







3597
3598
3599
3600
3601
3602
3603
3604
3605
3606
3607
3608
3609
3610
3611
3612
3613
3614
      fossil_fatal("unable to deduce a repository name from the url \"%s\"",
                   zUri);
    }
    if( zRepoDir==0 ) zRepoDir = zPwd;
    zRepo = mprintf("%s/%s.fossil", zRepoDir, zNewBase);
    fossil_free(zNewBase);
    blob_init(&cmd, 0, 0);
    blob_append_escaped_arg(&cmd, g.nameOfExe, 1);
    blob_append(&cmd, " clone", -1);
    blob_append_escaped_arg(&cmd, zUri, 1);
    blob_append_escaped_arg(&cmd, zRepo, 1);
    zCmd = blob_str(&cmd);
    fossil_print("%s\n", zCmd);
    if( zWorkDir ) file_chdir(zPwd, 0);
    rc = fossil_system(zCmd);
    if( rc ){
      fossil_fatal("clone of %s failed", zUri);
    }
Changes to src/diffcmd.c.
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
    file_tempname(&nameFile1, zFile2, "orig");
    blob_write_to_file(pFile1, blob_str(&nameFile1));

    /* Construct the external diff command */
    blob_zero(&cmd);
    blob_append(&cmd, zDiffCmd, -1);
    if( fSwapDiff ){
      blob_append_escaped_arg(&cmd, zFile2);
      blob_append_escaped_arg(&cmd, blob_str(&nameFile1));
    }else{
      blob_append_escaped_arg(&cmd, blob_str(&nameFile1));
      blob_append_escaped_arg(&cmd, zFile2);
    }

    /* Run the external diff command */
    fossil_system(blob_str(&cmd));

    /* Delete the temporary file and clean up memory used */
    file_delete(blob_str(&nameFile1));







|
|

|
|







269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
    file_tempname(&nameFile1, zFile2, "orig");
    blob_write_to_file(pFile1, blob_str(&nameFile1));

    /* Construct the external diff command */
    blob_zero(&cmd);
    blob_append(&cmd, zDiffCmd, -1);
    if( fSwapDiff ){
      blob_append_escaped_arg(&cmd, zFile2, 1);
      blob_append_escaped_arg(&cmd, blob_str(&nameFile1), 1);
    }else{
      blob_append_escaped_arg(&cmd, blob_str(&nameFile1), 1);
      blob_append_escaped_arg(&cmd, zFile2, 1);
    }

    /* Run the external diff command */
    fossil_system(blob_str(&cmd));

    /* Delete the temporary file and clean up memory used */
    file_delete(blob_str(&nameFile1));
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
    file_tempname(&temp2, zName, "after");
    blob_write_to_file(pFile1, blob_str(&temp1));
    blob_write_to_file(pFile2, blob_str(&temp2));

    /* Construct the external diff command */
    blob_zero(&cmd);
    blob_append(&cmd, zDiffCmd, -1);
    blob_append_escaped_arg(&cmd, blob_str(&temp1));
    blob_append_escaped_arg(&cmd, blob_str(&temp2));

    /* Run the external diff command */
    fossil_system(blob_str(&cmd));

    /* Delete the temporary file and clean up memory used */
    file_delete(blob_str(&temp1));
    file_delete(blob_str(&temp2));







|
|







355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
    file_tempname(&temp2, zName, "after");
    blob_write_to_file(pFile1, blob_str(&temp1));
    blob_write_to_file(pFile2, blob_str(&temp2));

    /* Construct the external diff command */
    blob_zero(&cmd);
    blob_append(&cmd, zDiffCmd, -1);
    blob_append_escaped_arg(&cmd, blob_str(&temp1), 1);
    blob_append_escaped_arg(&cmd, blob_str(&temp2), 1);

    /* Run the external diff command */
    fossil_system(blob_str(&cmd));

    /* Delete the temporary file and clean up memory used */
    file_delete(blob_str(&temp1));
    file_delete(blob_str(&temp2));
Changes to src/http_transport.c.
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
  char *zHost;       /* The host name to contact */

  socket_ssh_resolve_addr(pUrlData);
  transport_ssh_command(&zCmd);
  if( pUrlData->port!=pUrlData->dfltPort && pUrlData->port ){
    blob_appendf(&zCmd, " -p %d", pUrlData->port);
  }

  if( pUrlData->user && pUrlData->user[0] ){
    zHost = mprintf("%s@%s", pUrlData->user, pUrlData->name);
    blob_append_escaped_arg(&zCmd, zHost);
    fossil_free(zHost);
  }else{
    blob_append_escaped_arg(&zCmd, pUrlData->name);
  }
  if( !is_safe_fossil_command(pUrlData->fossil) ){
    fossil_fatal("the ssh:// URL is asking to run an unsafe command [%s] on "
                 "the server.", pUrlData->fossil);
  }
  blob_append_escaped_arg(&zCmd, pUrlData->fossil);
  blob_append(&zCmd, " test-http", 10);
  if( pUrlData->path && pUrlData->path[0] ){
    blob_append_escaped_arg(&zCmd, pUrlData->path);
  }else{
    fossil_fatal("ssh:// URI does not specify a path to the repository");
  }
  if( g.fSshTrace ){
    fossil_print("%s\n", blob_str(&zCmd));  /* Show the whole SSH command */
  }
  popen2(blob_str(&zCmd), &sshIn, &sshOut, &sshPid, 0);







>


|


|





|


|







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
  char *zHost;       /* The host name to contact */

  socket_ssh_resolve_addr(pUrlData);
  transport_ssh_command(&zCmd);
  if( pUrlData->port!=pUrlData->dfltPort && pUrlData->port ){
    blob_appendf(&zCmd, " -p %d", pUrlData->port);
  }
  blob_appendf(&zCmd, " --");  /* End of switches */
  if( pUrlData->user && pUrlData->user[0] ){
    zHost = mprintf("%s@%s", pUrlData->user, pUrlData->name);
    blob_append_escaped_arg(&zCmd, zHost, 0);
    fossil_free(zHost);
  }else{
    blob_append_escaped_arg(&zCmd, pUrlData->name, 0);
  }
  if( !is_safe_fossil_command(pUrlData->fossil) ){
    fossil_fatal("the ssh:// URL is asking to run an unsafe command [%s] on "
                 "the server.", pUrlData->fossil);
  }
  blob_append_escaped_arg(&zCmd, pUrlData->fossil, 1);
  blob_append(&zCmd, " test-http", 10);
  if( pUrlData->path && pUrlData->path[0] ){
    blob_append_escaped_arg(&zCmd, pUrlData->path, 1);
  }else{
    fossil_fatal("ssh:// URI does not specify a path to the repository");
  }
  if( g.fSshTrace ){
    fossil_print("%s\n", blob_str(&zCmd));  /* Show the whole SSH command */
  }
  popen2(blob_str(&zCmd), &sshIn, &sshOut, &sshPid, 0);
Changes to src/patch.c.
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
    "SELECT patch.cfg.value"
    "  FROM patch.cfg, localdb.vvar"
    " WHERE patch.cfg.key='baseline'"
    "   AND localdb.vvar.name='checkout-hash'"
    "   AND patch.cfg.key<>localdb.vvar.name"
  );
  if( db_step(&q)==SQLITE_ROW ){
    blob_append_escaped_arg(&cmd, g.nameOfExe);
    blob_appendf(&cmd, " update %s", db_column_text(&q, 0));
    if( mFlags & PATCH_VERBOSE ){
      fossil_print("%-10s %s\n", "BASELINE", db_column_text(&q,0));
    }
  }
  db_finalize(&q);
  if( blob_size(&cmd)>0 ){







|







394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
    "SELECT patch.cfg.value"
    "  FROM patch.cfg, localdb.vvar"
    " WHERE patch.cfg.key='baseline'"
    "   AND localdb.vvar.name='checkout-hash'"
    "   AND patch.cfg.key<>localdb.vvar.name"
  );
  if( db_step(&q)==SQLITE_ROW ){
    blob_append_escaped_arg(&cmd, g.nameOfExe, 1);
    blob_appendf(&cmd, " update %s", db_column_text(&q, 0));
    if( mFlags & PATCH_VERBOSE ){
      fossil_print("%-10s %s\n", "BASELINE", db_column_text(&q,0));
    }
  }
  db_finalize(&q);
  if( blob_size(&cmd)>0 ){
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
    db_prepare(&q,
      "SELECT type, mhash, upper(type) FROM patch.patchmerge"
      " WHERE type IN ('merge','cherrypick','backout','integrate')"
      "   AND mhash NOT GLOB '*[^a-fA-F0-9]*';"
    );
    while( db_step(&q)==SQLITE_ROW ){
      const char *zType = db_column_text(&q,0);
      blob_append_escaped_arg(&cmd, g.nameOfExe);
      if( strcmp(zType,"merge")==0 ){
        blob_appendf(&cmd, " merge %s\n", db_column_text(&q,1));
      }else{
        blob_appendf(&cmd, " merge --%s %s\n", zType, db_column_text(&q,1));
      }
      if( mFlags & PATCH_VERBOSE ){
        fossil_print("%-10s %s\n", db_column_text(&q,2), 







|







421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
    db_prepare(&q,
      "SELECT type, mhash, upper(type) FROM patch.patchmerge"
      " WHERE type IN ('merge','cherrypick','backout','integrate')"
      "   AND mhash NOT GLOB '*[^a-fA-F0-9]*';"
    );
    while( db_step(&q)==SQLITE_ROW ){
      const char *zType = db_column_text(&q,0);
      blob_append_escaped_arg(&cmd, g.nameOfExe, 1);
      if( strcmp(zType,"merge")==0 ){
        blob_appendf(&cmd, " merge %s\n", db_column_text(&q,1));
      }else{
        blob_appendf(&cmd, " merge --%s %s\n", zType, db_column_text(&q,1));
      }
      if( mFlags & PATCH_VERBOSE ){
        fossil_print("%-10s %s\n", db_column_text(&q,2), 
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
    blob_reset(&cmd);
  }

  /* Deletions */
  db_prepare(&q, "SELECT pathname FROM patch.chng"
                 " WHERE origname IS NULL AND delta IS NULL");
  while( db_step(&q)==SQLITE_ROW ){
    blob_append_escaped_arg(&cmd, g.nameOfExe);
    blob_appendf(&cmd, " rm --hard %$\n", db_column_text(&q,0));
    if( mFlags & PATCH_VERBOSE ){
      fossil_print("%-10s %s\n", "DELETE", db_column_text(&q,0));
    }
  }
  db_finalize(&q);
  if( blob_size(&cmd)>0 ){







|







449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
    blob_reset(&cmd);
  }

  /* Deletions */
  db_prepare(&q, "SELECT pathname FROM patch.chng"
                 " WHERE origname IS NULL AND delta IS NULL");
  while( db_step(&q)==SQLITE_ROW ){
    blob_append_escaped_arg(&cmd, g.nameOfExe, 1);
    blob_appendf(&cmd, " rm --hard %$\n", db_column_text(&q,0));
    if( mFlags & PATCH_VERBOSE ){
      fossil_print("%-10s %s\n", "DELETE", db_column_text(&q,0));
    }
  }
  db_finalize(&q);
  if( blob_size(&cmd)>0 ){
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
  /* Renames */
  db_prepare(&q,
    "SELECT origname, pathname FROM patch.chng"
    " WHERE origname IS NOT NULL"
    "   AND origname<>pathname"
  );
  while( db_step(&q)==SQLITE_ROW ){
    blob_append_escaped_arg(&cmd, g.nameOfExe);
    blob_appendf(&cmd, " mv --hard %$ %$\n",
        db_column_text(&q,0), db_column_text(&q,1));
    if( mFlags & PATCH_VERBOSE ){
      fossil_print("%-10s %s -> %s\n", "RENAME",
         db_column_text(&q,0), db_column_text(&q,1));
    }
  }







|







476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
  /* Renames */
  db_prepare(&q,
    "SELECT origname, pathname FROM patch.chng"
    " WHERE origname IS NOT NULL"
    "   AND origname<>pathname"
  );
  while( db_step(&q)==SQLITE_ROW ){
    blob_append_escaped_arg(&cmd, g.nameOfExe, 1);
    blob_appendf(&cmd, " mv --hard %$ %$\n",
        db_column_text(&q,0), db_column_text(&q,1));
    if( mFlags & PATCH_VERBOSE ){
      fossil_print("%-10s %s -> %s\n", "RENAME",
         db_column_text(&q,0), db_column_text(&q,1));
    }
  }
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
      blob_reset(&data);
      blob_append(&data, aOut, sz);
      sqlite3_free(aOut);
      if( mFlags & PATCH_VERBOSE ){
        fossil_print("%-10s %s\n", "EDIT", zPathname);
      }
    }else{
      blob_append_escaped_arg(&cmd, g.nameOfExe);
      blob_appendf(&cmd, " add %$\n", zPathname);
      if( mFlags & PATCH_VERBOSE ){
        fossil_print("%-10s %s\n", "NEW", zPathname);
      }
    }
    if( (mFlags & PATCH_DRYRUN)==0 ){   
      if( isLink ){







|







550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
      blob_reset(&data);
      blob_append(&data, aOut, sz);
      sqlite3_free(aOut);
      if( mFlags & PATCH_VERBOSE ){
        fossil_print("%-10s %s\n", "EDIT", zPathname);
      }
    }else{
      blob_append_escaped_arg(&cmd, g.nameOfExe, 1);
      blob_appendf(&cmd, " add %$\n", zPathname);
      if( mFlags & PATCH_VERBOSE ){
        fossil_print("%-10s %s\n", "NEW", zPathname);
      }
    }
    if( (mFlags & PATCH_DRYRUN)==0 ){   
      if( isLink ){
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
    usage(mprintf("%s [USER@]HOST:DIRECTORY", zThisCmd));
  }
  zRemote = fossil_strdup(g.argv[3]);
  zDir = strchr(zRemote,':');
  if( zDir==0 ){
    zDir = zRemote;
    blob_init(&cmd, 0, 0);
    blob_append_escaped_arg(&cmd, g.nameOfExe);
    blob_appendf(&cmd, " patch %s%s %$ -", zRemoteCmd, zForce, zDir);
  }else{
    Blob remote;
    zDir[0] = 0;
    zDir++;
    transport_ssh_command(&cmd);
    blob_append_escaped_arg(&cmd, zRemote);
    blob_init(&remote, 0, 0);
    blob_appendf(&remote, "fossil patch %s%s --dir64 %z -", 
                 zRemoteCmd, zForce, encode64(zDir, -1));
    blob_append_escaped_arg(&cmd, blob_str(&remote));
    blob_reset(&remote);
  }
  if( mFlags & PATCH_VERBOSE ){
    fossil_print("# %s\n", blob_str(&cmd));
    fflush(stdout);
  }
  f = popen(blob_str(&cmd), zRW);







|






|



|







671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
    usage(mprintf("%s [USER@]HOST:DIRECTORY", zThisCmd));
  }
  zRemote = fossil_strdup(g.argv[3]);
  zDir = strchr(zRemote,':');
  if( zDir==0 ){
    zDir = zRemote;
    blob_init(&cmd, 0, 0);
    blob_append_escaped_arg(&cmd, g.nameOfExe, 1);
    blob_appendf(&cmd, " patch %s%s %$ -", zRemoteCmd, zForce, zDir);
  }else{
    Blob remote;
    zDir[0] = 0;
    zDir++;
    transport_ssh_command(&cmd);
    blob_append_escaped_arg(&cmd, zRemote, 0);
    blob_init(&remote, 0, 0);
    blob_appendf(&remote, "fossil patch %s%s --dir64 %z -", 
                 zRemoteCmd, zForce, encode64(zDir, -1));
    blob_append_escaped_arg(&cmd, blob_str(&remote), 0);
    blob_reset(&remote);
  }
  if( mFlags & PATCH_VERBOSE ){
    fossil_print("# %s\n", blob_str(&cmd));
    fflush(stdout);
  }
  f = popen(blob_str(&cmd), zRW);
Changes to src/printf.c.
100
101
102
103
104
105
106
107


108
109
110
111
112
113
114
#define etPATH       21 /* Path type */
#define etWIKISTR    22 /* Timeline comment text rendered from a char*: %W */
#define etSTRINGID   23 /* String with length limit for a hash prefix: %S */
#define etROOT       24 /* String value of g.zTop: %R */
#define etJSONSTR    25 /* String encoded as a JSON string literal: %j
                           Use %!j to include double-quotes around it. */
#define etSHELLESC   26 /* Escape a filename for use in a shell command: %$
                           See blob_append_escaped_arg() for details */




/*
** An "etByte" is an 8-bit unsigned value.
*/
typedef unsigned char etByte;








|
>
>







100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
#define etPATH       21 /* Path type */
#define etWIKISTR    22 /* Timeline comment text rendered from a char*: %W */
#define etSTRINGID   23 /* String with length limit for a hash prefix: %S */
#define etROOT       24 /* String value of g.zTop: %R */
#define etJSONSTR    25 /* String encoded as a JSON string literal: %j
                           Use %!j to include double-quotes around it. */
#define etSHELLESC   26 /* Escape a filename for use in a shell command: %$
                           See blob_append_escaped_arg() for details
                           "%$"  -> adds "./" prefix if necessary.
                           "%!$" -> omits the "./" prefix. */


/*
** An "etByte" is an 8-bit unsigned value.
*/
typedef unsigned char etByte;

836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
        wiki_convert(&wiki, pBlob, wiki_convert_flags(flag_altform2));
        blob_reset(&wiki);
        length = width = 0;
        break;
      }
      case etSHELLESC: {
        char *zArg = va_arg(ap, char*);
        blob_append_escaped_arg(pBlob, zArg);
        length = width = 0;
        break;
      }
      case etERROR:
        buf[0] = '%';
        buf[1] = c;
        errorflag = 0;







|







838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
        wiki_convert(&wiki, pBlob, wiki_convert_flags(flag_altform2));
        blob_reset(&wiki);
        length = width = 0;
        break;
      }
      case etSHELLESC: {
        char *zArg = va_arg(ap, char*);
        blob_append_escaped_arg(pBlob, zArg, !flag_altform2);
        length = width = 0;
        break;
      }
      case etERROR:
        buf[0] = '%';
        buf[1] = c;
        errorflag = 0;
Changes to src/winhttp.c.
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

  blob_zero(&options);
  if( PB("HTTPS") ){
    blob_appendf(&options, " --https");
  }
  if( zBaseUrl ){
    blob_appendf(&options, " --baseurl ");
    blob_append_escaped_arg(&options, zBaseUrl);
  }
  if( zNotFound ){
    blob_appendf(&options, " --notfound ");
    blob_append_escaped_arg(&options, zNotFound);
  }
  if( g.zCkoutAlias ){
    blob_appendf(&options, " --ckout-alias ");
    blob_append_escaped_arg(&options, g.zCkoutAlias);
  }
  if( zFileGlob ){
    blob_appendf(&options, " --files-urlenc %T", zFileGlob);
  }
  if( g.useLocalauth ){
    blob_appendf(&options, " --localauth");
  }
  if( g.thTrace ){
    blob_appendf(&options, " --th-trace");
  }
  if( flags & HTTP_SERVER_REPOLIST ){
    blob_appendf(&options, " --repolist");
  }
  if( g.zExtRoot && g.zExtRoot[0] ){
    blob_appendf(&options, " --extroot");
    blob_append_escaped_arg(&options, g.zExtRoot);
  }
  zSkin = skin_in_use();
  if( zSkin ){
    blob_appendf(&options, " --skin %s", zSkin);
  }
  if( g.zMainMenuFile ){
    blob_appendf(&options, " --mainmenu ");
    blob_append_escaped_arg(&options, g.zMainMenuFile);
  }
#if USE_SEE
  zSavedKey = db_get_saved_encryption_key();
  savedKeySize = db_get_saved_encryption_key_size();
  if( zSavedKey!=0 && savedKeySize>0 ){
    blob_appendf(&options, " --usepidkey %lu:%p:%u", GetCurrentProcessId(),
                 zSavedKey, savedKeySize);







|



|



|















|







|







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

  blob_zero(&options);
  if( PB("HTTPS") ){
    blob_appendf(&options, " --https");
  }
  if( zBaseUrl ){
    blob_appendf(&options, " --baseurl ");
    blob_append_escaped_arg(&options, zBaseUrl, 0);
  }
  if( zNotFound ){
    blob_appendf(&options, " --notfound ");
    blob_append_escaped_arg(&options, zNotFound, 1);
  }
  if( g.zCkoutAlias ){
    blob_appendf(&options, " --ckout-alias ");
    blob_append_escaped_arg(&options, g.zCkoutAlias, 0);
  }
  if( zFileGlob ){
    blob_appendf(&options, " --files-urlenc %T", zFileGlob);
  }
  if( g.useLocalauth ){
    blob_appendf(&options, " --localauth");
  }
  if( g.thTrace ){
    blob_appendf(&options, " --th-trace");
  }
  if( flags & HTTP_SERVER_REPOLIST ){
    blob_appendf(&options, " --repolist");
  }
  if( g.zExtRoot && g.zExtRoot[0] ){
    blob_appendf(&options, " --extroot");
    blob_append_escaped_arg(&options, g.zExtRoot, 1);
  }
  zSkin = skin_in_use();
  if( zSkin ){
    blob_appendf(&options, " --skin %s", zSkin);
  }
  if( g.zMainMenuFile ){
    blob_appendf(&options, " --mainmenu ");
    blob_append_escaped_arg(&options, g.zMainMenuFile, 1);
  }
#if USE_SEE
  zSavedKey = db_get_saved_encryption_key();
  savedKeySize = db_get_saved_encryption_key_size();
  if( zSavedKey!=0 && savedKeySize>0 ){
    blob_appendf(&options, " --usepidkey %lu:%p:%u", GetCurrentProcessId(),
                 zSavedKey, savedKeySize);