Fossil

Check-in [593ceca27d]
Login

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

Overview
Comment:Send the --from argument of the "fossil ui" command encoded as hexadecimal, to work around quoting problems on Windows. [forum:/forumpost/cfc22d41b19a1a96|Forum post cfc22d41b19a1a96].
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 593ceca27dbe17d8d7658c217a771792e3db41fbab4cba5c4cdf80be9a385748
User & Date: drh 2024-12-17 12:38:33.468
References
2024-12-18
18:30
Fix a problem introduced with [593ceca27d]: the blob resize operation may realloc the buffer, so obtain the pointer to the buffer only after the resize to avoid a "use after free". check-in: 36bcaaeee0 user: florian tags: trunk
Context
2024-12-18
05:06
Call `cgi_is_loopback()' before `db_open_local()' when checking for a local `fossil ui' session. The former performs simple string comparison, while the latter crawls the file system for multiple (3) check-out database file names at multiple directory hierarchy levels. The main motivation for this change is to reduce "attack surface" of the /jchunk interface, but also to align `cgi_is_loopback()' vs. `db_open_local()' precedence with usage elsewhere in the code base. check-in: 2dda151c40 user: florian tags: trunk
2024-12-17
12:38
Send the --from argument of the "fossil ui" command encoded as hexadecimal, to work around quoting problems on Windows. [forum:/forumpost/cfc22d41b19a1a96|Forum post cfc22d41b19a1a96]. check-in: 593ceca27d user: drh tags: trunk
06:12
On the new /ckout UI page, output the page footer only once. check-in: 92372ce946 user: florian tags: trunk
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/encode.c.
727
728
729
730
731
732
733


















734
735
736
737
738
739
740
*/
void canonical16(char *z, int n){
  while( *z && n-- ){
    *z = zEncode[zDecode[(*z)&0x7f]&0x1f];
    z++;
  }
}



















/*
** Decode a string encoded using "quoted-printable".
**
**   (1)  "=" followed by two hex digits becomes a single
**        byte specified by the two digits
**







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







727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
*/
void canonical16(char *z, int n){
  while( *z && n-- ){
    *z = zEncode[zDecode[(*z)&0x7f]&0x1f];
    z++;
  }
}

/*
** Decode hexadecimal into a string and return the new string.  Space to
** hold the string is obtained from fossil_malloc() and should be released
** by the caller.
**
** If the input is not hex, return NULL.
*/
char *decode16_dup(const char *zIn){
  int nIn = (int)strlen(zIn);
  char *zOut;
  if( !validate16(zIn, nIn) ) return 0;
  zOut = fossil_malloc(nIn/2+1);
  decode16((const u8*)zIn, (u8*)zOut, nIn);
  zOut[nIn/2] = 0;
  return zOut;
}


