Fossil

Check-in [286110dec0]
Login

Check-in [286110dec0]

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

Overview
Comment:Doc improvements and internal API renaming for clarity. No functional changes.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | xfer-login-card
Files: files | file ages | folders
SHA3-256: 286110dec0d1b95f82743768b5c7572ec7a20c651fa3d499f60bbdd3c7581cb2
User & Date: stephan 2025-07-24 02:20:36.703
Context
2025-07-24
02:41
Update the change log and sync.wiki for the login card additions. ... (check-in: edfa01d9d2 user: stephan tags: xfer-login-card)
02:20
Doc improvements and internal API renaming for clarity. No functional changes. ... (check-in: 286110dec0 user: stephan tags: xfer-login-card)
01:12
Remove some debug output. ... (check-in: d1b7be2ff8 user: stephan tags: xfer-login-card)
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/cgi.c.
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
static NORETURN void malformed_request(const char *zMsg, ...);

/*
** Checks the QUERY_STRING environment variable, sets it up via
** add_param_list() and, if found, applies its "skin" setting. Returns
** 0 if no QUERY_STRING is set, else it returns a bitmask of:
**
** 0x01 = QUERY_STRING was set.
** 0x02 = "skin" argument was set and processed
** 0x04 = "x-f-x-l" arg was processed.
**
*  In the case of the skin, the cookie may still need flushing
** by the page, via cookie_render().
*/
int cgi_setup_query_string(void){
  int rc = 0;
  char * z = (char*)P("QUERY_STRING");







|
|
|







1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
static NORETURN void malformed_request(const char *zMsg, ...);

/*
** Checks the QUERY_STRING environment variable, sets it up via
** add_param_list() and, if found, applies its "skin" setting. Returns
** 0 if no QUERY_STRING is set, else it returns a bitmask of:
**
** 0x01 = QUERY_STRING was set up
** 0x02 = "skin" GET arg was processed
** 0x04 = "x-f-x-l" GET arg was processed.
**
*  In the case of the skin, the cookie may still need flushing
** by the page, via cookie_render().
*/
int cgi_setup_query_string(void){
  int rc = 0;
  char * z = (char*)P("QUERY_STRING");
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
    if( !g.syncInfo.zLoginCard && 0!=(z=(char*)P("x-f-x-l")) ){
      /* CGI fossil instances do not read the HTTP headers, so
      ** they cannot see the X-Fossil-Xfer-Login card. As a consolation
      ** to them, we'll accept that via this query argument. */
      rc |= 0x04;
      fossil_free( g.syncInfo.zLoginCard );
      g.syncInfo.zLoginCard = fossil_strdup(z);
      g.syncInfo.bLoginCardHeader = 3;
      cgi_delete_parameter("x-f-x-l");
    }
  }
  return rc;
}

/*







|







1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
    if( !g.syncInfo.zLoginCard && 0!=(z=(char*)P("x-f-x-l")) ){
      /* CGI fossil instances do not read the HTTP headers, so
      ** they cannot see the X-Fossil-Xfer-Login card. As a consolation
      ** to them, we'll accept that via this query argument. */
      rc |= 0x04;
      fossil_free( g.syncInfo.zLoginCard );
      g.syncInfo.zLoginCard = fossil_strdup(z);
      g.syncInfo.fLoginCardMode = 3;
      cgi_delete_parameter("x-f-x-l");
    }
  }
  return rc;
}

/*
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
  char *z, *zToken;
  int i;
  const char *zScheme = "http";
  char zLine[2000];     /* A single line of input. */
  g.fullHttpReply = 1;
  g.zReqType = "HTTP";

  /*cgi_setenv("JUST_TESTING1", "cgi_handle_http_request()");*/
  if( cgi_fgets(zLine, sizeof(zLine))==0 ){
    malformed_request("missing header");
  }
  blob_append(&g.httpHeader, zLine, -1);
  cgi_trace(zLine);
  zToken = extract_token(zLine, &z);
  if( zToken==0 ){







<







2139
2140
2141
2142
2143
2144
2145

2146
2147
2148
2149
2150
2151
2152
  char *z, *zToken;
  int i;
  const char *zScheme = "http";
  char zLine[2000];     /* A single line of input. */
  g.fullHttpReply = 1;
  g.zReqType = "HTTP";


  if( cgi_fgets(zLine, sizeof(zLine))==0 ){
    malformed_request("missing header");
  }
  blob_append(&g.httpHeader, zLine, -1);
  cgi_trace(zLine);
  zToken = extract_token(zLine, &z);
  if( zToken==0 ){
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
      if( sscanf(zVal,"bytes=%d-%d",&x1,&x2)==2 && x1>=0 && x1<=x2 ){
        rangeStart = x1;
        rangeEnd = x2+1;
      }
    }else if( fossil_strcmp(zFieldName, "x-fossil-xfer-login:")==0 ){
      fossil_free( g.syncInfo.zLoginCard );
      g.syncInfo.zLoginCard = fossil_strdup(zVal);
      g.syncInfo.bLoginCardHeader = 1;
    }
  }
  cgi_setenv("REQUEST_SCHEME",zScheme);
  cgi_init();
  cgi_trace(0);
}








