Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
| Comment: | Get sync working from both login card forms and add a temporary --login-card-header CLI flag to force it to emit the HTTP header form of the card in output requests. If all is well, this checkin should be able to push to the canonical repo, despite their differences. |
|---|---|
| Downloads: | Tarball | ZIP archive |
| Timelines: | family | ancestors | descendants | both | xfer-login-card |
| Files: | files | file ages | folders |
| SHA3-256: |
042560df547a44ae91d03cb0bd791f92 |
| User & Date: | stephan 2025-07-22 15:12:41.690 |
Context
|
2025-07-22
| ||
| 15:41 | Enable the HTTP login header if the xfer server-version is high enough, analog to the same check for the client-version. check-in: bc3ad94411 user: stephan tags: xfer-login-card | |
| 15:12 | Get sync working from both login card forms and add a temporary --login-card-header CLI flag to force it to emit the HTTP header form of the card in output requests. If all is well, this checkin should be able to push to the canonical repo, despite their differences. check-in: 042560df54 user: stephan tags: xfer-login-card | |
| 02:32 | The previous checkin left me unable to push because (of course) the remote trunk doesn't know how to use the login card header. This checkin disables, via a macro toggle, the use of that header on outbound sync requests. check-in: cb42278d84 user: stephan tags: xfer-login-card | |
Changes
Changes to src/cgi.c.
| ︙ | ︙ | |||
2220 2221 2222 2223 2224 2225 2226 |
int x1 = 0;
int x2 = 0;
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 ){
| | | | 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 |
int x1 = 0;
int x2 = 0;
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 ){
g.syncInfo.zLoginCard = fossil_strdup(zVal);
/*fprintf(stderr, "X-Fossil-Xfer-Login: %s\n", g.syncInfo.zLoginCard);*/
}
}
cgi_setenv("REQUEST_SCHEME",zScheme);
cgi_init();
cgi_trace(0);
}
|
| ︙ | ︙ |
Changes to src/http.c.
| ︙ | ︙ | |||
50 51 52 53 54 55 56 | /* ** Construct the "login" card with the client credentials. ** ** login LOGIN NONCE SIGNATURE ** ** The LOGIN is the user id of the client. NONCE is the sha1 checksum | | | > | > | > | | | 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
/*
** Construct the "login" card with the client credentials.
**
** login LOGIN NONCE SIGNATURE
**
** The LOGIN is the user id of the client. NONCE is the sha1 checksum
** of all payload that follows the login card. Randomness for the
** NONCE must be provided in the payload (in xfer.c) (e.g. by
** appending a timestamp or random bytes as a comment line to the
** payload). SIGNATURE is the sha1 checksum of the nonce followed by
** the fossil-hashed version of the user's password.
**
** Write the constructed login card into pLogin. The result does not
** have an EOL added to it because which type of EOL it needs has to
** be determined later. pLogin is initialized by this routine.
*/
static void http_build_login_card(Blob * const pPayload, Blob * const pLogin){
Blob nonce; /* The nonce */
const char *zLogin; /* The user login name */
const char *zPw; /* The user password */
Blob pw; /* The nonce with user password appended */
Blob sig; /* The signature field */
blob_zero(pLogin);
|
| ︙ | ︙ | |||
126 127 128 129 130 131 132 | blob_reset(&sig); blob_reset(&nonce); } /* ** Construct an appropriate HTTP request header. Write the header ** into pHdr. This routine initializes the pHdr blob. pPayload is | | > < > | | 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 168 169 170 |
blob_reset(&sig);
blob_reset(&nonce);
}
/*
** Construct an appropriate HTTP request header. Write the header
** into pHdr. This routine initializes the pHdr blob. pPayload is
** the complete payload (including the login card if pLogin is NULL or
** empty) already compressed.
*/
static void http_build_header(
Blob *pPayload, /* the payload that will be sent */
Blob *pHdr, /* construct the header here */
Blob *pLogin, /* Login card header value or NULL */
const char *zAltMimetype /* Alternative mimetype */
){
int nPayload = pPayload ? blob_size(pPayload) : 0;
blob_zero(pHdr);
blob_appendf(pHdr, "%s %s%s HTTP/1.0\r\n",
nPayload>0 ? "POST" : "GET", g.url.path,
g.url.path[0]==0 ? "/" : "");
if( g.url.proxyAuth ){
blob_appendf(pHdr, "Proxy-Authorization: %s\r\n", g.url.proxyAuth);
}
if( g.zHttpAuth && g.zHttpAuth[0] ){
const char *zCredentials = g.zHttpAuth;
char *zEncoded = encode64(zCredentials, -1);
blob_appendf(pHdr, "Authorization: Basic %s\r\n", zEncoded);
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);
}
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");
|
| ︙ | ︙ | |||
466 467 468 469 470 471 472 |
/* 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);
| < | > > > > > > | > | > > > > > | | > | | | | > | | | | | | | | | | | < > < < < < | < < | 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 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 |
/* 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 0
fprintf(stderr, "# g.syncInfo.bLoginCardHeader=%d login card=%s\n",
g.syncInfo.bLoginCardHeader,
blob_size(&login) ? blob_str(&login) : "<empty>");
#endif
if( g.syncInfo.bLoginCardHeader ) {
/* Send the login card as an HTTP header. */
if( g.fHttpTrace || (mHttpFlags & HTTP_NOCOMPRESS)!=0 ){
#if 1
/*blob_append(&payload, blob_buffer(pSend), blob_size(pSend));*/
blob_init(&payload, blob_buffer(pSend), blob_size(pSend));
#else
/* This could save memory but looks like it would break in a
** couple of cases in the loop below where pSend is referenced
** for HTTP 401 and redirects. */
blob_zero(&payload);
blob_swap(pSend, &payload);
#endif
}else{
blob_compress(pSend, &payload);
}
}else{
/* Prepend the login card (if set) to the payload */
if( blob_size(&login) ){
blob_append_char(&login, '\n');
}
if( g.fHttpTrace || (mHttpFlags & HTTP_NOCOMPRESS)!=0 ){
payload = login;
login = empty_blob/*transfer ownership*/;
blob_append(&payload, blob_buffer(pSend), blob_size(pSend));
}else{
blob_compress2(&login, pSend, &payload);
blob_reset(&login);
}
}
}
/* Construct the HTTP request header */
http_build_header(&payload, &hdr, &login, zAltMimetype);
/* When tracing, write the transmitted HTTP message both to standard
** output and into a file. The file can then be used to drive the
** server-side like this:
**
** ./fossil test-http <http-request-1.txt
*/
|
| ︙ | ︙ |
Changes to src/main.c.
| ︙ | ︙ | |||
228 229 230 231 232 233 234 |
** SSL client identity */
const char *zCgiFile; /* Name of the CGI file */
const char *zReqType; /* Type of request: "HTTP", "CGI", "SCGI" */
#if USE_SEE
const char *zPidKey; /* Saved value of the --usepidkey option. Only
* applicable when using SEE on Windows or Linux. */
#endif
| < | 228 229 230 231 232 233 234 235 236 237 238 239 240 241 |
** SSL client identity */
const char *zCgiFile; /* Name of the CGI file */
const char *zReqType; /* Type of request: "HTTP", "CGI", "SCGI" */
#if USE_SEE
const char *zPidKey; /* Saved value of the --usepidkey option. Only
* applicable when using SEE on Windows or Linux. */
#endif
int useLocalauth; /* No login required if from 127.0.0.1 */
int noPswd; /* Logged in without password (on 127.0.0.1) */
int userUid; /* Integer user id */
int isHuman; /* True if access by a human, not a spider or bot */
int comFmtFlags; /* Zero or more "COMMENT_PRINT_*" bit flags, should be
** accessed through get_comment_format(). */
const char *zSockName; /* Name of the unix-domain socket file */
|
| ︙ | ︙ | |||
287 288 289 290 291 292 293 294 295 296 297 298 299 300 |
const char **azAuxOpt[MX_AUX]; /* Options of each option() value */
int anAuxCols[MX_AUX]; /* Number of columns for option() values */
int allowSymlinks; /* Cached "allow-symlinks" option */
int mainTimerId; /* Set to fossil_timer_start() */
int nPendingRequest; /* # of HTTP requests in "fossil server" */
int nRequest; /* Total # of HTTP request */
int bAvoidDeltaManifests; /* Avoid using delta manifests if true */
#ifdef FOSSIL_ENABLE_JSON
struct FossilJsonBits {
int isJsonMode; /* True if running in JSON mode, else
false. This changes how errors are
reported. In JSON mode we try to
always output JSON-form error
responses and always (in CGI mode)
| > > > > > | 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 |
const char **azAuxOpt[MX_AUX]; /* Options of each option() value */
int anAuxCols[MX_AUX]; /* Number of columns for option() values */
int allowSymlinks; /* Cached "allow-symlinks" option */
int mainTimerId; /* Set to fossil_timer_start() */
int nPendingRequest; /* # of HTTP requests in "fossil server" */
int nRequest; /* Total # of HTTP request */
int bAvoidDeltaManifests; /* Avoid using delta manifests if true */
struct {
char *zLoginCard; /* X-Fossil-Xfer-Login request header value */
int bLoginCardHeader; /* If true, emit login cards as HTTP headers
** instead of as part of the payload */
} syncInfo;
#ifdef FOSSIL_ENABLE_JSON
struct FossilJsonBits {
int isJsonMode; /* True if running in JSON mode, else
false. This changes how errors are
reported. In JSON mode we try to
always output JSON-form error
responses and always (in CGI mode)
|
| ︙ | ︙ | |||
757 758 759 760 761 762 763 764 765 766 767 768 769 770 |
#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.zVfsName = find_option("vfs",0,1);
if( g.zVfsName==0 ){
g.zVfsName = fossil_getenv("FOSSIL_VFS");
}
if( g.zVfsName ){
sqlite3_vfs *pVfs = sqlite3_vfs_find(g.zVfsName);
if( pVfs ){
| > > > > > | 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 |
#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");
}
if( g.zVfsName ){
sqlite3_vfs *pVfs = sqlite3_vfs_find(g.zVfsName);
if( pVfs ){
|
| ︙ | ︙ |
Changes to src/xfer.c.
| ︙ | ︙ | |||
1328 1329 1330 1331 1332 1333 1334 |
nErr++;
}
zScript = xfer_push_code();
if( zScript ){ /* NOTE: Are TH1 transfer hooks enabled? */
pzUuidList = &zUuidList;
pnUuidList = &nUuidList;
}
| | | | | | | > | 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 |
nErr++;
}
zScript = xfer_push_code();
if( zScript ){ /* NOTE: Are TH1 transfer hooks enabled? */
pzUuidList = &zUuidList;
pnUuidList = &nUuidList;
}
if( g.syncInfo.zLoginCard ){
/* Login card received via HTTP header X-Fossil-Xfer-Login */
blob_zero(&xfer.line);
blob_append(&xfer.line, g.syncInfo.zLoginCard, -1);
xfer.nToken = blob_tokenize(&xfer.line, xfer.aToken,
count(xfer.aToken));
#if 0
fprintf(stderr,"%s:%d: g.syncInfo.zLoginCard=[%s]\nnToken=%d tok[0]=%s line=%s\n",
__FILE__, __LINE__, g.syncInfo.zLoginCard,
xfer.nToken, xfer.nToken ? blob_str(&xfer.aToken[0]) : "<NULL>",
blob_str(&xfer.line));
#endif
fossil_free( g.syncInfo.zLoginCard );
g.syncInfo.zLoginCard = 0;
if( xfer.nToken==4
&& blob_eq(&xfer.aToken[0], "login") ){
g.syncInfo.bLoginCardHeader = 1;
goto handle_login_card;
}
}
while( blob_line(xfer.pIn, &xfer.line) ){
if( blob_buffer(&xfer.line)[0]=='#' ) continue;
if( blob_size(&xfer.line)==0 ) continue;
xfer.nToken = blob_tokenize(&xfer.line, xfer.aToken, count(xfer.aToken));
|
| ︙ | ︙ | |||
1602 1603 1604 1605 1606 1607 1608 |
}else if( nLogin > 1 ){
cgi_reset_content();
@ error multiple\slogin\cards
nErr++;
break;
}else{
#if 0
| | > > > > > | 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 |
}else if( nLogin > 1 ){
cgi_reset_content();
@ error multiple\slogin\cards
nErr++;
break;
}else{
#if 0
fprintf(stderr, "# handle_login_card: aToken[2]=[%.*s]\n",
blob_size(&xfer.aToken[2]),
blob_str(&xfer.aToken[2]));
#endif
if( check_tail_hash(&xfer.aToken[2], xfer.pIn)
|| check_login(&xfer.aToken[1], &xfer.aToken[2], &xfer.aToken[3])
){
cgi_reset_content();
@ error login\sfailed
nErr++;
break;
}
#if 0
fprintf(stderr, "# logged in as [%.*s]\n",
blob_size(&xfer.aToken[1]),
blob_str(&xfer.aToken[1]));
#endif
}
}else
/* reqconfig NAME
**
** Client is requesting a configuration value from the server
*/
|
| ︙ | ︙ | |||
1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 |
**
** The client announces to the server what version of Fossil it
** is running. The DATE and TIME are a pure numeric ISO8601 time
** for the specific check-in of the client.
*/
if( xfer.nToken>=3 && blob_eq(&xfer.aToken[1], "client-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 server-version %d(RELEASE_VERSION_NUMBER) \
@ %d(MANIFEST_NUMERIC_DATE) %d(MANIFEST_NUMERIC_TIME)
}
}else
| > > | 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 |
**
** The client announces to the server what version of Fossil it
** is running. The DATE and TIME are a pure numeric ISO8601 time
** for the specific check-in of the client.
*/
if( xfer.nToken>=3 && blob_eq(&xfer.aToken[1], "client-version") ){
xfer.remoteVersion = atoi(blob_str(&xfer.aToken[2]));
g.syncInfo.bLoginCardHeader =
xfer.remoteVersion>=RELEASE_VERSION_NUMBER;
if( xfer.nToken>=5 ){
xfer.remoteDate = atoi(blob_str(&xfer.aToken[3]));
xfer.remoteTime = atoi(blob_str(&xfer.aToken[4]));
@ pragma server-version %d(RELEASE_VERSION_NUMBER) \
@ %d(MANIFEST_NUMERIC_DATE) %d(MANIFEST_NUMERIC_TIME)
}
}else
|
| ︙ | ︙ |