/*
** Decode a string encoded using "quoted-printable".
**
**   (1)  "=" followed by two hex digits becomes a single
**        byte specified by the two digits
**
Changes to src/info.c.
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
      ){
        @ <div class='file-change-line'><span>
        @ Changes to %h(zFile)
        @ </span></div>
        if( pCfg ){
          char *zFullFN;
          char *zHexFN;
          int nFullFN;
          zFullFN = file_canonical_name_dup(zLhs);
          nFullFN = (int)strlen(zFullFN);
          zHexFN = fossil_malloc( nFullFN*2 + 5 );
          zHexFN[0] = 'x';
          encode16((const u8*)zFullFN, (u8*)(zHexFN+1), nFullFN);
          zHexFN[1+nFullFN*2] = 0;
          fossil_free(zFullFN);
          pCfg->zLeftHash = zHexFN;
          text_diff(&lhs, &rhs, cgi_output_blob(), pCfg);
          pCfg->zLeftHash = 0;
          fossil_free(zHexFN);
        }
      }







<

<
|
<
<
<







782
783
784
785
786
787
788

789

790



791
792
793
794
795
796
797
      ){
        @ <div class='file-change-line'><span>
        @ Changes to %h(zFile)
        @ </span></div>
        if( pCfg ){
          char *zFullFN;
          char *zHexFN;

          zFullFN = file_canonical_name_dup(zLhs);

          zHexFN = mprintf("x%H", zFullFN);



          fossil_free(zFullFN);
          pCfg->zLeftHash = zHexFN;
          text_diff(&lhs, &rhs, cgi_output_blob(), pCfg);
          pCfg->zLeftHash = 0;
          fossil_free(zHexFN);
        }
      }
817
818
819
820
821
822
823
824



825
826
827
828
829
830
831
** if the web server is run on a loopback interface (in other words, was
** started using "fossil ui" or similar) from within an open check-out.
**
** If the "exbase=PATH" query parameter is provided, then the diff shown
** uses the files in PATH as the baseline.  This is the same as using
** the "--from PATH" argument to the "fossil diff" command-line.  In fact,
** when using "fossil ui --from PATH", the --from argument becomes the value
** of the exbase query parameter for the start page.



**
** Other query parameters related to diffs are also accepted.
*/
void ckout_page(void){
  int vid;
  const char *zHome;          /* Home directory */
  int nHome;







|
>
>
>







812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
** if the web server is run on a loopback interface (in other words, was
** started using "fossil ui" or similar) from within an open check-out.
**
** If the "exbase=PATH" query parameter is provided, then the diff shown
** uses the files in PATH as the baseline.  This is the same as using
** the "--from PATH" argument to the "fossil diff" command-line.  In fact,
** when using "fossil ui --from PATH", the --from argument becomes the value
** of the exbase query parameter for the start page.  Note that if PATH
** is a pure hexadecimal string, it is decoded first before being used as
** the pathname.  Real pathnames should contain at least one directory
** separator character.
**
** Other query parameters related to diffs are also accepted.
*/
void ckout_page(void){
  int vid;
  const char *zHome;          /* Home directory */
  int nHome;
859
860
861
862
863
864
865

866
867
868
869
870
871
872
873

874
875
876
877
878
879
880
  }else{
    style_header("Checkout Status: %h", zCwd);
  }
  render_checkin_context(vid, 0, 0, 0);
  @ <hr>
  zExBase = P("exbase");
  if( zExBase && zExBase[0] ){

    char *zCBase = file_canonical_name_dup(zExBase);
    if( nHome && strncmp(zCBase, zHome, nHome)==0 && zCBase[nHome]=='/' ){
      @ <p>Using external baseline: ~%h(zCBase+nHome)</p>
    }else{
      @ <p>Using external baseline: %h(zCBase)</p>
    }
    ckout_external_base_diff(vid, zCBase);
    fossil_free(zCBase);

  }else{
    ckout_normal_diff(vid);
  }
  style_finish_page();
}

/*







>
|







>







857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
  }else{
    style_header("Checkout Status: %h", zCwd);
  }
  render_checkin_context(vid, 0, 0, 0);
  @ <hr>
  zExBase = P("exbase");
  if( zExBase && zExBase[0] ){
    char *zPath = decode16_dup(zExBase);
    char *zCBase = file_canonical_name_dup(zPath?zPath:zExBase);
    if( nHome && strncmp(zCBase, zHome, nHome)==0 && zCBase[nHome]=='/' ){
      @ <p>Using external baseline: ~%h(zCBase+nHome)</p>
    }else{
      @ <p>Using external baseline: %h(zCBase)</p>
    }
    ckout_external_base_diff(vid, zCBase);
    fossil_free(zCBase);
    fossil_free(zPath);
  }else{
    ckout_normal_diff(vid);
  }
  style_finish_page();
}

/*
Changes to src/main.c.
3295
3296
3297
3298
3299
3300
3301
3302
3303
3304
3305
3306
3307
3308
3309
      fossil_fatal("the argument to --from must be a pathname for"
                   " the \"ui\" command");
    }
    zInitPage = find_option("page", "p", 1);
    if( zInitPage && zInitPage[0]=='/' ) zInitPage++;
    zFossilCmd = find_option("fossilcmd", 0, 1);
    if( zFrom && zInitPage==0 ){
      zInitPage = mprintf("ckout?exbase=%T", zFrom);
    }
  }
  zNotFound = find_option("notfound", 0, 1);
  allowRepoList = find_option("repolist",0,0)!=0;
  if( find_option("nocompress",0,0)!=0 ) g.fNoHttpCompress = 1;
  zAltBase = find_option("baseurl", 0, 1);
  fCreate = find_option("create",0,0)!=0;







|







3295
3296
3297
3298
3299
3300
3301
3302
3303
3304
3305
3306
3307
3308
3309
      fossil_fatal("the argument to --from must be a pathname for"
                   " the \"ui\" command");
    }
    zInitPage = find_option("page", "p", 1);
    if( zInitPage && zInitPage[0]=='/' ) zInitPage++;
    zFossilCmd = find_option("fossilcmd", 0, 1);
    if( zFrom && zInitPage==0 ){
      zInitPage = mprintf("ckout?exbase=%H", zFrom);
    }
  }
  zNotFound = find_option("notfound", 0, 1);
  allowRepoList = find_option("repolist",0,0)!=0;
  if( find_option("nocompress",0,0)!=0 ) g.fNoHttpCompress = 1;
  zAltBase = find_option("baseurl", 0, 1);
  fCreate = find_option("create",0,0)!=0;
Changes to src/printf.c.
103
104
105
106
107
108
109

110
111
112
113
114
115
116
#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;








>







103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
#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. */
#define etHEX        27 /* Encode a string as hexadecimal */


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