|







2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
      if( sscanf(zVal,"bytes=%d-%d",&x1,&x2)==2 && x1>=0 && x1<=x2 ){
        rangeStart = x1;
        rangeEnd = x2+1;
      }
    }else if( fossil_strcmp(zFieldName, "x-fossil-xfer-login:")==0 ){
      fossil_free( g.syncInfo.zLoginCard );
      g.syncInfo.zLoginCard = fossil_strdup(zVal);
      g.syncInfo.fLoginCardMode = 1;
    }
  }
  cgi_setenv("REQUEST_SCHEME",zScheme);
  cgi_init();
  cgi_trace(0);
}

Changes to src/http.c.
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
** g.url.path, replacing any "?..." part of g.url.path.  ABC = the
** %T-encoded contents of pLogin.  This is workaround for feeding the
** login card to CGI-hosted fossil instances, as those do not read the
** HTTP headers so cannot see the X-Fossil-Xfer-Login (x-f-x-l)
** header.
*/
static void url_append_login_card(Blob * const pLogin){
  if( g.syncInfo.bLoginCardHeader ||
      g.syncInfo.remoteVersion >= RELEASE_VERSION_NUMBER ){
    char * x;
    char * z = g.url.path;
    while( z && *z && '?'!=*z ) ++z;
    if( z && *z ) *z = 0;
    x = mprintf("%s?x-f-x-l=%T", g.url.path ? g.url.path : "/",
                blob_str(pLogin));
    fossil_free(g.url.path);
    g.url.path = x;
    if( !g.syncInfo.bLoginCardHeader ){
      g.syncInfo.bLoginCardHeader = 4;
    }
  }
}

/*
** Construct an appropriate HTTP request header.  Write the header
** into pHdr.  This routine initializes the pHdr blob.  pPayload is







|









|
|







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
** g.url.path, replacing any "?..." part of g.url.path.  ABC = the
** %T-encoded contents of pLogin.  This is workaround for feeding the
** login card to CGI-hosted fossil instances, as those do not read the
** HTTP headers so cannot see the X-Fossil-Xfer-Login (x-f-x-l)
** header.
*/
static void url_append_login_card(Blob * const pLogin){
  if( g.syncInfo.fLoginCardMode ||
      g.syncInfo.remoteVersion >= RELEASE_VERSION_NUMBER ){
    char * x;
    char * z = g.url.path;
    while( z && *z && '?'!=*z ) ++z;
    if( z && *z ) *z = 0;
    x = mprintf("%s?x-f-x-l=%T", g.url.path ? g.url.path : "/",
                blob_str(pLogin));
    fossil_free(g.url.path);
    g.url.path = x;
    if( !g.syncInfo.fLoginCardMode ){
      g.syncInfo.fLoginCardMode = 4;
    }
  }
}

