Fossil SCM
Use a Cookie, instead of a custom HTTP header and/or URL param, to send the sync login header, as suggested in [forum:9959d2d9d9be22d2 | forum post 9959d2d9d9be22d2]. This is simpler.
Commit
756ad2f23c67a4c817fb39f6d623dc5937bb8a549771fb164807f866e304b214
Parent
780d3b2fe3234fa…
4 files changed
+10
-12
+3
-32
+7
-7
+8
-14
+10
-12
| --- src/cgi.c | ||
| +++ src/cgi.c | ||
| @@ -1285,12 +1285,12 @@ | ||
| 1285 | 1285 | ** Checks the QUERY_STRING environment variable, sets it up via |
| 1286 | 1286 | ** add_param_list() and, if found, applies its "skin" setting. Returns |
| 1287 | 1287 | ** 0 if no QUERY_STRING is set, else it returns a bitmask of: |
| 1288 | 1288 | ** |
| 1289 | 1289 | ** 0x01 = QUERY_STRING was set up |
| 1290 | -** 0x02 = "skin" GET arg was processed | |
| 1291 | -** 0x04 = "x-f-x-l" GET arg was processed. | |
| 1290 | +** 0x02 = "skin" URL param arg was processed | |
| 1291 | +** 0x04 = "x-f-x-l" cookie arg was processed. | |
| 1292 | 1292 | ** |
| 1293 | 1293 | * In the case of the skin, the cookie may still need flushing |
| 1294 | 1294 | ** by the page, via cookie_render(). |
| 1295 | 1295 | */ |
| 1296 | 1296 | int cgi_setup_query_string(void){ |
| @@ -1311,20 +1311,18 @@ | ||
| 1311 | 1311 | ** environment here. */ |
| 1312 | 1312 | cgi_set_parameter_nocopy("udc", "1", 1); |
| 1313 | 1313 | } |
| 1314 | 1314 | fossil_free(zErr); |
| 1315 | 1315 | } |
| 1316 | - if( !g.syncInfo.zLoginCard && 0!=(z=(char*)P("x-f-x-l")) ){ | |
| 1317 | - /* CGI fossil instances do not read the HTTP headers, so | |
| 1318 | - ** they cannot see the X-Fossil-Xfer-Login card. As a consolation | |
| 1319 | - ** to them, we'll accept that via this query argument. */ | |
| 1320 | - rc |= 0x04; | |
| 1321 | - fossil_free( g.syncInfo.zLoginCard ); | |
| 1322 | - g.syncInfo.zLoginCard = fossil_strdup(z); | |
| 1323 | - g.syncInfo.fLoginCardMode |= 0x10; | |
| 1324 | - cgi_delete_parameter("x-f-x-l"); | |
| 1325 | - } | |
| 1316 | + } | |
| 1317 | + if( !g.syncInfo.zLoginCard && 0!=(z=(char*)P("x-f-x-l")) ){ | |
| 1318 | + /* X-Fossil-Xfer-Login card transmitted via cookie instead of in | |
| 1319 | + ** the sync payload. */ | |
| 1320 | + rc |= 0x04; | |
| 1321 | + g.syncInfo.zLoginCard = fossil_strdup(z); | |
| 1322 | + g.syncInfo.fLoginCardMode |= 0x04; | |
| 1323 | + cgi_delete_parameter("x-f-x-l"); | |
| 1326 | 1324 | } |
| 1327 | 1325 | return rc; |
| 1328 | 1326 | } |
| 1329 | 1327 | |
| 1330 | 1328 | /* |
| 1331 | 1329 |
| --- src/cgi.c | |
| +++ src/cgi.c | |
| @@ -1285,12 +1285,12 @@ | |
| 1285 | ** Checks the QUERY_STRING environment variable, sets it up via |
| 1286 | ** add_param_list() and, if found, applies its "skin" setting. Returns |
| 1287 | ** 0 if no QUERY_STRING is set, else it returns a bitmask of: |
| 1288 | ** |
| 1289 | ** 0x01 = QUERY_STRING was set up |
| 1290 | ** 0x02 = "skin" GET arg was processed |
| 1291 | ** 0x04 = "x-f-x-l" GET arg was processed. |
| 1292 | ** |
| 1293 | * In the case of the skin, the cookie may still need flushing |
| 1294 | ** by the page, via cookie_render(). |
| 1295 | */ |
| 1296 | int cgi_setup_query_string(void){ |
| @@ -1311,20 +1311,18 @@ | |
| 1311 | ** environment here. */ |
| 1312 | cgi_set_parameter_nocopy("udc", "1", 1); |
| 1313 | } |
| 1314 | fossil_free(zErr); |
| 1315 | } |
| 1316 | if( !g.syncInfo.zLoginCard && 0!=(z=(char*)P("x-f-x-l")) ){ |
| 1317 | /* CGI fossil instances do not read the HTTP headers, so |
| 1318 | ** they cannot see the X-Fossil-Xfer-Login card. As a consolation |
| 1319 | ** to them, we'll accept that via this query argument. */ |
| 1320 | rc |= 0x04; |
| 1321 | fossil_free( g.syncInfo.zLoginCard ); |
| 1322 | g.syncInfo.zLoginCard = fossil_strdup(z); |
| 1323 | g.syncInfo.fLoginCardMode |= 0x10; |
| 1324 | cgi_delete_parameter("x-f-x-l"); |
| 1325 | } |
| 1326 | } |
| 1327 | return rc; |
| 1328 | } |
| 1329 | |
| 1330 | /* |
| 1331 |
| --- src/cgi.c | |
| +++ src/cgi.c | |
| @@ -1285,12 +1285,12 @@ | |
| 1285 | ** Checks the QUERY_STRING environment variable, sets it up via |
| 1286 | ** add_param_list() and, if found, applies its "skin" setting. Returns |
| 1287 | ** 0 if no QUERY_STRING is set, else it returns a bitmask of: |
| 1288 | ** |
| 1289 | ** 0x01 = QUERY_STRING was set up |
| 1290 | ** 0x02 = "skin" URL param arg was processed |
| 1291 | ** 0x04 = "x-f-x-l" cookie arg was processed. |
| 1292 | ** |
| 1293 | * In the case of the skin, the cookie may still need flushing |
| 1294 | ** by the page, via cookie_render(). |
| 1295 | */ |
| 1296 | int cgi_setup_query_string(void){ |
| @@ -1311,20 +1311,18 @@ | |
| 1311 | ** environment here. */ |
| 1312 | cgi_set_parameter_nocopy("udc", "1", 1); |
| 1313 | } |
| 1314 | fossil_free(zErr); |
| 1315 | } |
| 1316 | } |
| 1317 | if( !g.syncInfo.zLoginCard && 0!=(z=(char*)P("x-f-x-l")) ){ |
| 1318 | /* X-Fossil-Xfer-Login card transmitted via cookie instead of in |
| 1319 | ** the sync payload. */ |
| 1320 | rc |= 0x04; |
| 1321 | g.syncInfo.zLoginCard = fossil_strdup(z); |
| 1322 | g.syncInfo.fLoginCardMode |= 0x04; |
| 1323 | cgi_delete_parameter("x-f-x-l"); |
| 1324 | } |
| 1325 | return rc; |
| 1326 | } |
| 1327 | |
| 1328 | /* |
| 1329 |
+3
-32
| --- src/http.c | ||
| +++ src/http.c | ||
| @@ -128,33 +128,10 @@ | ||
| 128 | 128 | blob_reset(&pw); |
| 129 | 129 | blob_reset(&sig); |
| 130 | 130 | blob_reset(&nonce); |
| 131 | 131 | } |
| 132 | 132 | |
| 133 | -/* | |
| 134 | -** If we're in "login card header" mode, append ?x-f-x-l=ABC to | |
| 135 | -** g.url.path, replacing any "?..." part of g.url.path. ABC = the | |
| 136 | -** %T-encoded contents of pLogin. This is workaround for feeding the | |
| 137 | -** login card to CGI-hosted fossil instances, as those do not read the | |
| 138 | -** HTTP headers so cannot see the X-Fossil-Xfer-Login (x-f-x-l) | |
| 139 | -** header. | |
| 140 | -*/ | |
| 141 | -static void url_append_login_card(Blob * const pLogin){ | |
| 142 | - if( g.syncInfo.fLoginCardMode || | |
| 143 | - g.syncInfo.remoteVersion >= RELEASE_VERSION_NUMBER ){ | |
| 144 | - char * x; | |
| 145 | - char * z = g.url.path; | |
| 146 | - while( z && *z && '?'!=*z ) ++z; | |
| 147 | - if( z && *z ) *z = 0; | |
| 148 | - x = mprintf("%s?x-f-x-l=%T", g.url.path ? g.url.path : "/", | |
| 149 | - blob_str(pLogin)); | |
| 150 | - fossil_free(g.url.path); | |
| 151 | - g.url.path = x; | |
| 152 | - g.syncInfo.fLoginCardMode |= 0x04; | |
| 153 | - } | |
| 154 | -} | |
| 155 | - | |
| 156 | 133 | /* |
| 157 | 134 | ** Construct an appropriate HTTP request header. Write the header |
| 158 | 135 | ** into pHdr. This routine initializes the pHdr blob. pPayload is |
| 159 | 136 | ** the complete payload (including the login card if pLogin is NULL or |
| 160 | 137 | ** empty) already compressed. |
| @@ -166,14 +143,10 @@ | ||
| 166 | 143 | const char *zAltMimetype /* Alternative mimetype */ |
| 167 | 144 | ){ |
| 168 | 145 | int nPayload = pPayload ? blob_size(pPayload) : 0; |
| 169 | 146 | |
| 170 | 147 | blob_zero(pHdr); |
| 171 | - if( nPayload>0 && pLogin && blob_size(pLogin)>0 ){ | |
| 172 | - /* Add login card URL arg for POST requests */ | |
| 173 | - url_append_login_card(pLogin); | |
| 174 | - } | |
| 175 | 148 | blob_appendf(pHdr, "%s %s HTTP/1.0\r\n", |
| 176 | 149 | nPayload>0 ? "POST" : "GET", |
| 177 | 150 | (g.url.path && g.url.path[0]) ? g.url.path : "/"); |
| 178 | 151 | if( g.url.proxyAuth ){ |
| 179 | 152 | blob_appendf(pHdr, "Proxy-Authorization: %s\r\n", g.url.proxyAuth); |
| @@ -185,15 +158,13 @@ | ||
| 185 | 158 | fossil_free(zEncoded); |
| 186 | 159 | } |
| 187 | 160 | blob_appendf(pHdr, "Host: %s\r\n", g.url.hostname); |
| 188 | 161 | blob_appendf(pHdr, "User-Agent: %s\r\n", get_user_agent()); |
| 189 | 162 | if( g.url.isSsh ) blob_appendf(pHdr, "X-Fossil-Transport: SSH\r\n"); |
| 190 | - if( pLogin && blob_size(pLogin) ){ | |
| 191 | - blob_appendf(pHdr, "X-Fossil-Xfer-Login: %b\r\n", pLogin) | |
| 192 | - /* Noting that CGIs can't read headers, but test-http can. If we | |
| 193 | - ** set this _only_ as a URL argument then we lose that info for | |
| 194 | - ** purposes of feeding it back through test-http. */; | |
| 163 | + if( nPayload>0 && pLogin && blob_size(pLogin) ){ | |
| 164 | + /* Add login card via a transient cookie. */ | |
| 165 | + blob_appendf(pHdr, "Cookie: x-f-x-l=%T\r\n", blob_str(pLogin)); | |
| 195 | 166 | } |
| 196 | 167 | if( nPayload ){ |
| 197 | 168 | if( zAltMimetype ){ |
| 198 | 169 | blob_appendf(pHdr, "Content-Type: %s\r\n", zAltMimetype); |
| 199 | 170 | }else if( g.fHttpTrace ){ |
| 200 | 171 |
| --- src/http.c | |
| +++ src/http.c | |
| @@ -128,33 +128,10 @@ | |
| 128 | blob_reset(&pw); |
| 129 | blob_reset(&sig); |
| 130 | blob_reset(&nonce); |
| 131 | } |
| 132 | |
| 133 | /* |
| 134 | ** If we're in "login card header" mode, append ?x-f-x-l=ABC to |
| 135 | ** g.url.path, replacing any "?..." part of g.url.path. ABC = the |
| 136 | ** %T-encoded contents of pLogin. This is workaround for feeding the |
| 137 | ** login card to CGI-hosted fossil instances, as those do not read the |
| 138 | ** HTTP headers so cannot see the X-Fossil-Xfer-Login (x-f-x-l) |
| 139 | ** header. |
| 140 | */ |
| 141 | static void url_append_login_card(Blob * const pLogin){ |
| 142 | if( g.syncInfo.fLoginCardMode || |
| 143 | g.syncInfo.remoteVersion >= RELEASE_VERSION_NUMBER ){ |
| 144 | char * x; |
| 145 | char * z = g.url.path; |
| 146 | while( z && *z && '?'!=*z ) ++z; |
| 147 | if( z && *z ) *z = 0; |
| 148 | x = mprintf("%s?x-f-x-l=%T", g.url.path ? g.url.path : "/", |
| 149 | blob_str(pLogin)); |
| 150 | fossil_free(g.url.path); |
| 151 | g.url.path = x; |
| 152 | g.syncInfo.fLoginCardMode |= 0x04; |
| 153 | } |
| 154 | } |
| 155 | |
| 156 | /* |
| 157 | ** Construct an appropriate HTTP request header. Write the header |
| 158 | ** into pHdr. This routine initializes the pHdr blob. pPayload is |
| 159 | ** the complete payload (including the login card if pLogin is NULL or |
| 160 | ** empty) already compressed. |
| @@ -166,14 +143,10 @@ | |
| 166 | const char *zAltMimetype /* Alternative mimetype */ |
| 167 | ){ |
| 168 | int nPayload = pPayload ? blob_size(pPayload) : 0; |
| 169 | |
| 170 | blob_zero(pHdr); |
| 171 | if( nPayload>0 && pLogin && blob_size(pLogin)>0 ){ |
| 172 | /* Add login card URL arg for POST requests */ |
| 173 | url_append_login_card(pLogin); |
| 174 | } |
| 175 | blob_appendf(pHdr, "%s %s HTTP/1.0\r\n", |
| 176 | nPayload>0 ? "POST" : "GET", |
| 177 | (g.url.path && g.url.path[0]) ? g.url.path : "/"); |
| 178 | if( g.url.proxyAuth ){ |
| 179 | blob_appendf(pHdr, "Proxy-Authorization: %s\r\n", g.url.proxyAuth); |
| @@ -185,15 +158,13 @@ | |
| 185 | fossil_free(zEncoded); |
| 186 | } |
| 187 | blob_appendf(pHdr, "Host: %s\r\n", g.url.hostname); |
| 188 | blob_appendf(pHdr, "User-Agent: %s\r\n", get_user_agent()); |
| 189 | if( g.url.isSsh ) blob_appendf(pHdr, "X-Fossil-Transport: SSH\r\n"); |
| 190 | if( pLogin && blob_size(pLogin) ){ |
| 191 | blob_appendf(pHdr, "X-Fossil-Xfer-Login: %b\r\n", pLogin) |
| 192 | /* Noting that CGIs can't read headers, but test-http can. If we |
| 193 | ** set this _only_ as a URL argument then we lose that info for |
| 194 | ** purposes of feeding it back through test-http. */; |
| 195 | } |
| 196 | if( nPayload ){ |
| 197 | if( zAltMimetype ){ |
| 198 | blob_appendf(pHdr, "Content-Type: %s\r\n", zAltMimetype); |
| 199 | }else if( g.fHttpTrace ){ |
| 200 |
| --- src/http.c | |
| +++ src/http.c | |
| @@ -128,33 +128,10 @@ | |
| 128 | blob_reset(&pw); |
| 129 | blob_reset(&sig); |
| 130 | blob_reset(&nonce); |
| 131 | } |
| 132 | |
| 133 | /* |
| 134 | ** Construct an appropriate HTTP request header. Write the header |
| 135 | ** into pHdr. This routine initializes the pHdr blob. pPayload is |
| 136 | ** the complete payload (including the login card if pLogin is NULL or |
| 137 | ** empty) already compressed. |
| @@ -166,14 +143,10 @@ | |
| 143 | const char *zAltMimetype /* Alternative mimetype */ |
| 144 | ){ |
| 145 | int nPayload = pPayload ? blob_size(pPayload) : 0; |
| 146 | |
| 147 | blob_zero(pHdr); |
| 148 | blob_appendf(pHdr, "%s %s HTTP/1.0\r\n", |
| 149 | nPayload>0 ? "POST" : "GET", |
| 150 | (g.url.path && g.url.path[0]) ? g.url.path : "/"); |
| 151 | if( g.url.proxyAuth ){ |
| 152 | blob_appendf(pHdr, "Proxy-Authorization: %s\r\n", g.url.proxyAuth); |
| @@ -185,15 +158,13 @@ | |
| 158 | fossil_free(zEncoded); |
| 159 | } |
| 160 | blob_appendf(pHdr, "Host: %s\r\n", g.url.hostname); |
| 161 | blob_appendf(pHdr, "User-Agent: %s\r\n", get_user_agent()); |
| 162 | if( g.url.isSsh ) blob_appendf(pHdr, "X-Fossil-Transport: SSH\r\n"); |
| 163 | if( nPayload>0 && pLogin && blob_size(pLogin) ){ |
| 164 | /* Add login card via a transient cookie. */ |
| 165 | blob_appendf(pHdr, "Cookie: x-f-x-l=%T\r\n", blob_str(pLogin)); |
| 166 | } |
| 167 | if( nPayload ){ |
| 168 | if( zAltMimetype ){ |
| 169 | blob_appendf(pHdr, "Content-Type: %s\r\n", zAltMimetype); |
| 170 | }else if( g.fHttpTrace ){ |
| 171 |
+7
-7
| --- src/main.c | ||
| +++ src/main.c | ||
| @@ -295,20 +295,20 @@ | ||
| 295 | 295 | ** header parser (cgi.c), xfer.c, and http.c. */ |
| 296 | 296 | struct { |
| 297 | 297 | char *zLoginCard; /* Inbound "X-Fossil-Xfer-Login" request |
| 298 | 298 | ** header or "x-f-x-l" URL parameter. */ |
| 299 | 299 | int fLoginCardMode; /* If non-0, emit login cards in outbound |
| 300 | - ** requests as a HTTP header or URL | |
| 301 | - ** parameter instead of as part of the | |
| 302 | - ** payload. Gets activated on-demand based | |
| 303 | - ** on xfer traffic contents. Values, for | |
| 300 | + ** requests as a HTTP cookie instead of as | |
| 301 | + ** part of the payload. Gets activated | |
| 302 | + ** on-demand based on xfer traffic | |
| 303 | + ** contents. Values, for | |
| 304 | 304 | ** diagnostic/debugging purposes: 0x01=CLI |
| 305 | 305 | ** --flag, 0x02=http_exchange(), |
| 306 | - ** 0x04=url_append_login_card(), | |
| 306 | + ** 0x04=cgi_setup_query_string(), | |
| 307 | 307 | ** 0x08=cgi_handle_cgi_request(), |
| 308 | - ** 0x10=cgi_setup_query_string(), | |
| 309 | - ** 0x20=page_xfer(), 0x40=client_sync(). */ | |
| 308 | + ** 0x20=page_xfer(), | |
| 309 | + ** 0x40=client_sync(). */ | |
| 310 | 310 | int remoteVersion; /* Remote fossil version. Used for negotiating |
| 311 | 311 | ** how to handle the login card. */ |
| 312 | 312 | } syncInfo; |
| 313 | 313 | #ifdef FOSSIL_ENABLE_JSON |
| 314 | 314 | struct FossilJsonBits { |
| 315 | 315 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -295,20 +295,20 @@ | |
| 295 | ** header parser (cgi.c), xfer.c, and http.c. */ |
| 296 | struct { |
| 297 | char *zLoginCard; /* Inbound "X-Fossil-Xfer-Login" request |
| 298 | ** header or "x-f-x-l" URL parameter. */ |
| 299 | int fLoginCardMode; /* If non-0, emit login cards in outbound |
| 300 | ** requests as a HTTP header or URL |
| 301 | ** parameter instead of as part of the |
| 302 | ** payload. Gets activated on-demand based |
| 303 | ** on xfer traffic contents. Values, for |
| 304 | ** diagnostic/debugging purposes: 0x01=CLI |
| 305 | ** --flag, 0x02=http_exchange(), |
| 306 | ** 0x04=url_append_login_card(), |
| 307 | ** 0x08=cgi_handle_cgi_request(), |
| 308 | ** 0x10=cgi_setup_query_string(), |
| 309 | ** 0x20=page_xfer(), 0x40=client_sync(). */ |
| 310 | int remoteVersion; /* Remote fossil version. Used for negotiating |
| 311 | ** how to handle the login card. */ |
| 312 | } syncInfo; |
| 313 | #ifdef FOSSIL_ENABLE_JSON |
| 314 | struct FossilJsonBits { |
| 315 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -295,20 +295,20 @@ | |
| 295 | ** header parser (cgi.c), xfer.c, and http.c. */ |
| 296 | struct { |
| 297 | char *zLoginCard; /* Inbound "X-Fossil-Xfer-Login" request |
| 298 | ** header or "x-f-x-l" URL parameter. */ |
| 299 | int fLoginCardMode; /* If non-0, emit login cards in outbound |
| 300 | ** requests as a HTTP cookie instead of as |
| 301 | ** part of the payload. Gets activated |
| 302 | ** on-demand based on xfer traffic |
| 303 | ** contents. Values, for |
| 304 | ** diagnostic/debugging purposes: 0x01=CLI |
| 305 | ** --flag, 0x02=http_exchange(), |
| 306 | ** 0x04=cgi_setup_query_string(), |
| 307 | ** 0x08=cgi_handle_cgi_request(), |
| 308 | ** 0x20=page_xfer(), |
| 309 | ** 0x40=client_sync(). */ |
| 310 | int remoteVersion; /* Remote fossil version. Used for negotiating |
| 311 | ** how to handle the login card. */ |
| 312 | } syncInfo; |
| 313 | #ifdef FOSSIL_ENABLE_JSON |
| 314 | struct FossilJsonBits { |
| 315 |
+8
-14
| --- www/sync.wiki | ||
| +++ www/sync.wiki | ||
| @@ -239,24 +239,18 @@ | ||
| 239 | 239 | a sync error. (Prior to 2025-07-21, the protocol permitted multiple |
| 240 | 240 | logins, treating the login as the union of all privileges from all |
| 241 | 241 | login cards. That capability was never used and has been removed.) |
| 242 | 242 | |
| 243 | 243 | As of version 2.27, Fossil supports transfering of the login card |
| 244 | -externally to the request payload in one of the following ways: | |
| 245 | - | |
| 246 | -<ul> | |
| 247 | -<li> URL parameter named "x-f-x-l". | |
| 248 | -<li> An HTTP header named "X-Fossil-Xfer-Login". The caveat for this | |
| 249 | - header is that CGI-hosted fossils cannot see the headers. It | |
| 250 | - works for standalone severs and connections running via fossil's | |
| 251 | - "test-http" mechanism. | |
| 252 | -</ul> | |
| 253 | - | |
| 254 | -It is legal to use both of those approaches together but it is not | |
| 255 | -possible to use either of them with an in-body login card because | |
| 256 | -including an in-body login card would change the login card's value | |
| 257 | -for the header or URL parameter. | |
| 244 | +externally to the request payload via a Cookie HTTP header: | |
| 245 | + | |
| 246 | +<verbatim> | |
| 247 | + Cookie: x-f-x-l=... | |
| 248 | +</verbatim> | |
| 249 | + | |
| 250 | +Where "..." is the URL-encoded login cookie. <code>x-f-x-l</code> is | |
| 251 | +short for X-Fossil-Xfer-Login. | |
| 258 | 252 | |
| 259 | 253 | |
| 260 | 254 | <h3 id="file">3.3 File Cards</h3> |
| 261 | 255 | |
| 262 | 256 | Artifacts are transferred using either "file" cards, or "cfile" |
| 263 | 257 |
| --- www/sync.wiki | |
| +++ www/sync.wiki | |
| @@ -239,24 +239,18 @@ | |
| 239 | a sync error. (Prior to 2025-07-21, the protocol permitted multiple |
| 240 | logins, treating the login as the union of all privileges from all |
| 241 | login cards. That capability was never used and has been removed.) |
| 242 | |
| 243 | As of version 2.27, Fossil supports transfering of the login card |
| 244 | externally to the request payload in one of the following ways: |
| 245 | |
| 246 | <ul> |
| 247 | <li> URL parameter named "x-f-x-l". |
| 248 | <li> An HTTP header named "X-Fossil-Xfer-Login". The caveat for this |
| 249 | header is that CGI-hosted fossils cannot see the headers. It |
| 250 | works for standalone severs and connections running via fossil's |
| 251 | "test-http" mechanism. |
| 252 | </ul> |
| 253 | |
| 254 | It is legal to use both of those approaches together but it is not |
| 255 | possible to use either of them with an in-body login card because |
| 256 | including an in-body login card would change the login card's value |
| 257 | for the header or URL parameter. |
| 258 | |
| 259 | |
| 260 | <h3 id="file">3.3 File Cards</h3> |
| 261 | |
| 262 | Artifacts are transferred using either "file" cards, or "cfile" |
| 263 |
| --- www/sync.wiki | |
| +++ www/sync.wiki | |
| @@ -239,24 +239,18 @@ | |
| 239 | a sync error. (Prior to 2025-07-21, the protocol permitted multiple |
| 240 | logins, treating the login as the union of all privileges from all |
| 241 | login cards. That capability was never used and has been removed.) |
| 242 | |
| 243 | As of version 2.27, Fossil supports transfering of the login card |
| 244 | externally to the request payload via a Cookie HTTP header: |
| 245 | |
| 246 | <verbatim> |
| 247 | Cookie: x-f-x-l=... |
| 248 | </verbatim> |
| 249 | |
| 250 | Where "..." is the URL-encoded login cookie. <code>x-f-x-l</code> is |
| 251 | short for X-Fossil-Xfer-Login. |
| 252 | |
| 253 | |
| 254 | <h3 id="file">3.3 File Cards</h3> |
| 255 | |
| 256 | Artifacts are transferred using either "file" cards, or "cfile" |
| 257 |