140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
** most frequently used conversion types first.
**
** NB: When modifying this table is it vital that you also update the fmtchr[]
** variable to match!!!
*/
static const char aDigits[] = "0123456789ABCDEF0123456789abcdef";
static const char aPrefix[] = "-x0\000X0";
static const char fmtchr[] = "dsgzqQbBWhRtTwFSjcouxXfeEGin%p/$";
static const et_info fmtinfo[] = {
  {  'd', 10, 1, etRADIX,      0,  0 },
  {  's',  0, 4, etSTRING,     0,  0 },
  {  'g',  0, 1, etGENERIC,    30, 0 },
  {  'z',  0, 6, etDYNSTRING,  0,  0 },
  {  'q',  0, 4, etSQLESCAPE,  0,  0 },
  {  'Q',  0, 4, etSQLESCAPE2, 0,  0 },







|







141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
** most frequently used conversion types first.
**
** NB: When modifying this table is it vital that you also update the fmtchr[]
** variable to match!!!
*/
static const char aDigits[] = "0123456789ABCDEF0123456789abcdef";
static const char aPrefix[] = "-x0\000X0";
static const char fmtchr[] = "dsgzqQbBWhRtTwFSjcouxXfeEGin%p/$H";
static const et_info fmtinfo[] = {
  {  'd', 10, 1, etRADIX,      0,  0 },
  {  's',  0, 4, etSTRING,     0,  0 },
  {  'g',  0, 1, etGENERIC,    30, 0 },
  {  'z',  0, 6, etDYNSTRING,  0,  0 },
  {  'q',  0, 4, etSQLESCAPE,  0,  0 },
  {  'Q',  0, 4, etSQLESCAPE2, 0,  0 },
174
175
176
177
178
179
180

181
182
183
184
185
186
187
  {  'G',  0, 1, etGENERIC,    14, 0 },
  {  'i', 10, 1, etRADIX,      0,  0 },
  {  'n',  0, 0, etSIZE,       0,  0 },
  {  '%',  0, 0, etPERCENT,    0,  0 },
  {  'p', 16, 0, etPOINTER,    0,  1 },
  {  '/',  0, 0, etPATH,       0,  0 },
  {  '$',  0, 0, etSHELLESC,   0,  0 },

  {  etERROR, 0,0,0,0,0}  /* Must be last */
};
#define etNINFO count(fmtinfo)

/*
** Verify that the fmtchr[] and fmtinfo[] arrays are in agreement.
**







>







175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
  {  'G',  0, 1, etGENERIC,    14, 0 },
  {  'i', 10, 1, etRADIX,      0,  0 },
  {  'n',  0, 0, etSIZE,       0,  0 },
  {  '%',  0, 0, etPERCENT,    0,  0 },
  {  'p', 16, 0, etPOINTER,    0,  1 },
  {  '/',  0, 0, etPATH,       0,  0 },
  {  '$',  0, 0, etSHELLESC,   0,  0 },
  {  'H',  0, 0, etHEX,        0,  0 },
  {  etERROR, 0,0,0,0,0}  /* Must be last */
};
#define etNINFO count(fmtinfo)

/*
** Verify that the fmtchr[] and fmtinfo[] arrays are in agreement.
**
841
842
843
844
845
846
847










848
849
850
851
852
853
854
        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;
        idx = 1+(c!=0);
        blob_append(pBlob,"%",idx);







>
>
>
>
>
>
>
>
>
>







843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
        break;
      }
      case etSHELLESC: {
        char *zArg = va_arg(ap, char*);
        blob_append_escaped_arg(pBlob, zArg, !flag_altform2);
        length = width = 0;
        break;
      }
      case etHEX: {
        char *zArg = va_arg(ap, char*);
        int szArg = (int)strlen(zArg);
        int szBlob = blob_size(pBlob);
        u8 *aBuf = (u8*)&blob_buffer(pBlob)[szBlob];
        blob_resize(pBlob, szBlob+szArg*2+1);
        encode16((const u8*)zArg, aBuf, szArg);
        length = width = 0;
        break;
      }
      case etERROR:
        buf[0] = '%';
        buf[1] = c;
        errorflag = 0;
        idx = 1+(c!=0);
        blob_append(pBlob,"%",idx);