/*
** Construct an appropriate HTTP request header.  Write the header
** into pHdr.  This routine initializes the pHdr blob.  pPayload is
187
188
189
190
191
192
193
194


195
196
197
198
199
200
201
    fossil_free(zEncoded);
  }
  blob_appendf(pHdr, "Host: %s\r\n", g.url.hostname);
  blob_appendf(pHdr, "User-Agent: %s\r\n", get_user_agent());
  if( g.url.isSsh ) blob_appendf(pHdr, "X-Fossil-Transport: SSH\r\n");
  if( pLogin && blob_size(pLogin) ){
    blob_appendf(pHdr, "X-Fossil-Xfer-Login: %b\r\n", pLogin)
      /* Noting that CGIs can't read headers */;


  }
  if( nPayload ){
    if( zAltMimetype ){
      blob_appendf(pHdr, "Content-Type: %s\r\n", zAltMimetype);
    }else if( g.fHttpTrace ){
      blob_appendf(pHdr, "Content-Type: application/x-fossil-debug\r\n");
    }else{







|
>
>







187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
    fossil_free(zEncoded);
  }
  blob_appendf(pHdr, "Host: %s\r\n", g.url.hostname);
  blob_appendf(pHdr, "User-Agent: %s\r\n", get_user_agent());
  if( g.url.isSsh ) blob_appendf(pHdr, "X-Fossil-Transport: SSH\r\n");
  if( pLogin && blob_size(pLogin) ){
    blob_appendf(pHdr, "X-Fossil-Xfer-Login: %b\r\n", pLogin)
      /* Noting that CGIs can't read headers, but test-http can. If we
      ** set this _only_ as a URL argument then we lose that info for
      ** purposes of feeding it back through test-http. */;
  }
  if( nPayload ){
    if( zAltMimetype ){
      blob_appendf(pHdr, "Content-Type: %s\r\n", zAltMimetype);
    }else if( g.fHttpTrace ){
      blob_appendf(pHdr, "Content-Type: application/x-fossil-debug\r\n");
    }else{
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
  }
  /* Construct the login card and prepare the complete payload */
  blob_zero(&login);
  if( blob_size(pSend)==0 ){
    blob_zero(&payload);
  }else{
    if( mHttpFlags & HTTP_USE_LOGIN ) http_build_login_card(pSend, &login);
    if( g.syncInfo.bLoginCardHeader>0 ){
      /* The login card will be sent via an HTTP header and/or URL flag. */
      if( g.fHttpTrace || (mHttpFlags & HTTP_NOCOMPRESS)!=0 ){
        /* Maintenance note: we cannot blob_swap(pSend,&payload) here
        ** because the HTTP 401 and redirect response handling below
        ** needs pSend unmodified. payload won't be modified after
        ** this point, so we can make it a proxy for pSend for
        ** zero heap memory. */







|







501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
  }
  /* Construct the login card and prepare the complete payload */
  blob_zero(&login);
  if( blob_size(pSend)==0 ){
    blob_zero(&payload);
  }else{
    if( mHttpFlags & HTTP_USE_LOGIN ) http_build_login_card(pSend, &login);
    if( g.syncInfo.fLoginCardMode>0 ){
      /* The login card will be sent via an HTTP header and/or URL flag. */
      if( g.fHttpTrace || (mHttpFlags & HTTP_NOCOMPRESS)!=0 ){
        /* Maintenance note: we cannot blob_swap(pSend,&payload) here
        ** because the HTTP 401 and redirect response handling below
        ** needs pSend unmodified. payload won't be modified after
        ** this point, so we can make it a proxy for pSend for
        ** zero heap memory. */
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
        }else if( fossil_strnicmp(&zLine[14], "application/x-fossil", -1)!=0 ){
          isError = 1;
        }
      }
    }else if( fossil_strnicmp(zLine, "x-fossil-xfer-login: ", 21)==0 ){
      fossil_free( g.syncInfo.zLoginCard );
      g.syncInfo.zLoginCard = fossil_strdup(&zLine[21]);
      g.syncInfo.bLoginCardHeader = 1;
    }
  }
  if( iHttpVersion<0 ){
    /* We got nothing back from the server.  If using the ssh: protocol,
    ** this might mean we need to add or remove the PATH=... argument
    ** to the SSH command being sent.  If that is the case, retry the
    ** request after adding or removing the PATH= argument.







|







694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
        }else if( fossil_strnicmp(&zLine[14], "application/x-fossil", -1)!=0 ){
          isError = 1;
        }
      }
    }else if( fossil_strnicmp(zLine, "x-fossil-xfer-login: ", 21)==0 ){
      fossil_free( g.syncInfo.zLoginCard );
      g.syncInfo.zLoginCard = fossil_strdup(&zLine[21]);
      g.syncInfo.fLoginCardMode = 1;
    }
  }
  if( iHttpVersion<0 ){
    /* We got nothing back from the server.  If using the ssh: protocol,
    ** this might mean we need to add or remove the PATH=... argument
    ** to the SSH command being sent.  If that is the case, retry the
    ** request after adding or removing the PATH= argument.
Changes to src/main.c.
290
291
292
293
294
295
296
297
298
299
300

301
302
303
304
305
306
307
308
309
310
311
312
313
314
  int nPendingRequest;           /* # of HTTP requests in "fossil server" */
  int nRequest;                  /* Total # of HTTP request */
  int bAvoidDeltaManifests;      /* Avoid using delta manifests if true */

  /* State for communicating specific details between the inbound HTTP
  ** header parser (cgi.c), xfer.c, and http.c. */
  struct {
    char *zLoginCard;       /* Inbound X-Fossil-Xfer-Login request header
                            ** or x-f-x-l URL parameter. */
    int bLoginCardHeader;   /* If non-0, emit login cards in outbound
                            ** requests as HTTP headers instead of as

                            ** part of the payload. Gets activated
                            ** on-demand based on xfer traffic
                            ** contents. Values, for
                            ** diagnostic/debuggin purposes: 1=set via
                            ** CLI --flag. 2=set via inbound HTTP
                            ** header. 3=set via query string
                            ** arg. 4=set via http_build_header(). */
    int remoteVersion;      /* Remote fossil version. Used for negotiating
                            ** how to handle the login card. */
  } syncInfo;
#ifdef FOSSIL_ENABLE_JSON
  struct FossilJsonBits {
    int isJsonMode;            /* True if running in JSON mode, else
                                  false. This changes how errors are







|
|
|
|
>
|
|
<
|
|
<
|







290
291
292
293
294
295
296
297
298
299
300
301
302
303

304
305

306
307
308
309
310
311
312
313
  int nPendingRequest;           /* # of HTTP requests in "fossil server" */
  int nRequest;                  /* Total # of HTTP request */
  int bAvoidDeltaManifests;      /* Avoid using delta manifests if true */

  /* State for communicating specific details between the inbound HTTP
  ** header parser (cgi.c), xfer.c, and http.c. */
  struct {
    char *zLoginCard;       /* Inbound "X-Fossil-Xfer-Login" request
                            ** header or "x-f-x-l" URL parameter. */
    int fLoginCardMode;     /* If non-0, emit login cards in outbound
                            ** requests as a HTTP header or URL
                            ** parameter instead of as part of the
                            ** payload. Gets activated on-demand based
                            ** on xfer traffic contents. Values, for

                            ** diagnostic/debugging purposes: 1=CLI
                            ** --flag. 2=inbound HTTP header. 3=query

                            ** string arg. 4=http_build_header(). */
    int remoteVersion;      /* Remote fossil version. Used for negotiating
                            ** how to handle the login card. */
  } syncInfo;
#ifdef FOSSIL_ENABLE_JSON
  struct FossilJsonBits {
    int isJsonMode;            /* True if running in JSON mode, else
                                  false. This changes how errors are
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
#ifdef FOSSIL_ENABLE_TCL
  memset(&g.tcl, 0, sizeof(TclContext));
  g.tcl.argc = g.argc;
  g.tcl.argv = copy_args(g.argc, g.argv); /* save full arguments */
#endif
  g.mainTimerId = fossil_timer_start();
  capture_case_sensitive_option();
  g.syncInfo.bLoginCardHeader =
    /* This is only for facilitating development of the
    ** xfer-login-card branch. It will be removed or re-imagined at
    ** some point. */
    !!find_option("login-card-header","lch", 0);
  g.zVfsName = find_option("vfs",0,1);
  if( g.zVfsName==0 ){
    g.zVfsName = fossil_getenv("FOSSIL_VFS");







|







773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
#ifdef FOSSIL_ENABLE_TCL
  memset(&g.tcl, 0, sizeof(TclContext));
  g.tcl.argc = g.argc;
  g.tcl.argv = copy_args(g.argc, g.argv); /* save full arguments */
#endif
  g.mainTimerId = fossil_timer_start();
  capture_case_sensitive_option();
  g.syncInfo.fLoginCardMode =
    /* This is only for facilitating development of the
    ** xfer-login-card branch. It will be removed or re-imagined at
    ** some point. */
    !!find_option("login-card-header","lch", 0);
  g.zVfsName = find_option("vfs",0,1);
  if( g.zVfsName==0 ){
    g.zVfsName = fossil_getenv("FOSSIL_VFS");