Fossil SCM

Fix the HTTP-reply parser so that it is able to deal with replies that lack a Content-Length header field. This resolves the issue reported by [forum:/forumpost/12ac403fd29cfc89|forum post 12ac403fd29cfc89]. Also in this merge: (1) Add the --xverbose option to "fossil clone". (2) Improved error messages when web servers misbehave. See also my misguided and incorrect [https://bz.apache.org/bugzilla/show_bug.cgi?id=68905|Apache bug 68905]. Special thanks to Apache devs for setting me straight.

drh 2024-04-17 12:58 trunk merge
Commit a8e33fb161f45b65167f0dfe39b6fcbad21f5844ee469131fd8fa8fc09cd5e99
4 files changed +33 -10 +13 -1 +57 -9 +10 -1
+33 -10
--- src/cgi.c
+++ src/cgi.c
@@ -1238,11 +1238,11 @@
12381238
}
12391239
fputs(z, pLog);
12401240
}
12411241
12421242
/* Forward declaration */
1243
-static NORETURN void malformed_request(const char *zMsg);
1243
+static NORETURN void malformed_request(const char *zMsg, ...);
12441244
12451245
/*
12461246
** Checks the QUERY_STRING environment variable, sets it up
12471247
** via add_param_list() and, if found, applies its "skin"
12481248
** setting. Returns 0 if no QUERY_STRING is set, 1 if it is,
@@ -1321,10 +1321,11 @@
13211321
char *zSemi;
13221322
int len;
13231323
const char *zRequestUri = cgi_parameter("REQUEST_URI",0);
13241324
const char *zScriptName = cgi_parameter("SCRIPT_NAME",0);
13251325
const char *zPathInfo = cgi_parameter("PATH_INFO",0);
1326
+ const char *zContentLength = 0;
13261327
#ifdef _WIN32
13271328
const char *zServerSoftware = cgi_parameter("SERVER_SOFTWARE",0);
13281329
#endif
13291330
13301331
#ifdef FOSSIL_ENABLE_JSON
@@ -1418,11 +1419,19 @@
14181419
z = (char*)P("REMOTE_ADDR");
14191420
if( z ){
14201421
g.zIpAddr = fossil_strdup(z);
14211422
}
14221423
1423
- len = atoi(PD("CONTENT_LENGTH", "0"));
1424
+ zContentLength = P("CONTENT_LENGTH");
1425
+ if( zContentLength==0 ){
1426
+ len = 0;
1427
+ if( sqlite3_stricmp(PD("REQUEST_METHOD",""),"POST")==0 ){
1428
+ malformed_request("missing CONTENT_LENGTH on a POST method");
1429
+ }
1430
+ }else{
1431
+ len = atoi(zContentLength);
1432
+ }
14241433
zType = P("CONTENT_TYPE");
14251434
zSemi = zType ? strchr(zType, ';') : 0;
14261435
if( zSemi ){
14271436
g.zContentType = fossil_strndup(zType, (int)(zSemi-zType));
14281437
zType = g.zContentType;
@@ -1912,15 +1921,26 @@
19121921
19131922
19141923
/*
19151924
** Send a reply indicating that the HTTP request was malformed
19161925
*/
1917
-static NORETURN void malformed_request(const char *zMsg){
1918
- cgi_set_status(501, "Not Implemented");
1919
- cgi_printf(
1920
- "<html><body><p>Bad Request: %s</p></body></html>\n", zMsg
1921
- );
1926
+static NORETURN void malformed_request(const char *zMsg, ...){
1927
+ va_list ap;
1928
+ char *z;
1929
+ va_start(ap, zMsg);
1930
+ z = vmprintf(zMsg, ap);
1931
+ va_end(ap);
1932
+ cgi_set_status(400, "Bad Request");
1933
+ zContentType = "text/plain";
1934
+ if( g.zReqType==0 ) g.zReqType = "WWW";
1935
+ if( g.zReqType[0]=='C' && PD("SERVER_SOFTWARE",0)!=0 ){
1936
+ const char *zServer = PD("SERVER_SOFTWARE","");
1937
+ cgi_printf("Bad CGI Request from \"%s\": %s\n",zServer,z);
1938
+ }else{
1939
+ cgi_printf("Bad %s Request: %s\n", g.zReqType, z);
1940
+ }
1941
+ fossil_free(z);
19221942
cgi_reply();
19231943
fossil_exit(0);
19241944
}
19251945
19261946
/*
@@ -2033,12 +2053,13 @@
20332053
char *z, *zToken;
20342054
int i;
20352055
const char *zScheme = "http";
20362056
char zLine[2000]; /* A single line of input. */
20372057
g.fullHttpReply = 1;
2058
+ g.zReqType = "HTTP";
20382059
if( cgi_fgets(zLine, sizeof(zLine))==0 ){
2039
- malformed_request("missing HTTP header");
2060
+ malformed_request("missing header");
20402061
}
20412062
blob_append(&g.httpHeader, zLine, -1);
20422063
cgi_trace(zLine);
20432064
zToken = extract_token(zLine, &z);
20442065
if( zToken==0 ){
@@ -2046,17 +2067,18 @@
20462067
}
20472068
if( fossil_strcmp(zToken,"GET")!=0
20482069
&& fossil_strcmp(zToken,"POST")!=0
20492070
&& fossil_strcmp(zToken,"HEAD")!=0
20502071
){
2051
- malformed_request("unsupported HTTP method");
2072
+ malformed_request("unsupported HTTP method: \"%s\" - Fossil only supports"
2073
+ "GET, POST, and HEAD", zToken);
20522074
}
20532075
cgi_setenv("GATEWAY_INTERFACE","CGI/1.0");
20542076
cgi_setenv("REQUEST_METHOD",zToken);
20552077
zToken = extract_token(z, &z);
20562078
if( zToken==0 ){
2057
- malformed_request("malformed URL in HTTP header");
2079
+ malformed_request("malformed URI in the HTTP header");
20582080
}
20592081
cgi_setenv("REQUEST_URI", zToken);
20602082
cgi_setenv("SCRIPT_NAME", "");
20612083
for(i=0; zToken[i] && zToken[i]!='?'; i++){}
20622084
if( zToken[i] ) zToken[i++] = 0;
@@ -2164,10 +2186,11 @@
21642186
g.zIpAddr = fossil_strdup(zIpAddr);
21652187
}
21662188
}else{
21672189
fossil_fatal("missing SSH IP address");
21682190
}
2191
+ g.zReqType = "HTTP";
21692192
if( fgets(zLine, sizeof(zLine),g.httpIn)==0 ){
21702193
malformed_request("missing HTTP header");
21712194
}
21722195
cgi_trace(zLine);
21732196
zToken = extract_token(zLine, &z);
21742197
--- src/cgi.c
+++ src/cgi.c
@@ -1238,11 +1238,11 @@
1238 }
1239 fputs(z, pLog);
1240 }
1241
1242 /* Forward declaration */
1243 static NORETURN void malformed_request(const char *zMsg);
1244
1245 /*
1246 ** Checks the QUERY_STRING environment variable, sets it up
1247 ** via add_param_list() and, if found, applies its "skin"
1248 ** setting. Returns 0 if no QUERY_STRING is set, 1 if it is,
@@ -1321,10 +1321,11 @@
1321 char *zSemi;
1322 int len;
1323 const char *zRequestUri = cgi_parameter("REQUEST_URI",0);
1324 const char *zScriptName = cgi_parameter("SCRIPT_NAME",0);
1325 const char *zPathInfo = cgi_parameter("PATH_INFO",0);
 
1326 #ifdef _WIN32
1327 const char *zServerSoftware = cgi_parameter("SERVER_SOFTWARE",0);
1328 #endif
1329
1330 #ifdef FOSSIL_ENABLE_JSON
@@ -1418,11 +1419,19 @@
1418 z = (char*)P("REMOTE_ADDR");
1419 if( z ){
1420 g.zIpAddr = fossil_strdup(z);
1421 }
1422
1423 len = atoi(PD("CONTENT_LENGTH", "0"));
 
 
 
 
 
 
 
 
1424 zType = P("CONTENT_TYPE");
1425 zSemi = zType ? strchr(zType, ';') : 0;
1426 if( zSemi ){
1427 g.zContentType = fossil_strndup(zType, (int)(zSemi-zType));
1428 zType = g.zContentType;
@@ -1912,15 +1921,26 @@
1912
1913
1914 /*
1915 ** Send a reply indicating that the HTTP request was malformed
1916 */
1917 static NORETURN void malformed_request(const char *zMsg){
1918 cgi_set_status(501, "Not Implemented");
1919 cgi_printf(
1920 "<html><body><p>Bad Request: %s</p></body></html>\n", zMsg
1921 );
 
 
 
 
 
 
 
 
 
 
 
1922 cgi_reply();
1923 fossil_exit(0);
1924 }
1925
1926 /*
@@ -2033,12 +2053,13 @@
2033 char *z, *zToken;
2034 int i;
2035 const char *zScheme = "http";
2036 char zLine[2000]; /* A single line of input. */
2037 g.fullHttpReply = 1;
 
2038 if( cgi_fgets(zLine, sizeof(zLine))==0 ){
2039 malformed_request("missing HTTP header");
2040 }
2041 blob_append(&g.httpHeader, zLine, -1);
2042 cgi_trace(zLine);
2043 zToken = extract_token(zLine, &z);
2044 if( zToken==0 ){
@@ -2046,17 +2067,18 @@
2046 }
2047 if( fossil_strcmp(zToken,"GET")!=0
2048 && fossil_strcmp(zToken,"POST")!=0
2049 && fossil_strcmp(zToken,"HEAD")!=0
2050 ){
2051 malformed_request("unsupported HTTP method");
 
2052 }
2053 cgi_setenv("GATEWAY_INTERFACE","CGI/1.0");
2054 cgi_setenv("REQUEST_METHOD",zToken);
2055 zToken = extract_token(z, &z);
2056 if( zToken==0 ){
2057 malformed_request("malformed URL in HTTP header");
2058 }
2059 cgi_setenv("REQUEST_URI", zToken);
2060 cgi_setenv("SCRIPT_NAME", "");
2061 for(i=0; zToken[i] && zToken[i]!='?'; i++){}
2062 if( zToken[i] ) zToken[i++] = 0;
@@ -2164,10 +2186,11 @@
2164 g.zIpAddr = fossil_strdup(zIpAddr);
2165 }
2166 }else{
2167 fossil_fatal("missing SSH IP address");
2168 }
 
2169 if( fgets(zLine, sizeof(zLine),g.httpIn)==0 ){
2170 malformed_request("missing HTTP header");
2171 }
2172 cgi_trace(zLine);
2173 zToken = extract_token(zLine, &z);
2174
--- src/cgi.c
+++ src/cgi.c
@@ -1238,11 +1238,11 @@
1238 }
1239 fputs(z, pLog);
1240 }
1241
1242 /* Forward declaration */
1243 static NORETURN void malformed_request(const char *zMsg, ...);
1244
1245 /*
1246 ** Checks the QUERY_STRING environment variable, sets it up
1247 ** via add_param_list() and, if found, applies its "skin"
1248 ** setting. Returns 0 if no QUERY_STRING is set, 1 if it is,
@@ -1321,10 +1321,11 @@
1321 char *zSemi;
1322 int len;
1323 const char *zRequestUri = cgi_parameter("REQUEST_URI",0);
1324 const char *zScriptName = cgi_parameter("SCRIPT_NAME",0);
1325 const char *zPathInfo = cgi_parameter("PATH_INFO",0);
1326 const char *zContentLength = 0;
1327 #ifdef _WIN32
1328 const char *zServerSoftware = cgi_parameter("SERVER_SOFTWARE",0);
1329 #endif
1330
1331 #ifdef FOSSIL_ENABLE_JSON
@@ -1418,11 +1419,19 @@
1419 z = (char*)P("REMOTE_ADDR");
1420 if( z ){
1421 g.zIpAddr = fossil_strdup(z);
1422 }
1423
1424 zContentLength = P("CONTENT_LENGTH");
1425 if( zContentLength==0 ){
1426 len = 0;
1427 if( sqlite3_stricmp(PD("REQUEST_METHOD",""),"POST")==0 ){
1428 malformed_request("missing CONTENT_LENGTH on a POST method");
1429 }
1430 }else{
1431 len = atoi(zContentLength);
1432 }
1433 zType = P("CONTENT_TYPE");
1434 zSemi = zType ? strchr(zType, ';') : 0;
1435 if( zSemi ){
1436 g.zContentType = fossil_strndup(zType, (int)(zSemi-zType));
1437 zType = g.zContentType;
@@ -1912,15 +1921,26 @@
1921
1922
1923 /*
1924 ** Send a reply indicating that the HTTP request was malformed
1925 */
1926 static NORETURN void malformed_request(const char *zMsg, ...){
1927 va_list ap;
1928 char *z;
1929 va_start(ap, zMsg);
1930 z = vmprintf(zMsg, ap);
1931 va_end(ap);
1932 cgi_set_status(400, "Bad Request");
1933 zContentType = "text/plain";
1934 if( g.zReqType==0 ) g.zReqType = "WWW";
1935 if( g.zReqType[0]=='C' && PD("SERVER_SOFTWARE",0)!=0 ){
1936 const char *zServer = PD("SERVER_SOFTWARE","");
1937 cgi_printf("Bad CGI Request from \"%s\": %s\n",zServer,z);
1938 }else{
1939 cgi_printf("Bad %s Request: %s\n", g.zReqType, z);
1940 }
1941 fossil_free(z);
1942 cgi_reply();
1943 fossil_exit(0);
1944 }
1945
1946 /*
@@ -2033,12 +2053,13 @@
2053 char *z, *zToken;
2054 int i;
2055 const char *zScheme = "http";
2056 char zLine[2000]; /* A single line of input. */
2057 g.fullHttpReply = 1;
2058 g.zReqType = "HTTP";
2059 if( cgi_fgets(zLine, sizeof(zLine))==0 ){
2060 malformed_request("missing header");
2061 }
2062 blob_append(&g.httpHeader, zLine, -1);
2063 cgi_trace(zLine);
2064 zToken = extract_token(zLine, &z);
2065 if( zToken==0 ){
@@ -2046,17 +2067,18 @@
2067 }
2068 if( fossil_strcmp(zToken,"GET")!=0
2069 && fossil_strcmp(zToken,"POST")!=0
2070 && fossil_strcmp(zToken,"HEAD")!=0
2071 ){
2072 malformed_request("unsupported HTTP method: \"%s\" - Fossil only supports"
2073 "GET, POST, and HEAD", zToken);
2074 }
2075 cgi_setenv("GATEWAY_INTERFACE","CGI/1.0");
2076 cgi_setenv("REQUEST_METHOD",zToken);
2077 zToken = extract_token(z, &z);
2078 if( zToken==0 ){
2079 malformed_request("malformed URI in the HTTP header");
2080 }
2081 cgi_setenv("REQUEST_URI", zToken);
2082 cgi_setenv("SCRIPT_NAME", "");
2083 for(i=0; zToken[i] && zToken[i]!='?'; i++){}
2084 if( zToken[i] ) zToken[i++] = 0;
@@ -2164,10 +2186,11 @@
2186 g.zIpAddr = fossil_strdup(zIpAddr);
2187 }
2188 }else{
2189 fossil_fatal("missing SSH IP address");
2190 }
2191 g.zReqType = "HTTP";
2192 if( fgets(zLine, sizeof(zLine),g.httpIn)==0 ){
2193 malformed_request("missing HTTP header");
2194 }
2195 cgi_trace(zLine);
2196 zToken = extract_token(zLine, &z);
2197
+13 -1
--- src/clone.c
+++ src/clone.c
@@ -136,10 +136,11 @@
136136
** --ssl-identity FILENAME Use the SSL identity if requested by the server
137137
** --transport-command CMD Use CMD to move messages to the server and back
138138
** -u|--unversioned Also sync unversioned content
139139
** -v|--verbose Show more statistics in output
140140
** --workdir DIR Also open a check-out in DIR
141
+** --xverbose Extra debugging output
141142
**
142143
** See also: [[init]], [[open]]
143144
*/
144145
void clone_cmd(void){
145146
char *zPassword;
@@ -161,10 +162,11 @@
161162
if( find_option("save-http-password",0,0)!=0 ){
162163
urlFlags &= ~URL_PROMPT_PW;
163164
urlFlags |= URL_REMEMBER_PW;
164165
}
165166
if( find_option("verbose","v",0)!=0) syncFlags |= SYNC_VERBOSE;
167
+ if( find_option("xverbose",0,0)!=0) syncFlags |= SYNC_XVERBOSE;
166168
if( find_option("unversioned","u",0)!=0 ){
167169
syncFlags |= SYNC_UNVERSIONED;
168170
if( syncFlags & SYNC_VERBOSE ){
169171
syncFlags |= SYNC_UV_TRACE;
170172
}
@@ -267,11 +269,21 @@
267269
verify_cancel();
268270
db_end_transaction(0);
269271
db_close(1);
270272
if( nErr ){
271273
file_delete(zRepo);
272
- fossil_fatal("server returned an error - clone aborted");
274
+ if( g.fHttpTrace ){
275
+ fossil_fatal(
276
+ "server returned an error - clone aborted\n\n%s",
277
+ http_last_trace_reply()
278
+ );
279
+ }else{
280
+ fossil_fatal(
281
+ "server returned an error - clone aborted\n"
282
+ "Rerun using --httptrace for more detail"
283
+ );
284
+ }
273285
}
274286
db_open_repository(zRepo);
275287
}
276288
db_begin_transaction();
277289
if( db_exists("SELECT 1 FROM delta WHERE srcId IN phantom") ){
278290
--- src/clone.c
+++ src/clone.c
@@ -136,10 +136,11 @@
136 ** --ssl-identity FILENAME Use the SSL identity if requested by the server
137 ** --transport-command CMD Use CMD to move messages to the server and back
138 ** -u|--unversioned Also sync unversioned content
139 ** -v|--verbose Show more statistics in output
140 ** --workdir DIR Also open a check-out in DIR
 
141 **
142 ** See also: [[init]], [[open]]
143 */
144 void clone_cmd(void){
145 char *zPassword;
@@ -161,10 +162,11 @@
161 if( find_option("save-http-password",0,0)!=0 ){
162 urlFlags &= ~URL_PROMPT_PW;
163 urlFlags |= URL_REMEMBER_PW;
164 }
165 if( find_option("verbose","v",0)!=0) syncFlags |= SYNC_VERBOSE;
 
166 if( find_option("unversioned","u",0)!=0 ){
167 syncFlags |= SYNC_UNVERSIONED;
168 if( syncFlags & SYNC_VERBOSE ){
169 syncFlags |= SYNC_UV_TRACE;
170 }
@@ -267,11 +269,21 @@
267 verify_cancel();
268 db_end_transaction(0);
269 db_close(1);
270 if( nErr ){
271 file_delete(zRepo);
272 fossil_fatal("server returned an error - clone aborted");
 
 
 
 
 
 
 
 
 
 
273 }
274 db_open_repository(zRepo);
275 }
276 db_begin_transaction();
277 if( db_exists("SELECT 1 FROM delta WHERE srcId IN phantom") ){
278
--- src/clone.c
+++ src/clone.c
@@ -136,10 +136,11 @@
136 ** --ssl-identity FILENAME Use the SSL identity if requested by the server
137 ** --transport-command CMD Use CMD to move messages to the server and back
138 ** -u|--unversioned Also sync unversioned content
139 ** -v|--verbose Show more statistics in output
140 ** --workdir DIR Also open a check-out in DIR
141 ** --xverbose Extra debugging output
142 **
143 ** See also: [[init]], [[open]]
144 */
145 void clone_cmd(void){
146 char *zPassword;
@@ -161,10 +162,11 @@
162 if( find_option("save-http-password",0,0)!=0 ){
163 urlFlags &= ~URL_PROMPT_PW;
164 urlFlags |= URL_REMEMBER_PW;
165 }
166 if( find_option("verbose","v",0)!=0) syncFlags |= SYNC_VERBOSE;
167 if( find_option("xverbose",0,0)!=0) syncFlags |= SYNC_XVERBOSE;
168 if( find_option("unversioned","u",0)!=0 ){
169 syncFlags |= SYNC_UNVERSIONED;
170 if( syncFlags & SYNC_VERBOSE ){
171 syncFlags |= SYNC_UV_TRACE;
172 }
@@ -267,11 +269,21 @@
269 verify_cancel();
270 db_end_transaction(0);
271 db_close(1);
272 if( nErr ){
273 file_delete(zRepo);
274 if( g.fHttpTrace ){
275 fossil_fatal(
276 "server returned an error - clone aborted\n\n%s",
277 http_last_trace_reply()
278 );
279 }else{
280 fossil_fatal(
281 "server returned an error - clone aborted\n"
282 "Rerun using --httptrace for more detail"
283 );
284 }
285 }
286 db_open_repository(zRepo);
287 }
288 db_begin_transaction();
289 if( db_exists("SELECT 1 FROM delta WHERE srcId IN phantom") ){
290
+57 -9
--- src/http.c
+++ src/http.c
@@ -47,10 +47,15 @@
4747
#define MAX_HTTP_AUTH 2
4848
4949
/* Keep track of HTTP Basic Authorization failures */
5050
static int fSeenHttpAuth = 0;
5151
52
+/* The N value for most recent http-request-N.txt and http-reply-N.txt
53
+** trace files.
54
+*/
55
+static int traceCnt = 0;
56
+
5257
/*
5358
** Construct the "login" card with the client credentials.
5459
**
5560
** login LOGIN NONCE SIGNATURE
5661
**
@@ -389,10 +394,29 @@
389394
*/
390395
void ssh_add_path_argument(Blob *pCmd){
391396
blob_append_escaped_arg(pCmd,
392397
"PATH=$HOME/bin:/usr/local/bin:/opt/homebrew/bin:$PATH", 1);
393398
}
399
+
400
+/*
401
+** Return the complete text of the last HTTP reply as saved in the
402
+** http-reply-N.txt file. This only works if run using --httptrace.
403
+** Without the --httptrace option, this routine returns a NULL pointer.
404
+** It still might return a NULL pointer if for some reason it cannot
405
+** find and open the last http-reply-N.txt file.
406
+*/
407
+char *http_last_trace_reply(void){
408
+ Blob x;
409
+ int n;
410
+ char *zFilename;
411
+ if( g.fHttpTrace==0 ) return 0;
412
+ zFilename = mprintf("http-reply-%d.txt", traceCnt);
413
+ n = blob_read_from_file(&x, zFilename, ExtFILE);
414
+ fossil_free(zFilename);
415
+ if( n<=0 ) return 0;
416
+ return blob_str(&x);
417
+}
394418
395419
/*
396420
** Sign the content in pSend, compress it, and send it to the server
397421
** via HTTP or HTTPS. Get a reply, uncompress the reply, and store the reply
398422
** in pRecv. pRecv is assumed to be uninitialized when
@@ -412,11 +436,10 @@
412436
Blob login; /* The login card */
413437
Blob payload; /* The complete payload including login card */
414438
Blob hdr; /* The HTTP request header */
415439
int closeConnection; /* True to close the connection when done */
416440
int iLength; /* Expected length of the reply payload */
417
- int iRecvLen; /* Received length of the reply payload */
418441
int rc = 0; /* Result code */
419442
int iHttpVersion; /* Which version of HTTP protocol server uses */
420443
char *zLine; /* A single line of the reply header */
421444
int i; /* Loop counter */
422445
int isError = 0; /* True if the reply is an error message */
@@ -465,11 +488,10 @@
465488
** server-side like this:
466489
**
467490
** ./fossil test-http <http-request-1.txt
468491
*/
469492
if( g.fHttpTrace ){
470
- static int traceCnt = 0;
471493
char *zOutFile;
472494
FILE *out;
473495
traceCnt++;
474496
zOutFile = mprintf("http-request-%d.txt", traceCnt);
475497
out = fopen(zOutFile, "wb");
@@ -502,10 +524,11 @@
502524
/*
503525
** Read and interpret the server reply
504526
*/
505527
closeConnection = 1;
506528
iLength = -1;
529
+ iHttpVersion = -1;
507530
while( (zLine = transport_receive_line(&g.url))!=0 && zLine[0]!=0 ){
508531
if( mHttpFlags & HTTP_VERBOSE ){
509532
fossil_print("Read: [%s]\n", zLine);
510533
}
511534
if( fossil_strnicmp(zLine, "http/1.", 7)==0 ){
@@ -540,10 +563,11 @@
540563
for(ii=7; zLine[ii] && zLine[ii]!=' '; ii++){}
541564
while( zLine[ii]==' ' ) ii++;
542565
fossil_warning("server says: %s", &zLine[ii]);
543566
goto write_err;
544567
}
568
+ if( iHttpVersion<0 ) iHttpVersion = 1;
545569
closeConnection = 0;
546570
}else if( fossil_strnicmp(zLine, "content-length:", 15)==0 ){
547571
for(i=15; fossil_isspace(zLine[i]); i++){}
548572
iLength = atoi(&zLine[i]);
549573
}else if( fossil_strnicmp(zLine, "connection:", 11)==0 ){
@@ -613,11 +637,11 @@
613637
isError = 1;
614638
}
615639
}
616640
}
617641
}
618
- if( iLength<0 ){
642
+ if( iHttpVersion<0 ){
619643
/* We got nothing back from the server. If using the ssh: protocol,
620644
** this might mean we need to add or remove the PATH=... argument
621645
** to the SSH command being sent. If that is the case, retry the
622646
** request after adding or removing the PATH= argument.
623647
*/
@@ -659,17 +683,41 @@
659683
660684
/*
661685
** Extract the reply payload that follows the header
662686
*/
663687
blob_zero(pReply);
664
- blob_resize(pReply, iLength);
665
- iRecvLen = transport_receive(&g.url, blob_buffer(pReply), iLength);
666
- if( iRecvLen != iLength ){
667
- fossil_warning("response truncated: got %d bytes of %d", iRecvLen, iLength);
668
- goto write_err;
688
+ if( iLength==0 ){
689
+ /* No content to read */
690
+ }else if( iLength>0 ){
691
+ /* Read content of a known length */
692
+ int iRecvLen; /* Received length of the reply payload */
693
+ blob_resize(pReply, iLength);
694
+ iRecvLen = transport_receive(&g.url, blob_buffer(pReply), iLength);
695
+ if( mHttpFlags & HTTP_VERBOSE ){
696
+ fossil_print("Reply received: %d of %d bytes\n", iRecvLen, iLength);
697
+ }
698
+ if( iRecvLen != iLength ){
699
+ fossil_warning("response truncated: got %d bytes of %d",
700
+ iRecvLen, iLength);
701
+ goto write_err;
702
+ }
703
+ }else{
704
+ /* Read content until end-of-file */
705
+ int iRecvLen; /* Received length of the reply payload */
706
+ unsigned int nReq = 1000;
707
+ unsigned int nPrior = 0;
708
+ do{
709
+ nReq *= 2;
710
+ blob_resize(pReply, nPrior+nReq);
711
+ iRecvLen = transport_receive(&g.url, &pReply->aData[nPrior], (int)nReq);
712
+ nPrior += iRecvLen;
713
+ pReply->nUsed = nPrior;
714
+ }while( iRecvLen==nReq && nReq<0x20000000 );
715
+ if( mHttpFlags & HTTP_VERBOSE ){
716
+ fossil_print("Reply received: %u bytes (w/o content-length)\n", nPrior);
717
+ }
669718
}
670
- blob_resize(pReply, iLength);
671719
if( isError ){
672720
char *z;
673721
int i, j;
674722
z = blob_str(pReply);
675723
for(i=j=0; z[i]; i++, j++){
676724
--- src/http.c
+++ src/http.c
@@ -47,10 +47,15 @@
47 #define MAX_HTTP_AUTH 2
48
49 /* Keep track of HTTP Basic Authorization failures */
50 static int fSeenHttpAuth = 0;
51
 
 
 
 
 
52 /*
53 ** Construct the "login" card with the client credentials.
54 **
55 ** login LOGIN NONCE SIGNATURE
56 **
@@ -389,10 +394,29 @@
389 */
390 void ssh_add_path_argument(Blob *pCmd){
391 blob_append_escaped_arg(pCmd,
392 "PATH=$HOME/bin:/usr/local/bin:/opt/homebrew/bin:$PATH", 1);
393 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
394
395 /*
396 ** Sign the content in pSend, compress it, and send it to the server
397 ** via HTTP or HTTPS. Get a reply, uncompress the reply, and store the reply
398 ** in pRecv. pRecv is assumed to be uninitialized when
@@ -412,11 +436,10 @@
412 Blob login; /* The login card */
413 Blob payload; /* The complete payload including login card */
414 Blob hdr; /* The HTTP request header */
415 int closeConnection; /* True to close the connection when done */
416 int iLength; /* Expected length of the reply payload */
417 int iRecvLen; /* Received length of the reply payload */
418 int rc = 0; /* Result code */
419 int iHttpVersion; /* Which version of HTTP protocol server uses */
420 char *zLine; /* A single line of the reply header */
421 int i; /* Loop counter */
422 int isError = 0; /* True if the reply is an error message */
@@ -465,11 +488,10 @@
465 ** server-side like this:
466 **
467 ** ./fossil test-http <http-request-1.txt
468 */
469 if( g.fHttpTrace ){
470 static int traceCnt = 0;
471 char *zOutFile;
472 FILE *out;
473 traceCnt++;
474 zOutFile = mprintf("http-request-%d.txt", traceCnt);
475 out = fopen(zOutFile, "wb");
@@ -502,10 +524,11 @@
502 /*
503 ** Read and interpret the server reply
504 */
505 closeConnection = 1;
506 iLength = -1;
 
507 while( (zLine = transport_receive_line(&g.url))!=0 && zLine[0]!=0 ){
508 if( mHttpFlags & HTTP_VERBOSE ){
509 fossil_print("Read: [%s]\n", zLine);
510 }
511 if( fossil_strnicmp(zLine, "http/1.", 7)==0 ){
@@ -540,10 +563,11 @@
540 for(ii=7; zLine[ii] && zLine[ii]!=' '; ii++){}
541 while( zLine[ii]==' ' ) ii++;
542 fossil_warning("server says: %s", &zLine[ii]);
543 goto write_err;
544 }
 
545 closeConnection = 0;
546 }else if( fossil_strnicmp(zLine, "content-length:", 15)==0 ){
547 for(i=15; fossil_isspace(zLine[i]); i++){}
548 iLength = atoi(&zLine[i]);
549 }else if( fossil_strnicmp(zLine, "connection:", 11)==0 ){
@@ -613,11 +637,11 @@
613 isError = 1;
614 }
615 }
616 }
617 }
618 if( iLength<0 ){
619 /* We got nothing back from the server. If using the ssh: protocol,
620 ** this might mean we need to add or remove the PATH=... argument
621 ** to the SSH command being sent. If that is the case, retry the
622 ** request after adding or removing the PATH= argument.
623 */
@@ -659,17 +683,41 @@
659
660 /*
661 ** Extract the reply payload that follows the header
662 */
663 blob_zero(pReply);
664 blob_resize(pReply, iLength);
665 iRecvLen = transport_receive(&g.url, blob_buffer(pReply), iLength);
666 if( iRecvLen != iLength ){
667 fossil_warning("response truncated: got %d bytes of %d", iRecvLen, iLength);
668 goto write_err;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
669 }
670 blob_resize(pReply, iLength);
671 if( isError ){
672 char *z;
673 int i, j;
674 z = blob_str(pReply);
675 for(i=j=0; z[i]; i++, j++){
676
--- src/http.c
+++ src/http.c
@@ -47,10 +47,15 @@
47 #define MAX_HTTP_AUTH 2
48
49 /* Keep track of HTTP Basic Authorization failures */
50 static int fSeenHttpAuth = 0;
51
52 /* The N value for most recent http-request-N.txt and http-reply-N.txt
53 ** trace files.
54 */
55 static int traceCnt = 0;
56
57 /*
58 ** Construct the "login" card with the client credentials.
59 **
60 ** login LOGIN NONCE SIGNATURE
61 **
@@ -389,10 +394,29 @@
394 */
395 void ssh_add_path_argument(Blob *pCmd){
396 blob_append_escaped_arg(pCmd,
397 "PATH=$HOME/bin:/usr/local/bin:/opt/homebrew/bin:$PATH", 1);
398 }
399
400 /*
401 ** Return the complete text of the last HTTP reply as saved in the
402 ** http-reply-N.txt file. This only works if run using --httptrace.
403 ** Without the --httptrace option, this routine returns a NULL pointer.
404 ** It still might return a NULL pointer if for some reason it cannot
405 ** find and open the last http-reply-N.txt file.
406 */
407 char *http_last_trace_reply(void){
408 Blob x;
409 int n;
410 char *zFilename;
411 if( g.fHttpTrace==0 ) return 0;
412 zFilename = mprintf("http-reply-%d.txt", traceCnt);
413 n = blob_read_from_file(&x, zFilename, ExtFILE);
414 fossil_free(zFilename);
415 if( n<=0 ) return 0;
416 return blob_str(&x);
417 }
418
419 /*
420 ** Sign the content in pSend, compress it, and send it to the server
421 ** via HTTP or HTTPS. Get a reply, uncompress the reply, and store the reply
422 ** in pRecv. pRecv is assumed to be uninitialized when
@@ -412,11 +436,10 @@
436 Blob login; /* The login card */
437 Blob payload; /* The complete payload including login card */
438 Blob hdr; /* The HTTP request header */
439 int closeConnection; /* True to close the connection when done */
440 int iLength; /* Expected length of the reply payload */
 
441 int rc = 0; /* Result code */
442 int iHttpVersion; /* Which version of HTTP protocol server uses */
443 char *zLine; /* A single line of the reply header */
444 int i; /* Loop counter */
445 int isError = 0; /* True if the reply is an error message */
@@ -465,11 +488,10 @@
488 ** server-side like this:
489 **
490 ** ./fossil test-http <http-request-1.txt
491 */
492 if( g.fHttpTrace ){
 
493 char *zOutFile;
494 FILE *out;
495 traceCnt++;
496 zOutFile = mprintf("http-request-%d.txt", traceCnt);
497 out = fopen(zOutFile, "wb");
@@ -502,10 +524,11 @@
524 /*
525 ** Read and interpret the server reply
526 */
527 closeConnection = 1;
528 iLength = -1;
529 iHttpVersion = -1;
530 while( (zLine = transport_receive_line(&g.url))!=0 && zLine[0]!=0 ){
531 if( mHttpFlags & HTTP_VERBOSE ){
532 fossil_print("Read: [%s]\n", zLine);
533 }
534 if( fossil_strnicmp(zLine, "http/1.", 7)==0 ){
@@ -540,10 +563,11 @@
563 for(ii=7; zLine[ii] && zLine[ii]!=' '; ii++){}
564 while( zLine[ii]==' ' ) ii++;
565 fossil_warning("server says: %s", &zLine[ii]);
566 goto write_err;
567 }
568 if( iHttpVersion<0 ) iHttpVersion = 1;
569 closeConnection = 0;
570 }else if( fossil_strnicmp(zLine, "content-length:", 15)==0 ){
571 for(i=15; fossil_isspace(zLine[i]); i++){}
572 iLength = atoi(&zLine[i]);
573 }else if( fossil_strnicmp(zLine, "connection:", 11)==0 ){
@@ -613,11 +637,11 @@
637 isError = 1;
638 }
639 }
640 }
641 }
642 if( iHttpVersion<0 ){
643 /* We got nothing back from the server. If using the ssh: protocol,
644 ** this might mean we need to add or remove the PATH=... argument
645 ** to the SSH command being sent. If that is the case, retry the
646 ** request after adding or removing the PATH= argument.
647 */
@@ -659,17 +683,41 @@
683
684 /*
685 ** Extract the reply payload that follows the header
686 */
687 blob_zero(pReply);
688 if( iLength==0 ){
689 /* No content to read */
690 }else if( iLength>0 ){
691 /* Read content of a known length */
692 int iRecvLen; /* Received length of the reply payload */
693 blob_resize(pReply, iLength);
694 iRecvLen = transport_receive(&g.url, blob_buffer(pReply), iLength);
695 if( mHttpFlags & HTTP_VERBOSE ){
696 fossil_print("Reply received: %d of %d bytes\n", iRecvLen, iLength);
697 }
698 if( iRecvLen != iLength ){
699 fossil_warning("response truncated: got %d bytes of %d",
700 iRecvLen, iLength);
701 goto write_err;
702 }
703 }else{
704 /* Read content until end-of-file */
705 int iRecvLen; /* Received length of the reply payload */
706 unsigned int nReq = 1000;
707 unsigned int nPrior = 0;
708 do{
709 nReq *= 2;
710 blob_resize(pReply, nPrior+nReq);
711 iRecvLen = transport_receive(&g.url, &pReply->aData[nPrior], (int)nReq);
712 nPrior += iRecvLen;
713 pReply->nUsed = nPrior;
714 }while( iRecvLen==nReq && nReq<0x20000000 );
715 if( mHttpFlags & HTTP_VERBOSE ){
716 fossil_print("Reply received: %u bytes (w/o content-length)\n", nPrior);
717 }
718 }
 
719 if( isError ){
720 char *z;
721 int i, j;
722 z = blob_str(pReply);
723 for(i=j=0; z[i]; i++, j++){
724
+10 -1
--- src/main.c
+++ src/main.c
@@ -227,10 +227,11 @@
227227
const char *zCkoutAlias; /* doc/ uses this branch as an alias for "ckout" */
228228
const char *zMainMenuFile; /* --mainmenu FILE from server/ui/cgi */
229229
const char *zSSLIdentity; /* Value of --ssl-identity option, filename of
230230
** SSL client identity */
231231
const char *zCgiFile; /* Name of the CGI file */
232
+ const char *zReqType; /* Type of request: "HTTP", "CGI", "SCGI" */
232233
#if USE_SEE
233234
const char *zPidKey; /* Saved value of the --usepidkey option. Only
234235
* applicable when using SEE on Windows or Linux. */
235236
#endif
236237
int useLocalauth; /* No login required if from 127.0.0.1 */
@@ -2363,10 +2364,11 @@
23632364
g.httpOut = stdout;
23642365
g.httpIn = stdin;
23652366
fossil_binary_mode(g.httpOut);
23662367
fossil_binary_mode(g.httpIn);
23672368
g.cgiOutput = 1;
2369
+ g.zReqType = "CGI";
23682370
fossil_set_timeout(FOSSIL_DEFAULT_TIMEOUT);
23692371
/* Find the name of the CGI control file */
23702372
if( g.argc==3 && fossil_strcmp(g.argv[1],"cgi")==0 ){
23712373
g.zCgiFile = g.argv[2];
23722374
}else if( g.argc>=2 ){
@@ -2846,10 +2848,11 @@
28462848
g.useLocalauth = find_option("localauth", 0, 0)!=0;
28472849
g.sslNotAvailable = find_option("nossl", 0, 0)!=0;
28482850
g.fNoHttpCompress = find_option("nocompress",0,0)!=0;
28492851
g.zExtRoot = find_option("extroot",0,1);
28502852
g.zCkoutAlias = find_option("ckout-alias",0,1);
2853
+ g.zReqType = "HTTP";
28512854
zInFile = find_option("in",0,1);
28522855
if( zInFile ){
28532856
backoffice_disable();
28542857
g.httpIn = fossil_fopen(zInFile, "rb");
28552858
if( g.httpIn==0 ) fossil_fatal("cannot open \"%s\" for reading", zInFile);
@@ -2869,10 +2872,11 @@
28692872
_setmode(_fileno(stdout), _O_BINARY);
28702873
#endif
28712874
}
28722875
zIpAddr = find_option("ipaddr",0,1);
28732876
useSCGI = find_option("scgi", 0, 0)!=0;
2877
+ if( useSCGI ) g.zReqType = "SCGI";
28742878
zAltBase = find_option("baseurl", 0, 1);
28752879
if( find_option("nodelay",0,0)!=0 ) backoffice_no_delay();
28762880
if( zAltBase ) set_base_url(zAltBase);
28772881
if( find_option("https",0,0)!=0 ){
28782882
zIpAddr = fossil_getenv("REMOTE_HOST"); /* From stunnel */
@@ -2995,10 +2999,11 @@
29952999
g.httpOut = stdout;
29963000
fossil_binary_mode(g.httpOut);
29973001
fossil_binary_mode(g.httpIn);
29983002
g.zExtRoot = find_option("extroot",0,1);
29993003
find_server_repository(2, 0);
3004
+ g.zReqType = "HTTP";
30003005
g.cgiOutput = 1;
30013006
g.fNoHttpCompress = 1;
30023007
g.fullHttpReply = 1;
30033008
g.sslNotAvailable = 1; /* Avoid attempts to redirect */
30043009
zIpAddr = bTest ? 0 : cgi_ssh_remote_addr(0);
@@ -3231,11 +3236,15 @@
32313236
zNotFound = find_option("notfound", 0, 1);
32323237
allowRepoList = find_option("repolist",0,0)!=0;
32333238
if( find_option("nocompress",0,0)!=0 ) g.fNoHttpCompress = 1;
32343239
zAltBase = find_option("baseurl", 0, 1);
32353240
fCreate = find_option("create",0,0)!=0;
3236
- if( find_option("scgi", 0, 0)!=0 ) flags |= HTTP_SERVER_SCGI;
3241
+ g.zReqType = "HTTP";
3242
+ if( find_option("scgi", 0, 0)!=0 ){
3243
+ g.zReqType = "SCGI";
3244
+ flags |= HTTP_SERVER_SCGI;
3245
+ }
32373246
if( zAltBase ){
32383247
set_base_url(zAltBase);
32393248
}
32403249
g.sslNotAvailable = find_option("nossl", 0, 0)!=0 || isUiCmd;
32413250
fNoBrowser = find_option("nobrowser", "B", 0)!=0;
32423251
--- src/main.c
+++ src/main.c
@@ -227,10 +227,11 @@
227 const char *zCkoutAlias; /* doc/ uses this branch as an alias for "ckout" */
228 const char *zMainMenuFile; /* --mainmenu FILE from server/ui/cgi */
229 const char *zSSLIdentity; /* Value of --ssl-identity option, filename of
230 ** SSL client identity */
231 const char *zCgiFile; /* Name of the CGI file */
 
232 #if USE_SEE
233 const char *zPidKey; /* Saved value of the --usepidkey option. Only
234 * applicable when using SEE on Windows or Linux. */
235 #endif
236 int useLocalauth; /* No login required if from 127.0.0.1 */
@@ -2363,10 +2364,11 @@
2363 g.httpOut = stdout;
2364 g.httpIn = stdin;
2365 fossil_binary_mode(g.httpOut);
2366 fossil_binary_mode(g.httpIn);
2367 g.cgiOutput = 1;
 
2368 fossil_set_timeout(FOSSIL_DEFAULT_TIMEOUT);
2369 /* Find the name of the CGI control file */
2370 if( g.argc==3 && fossil_strcmp(g.argv[1],"cgi")==0 ){
2371 g.zCgiFile = g.argv[2];
2372 }else if( g.argc>=2 ){
@@ -2846,10 +2848,11 @@
2846 g.useLocalauth = find_option("localauth", 0, 0)!=0;
2847 g.sslNotAvailable = find_option("nossl", 0, 0)!=0;
2848 g.fNoHttpCompress = find_option("nocompress",0,0)!=0;
2849 g.zExtRoot = find_option("extroot",0,1);
2850 g.zCkoutAlias = find_option("ckout-alias",0,1);
 
2851 zInFile = find_option("in",0,1);
2852 if( zInFile ){
2853 backoffice_disable();
2854 g.httpIn = fossil_fopen(zInFile, "rb");
2855 if( g.httpIn==0 ) fossil_fatal("cannot open \"%s\" for reading", zInFile);
@@ -2869,10 +2872,11 @@
2869 _setmode(_fileno(stdout), _O_BINARY);
2870 #endif
2871 }
2872 zIpAddr = find_option("ipaddr",0,1);
2873 useSCGI = find_option("scgi", 0, 0)!=0;
 
2874 zAltBase = find_option("baseurl", 0, 1);
2875 if( find_option("nodelay",0,0)!=0 ) backoffice_no_delay();
2876 if( zAltBase ) set_base_url(zAltBase);
2877 if( find_option("https",0,0)!=0 ){
2878 zIpAddr = fossil_getenv("REMOTE_HOST"); /* From stunnel */
@@ -2995,10 +2999,11 @@
2995 g.httpOut = stdout;
2996 fossil_binary_mode(g.httpOut);
2997 fossil_binary_mode(g.httpIn);
2998 g.zExtRoot = find_option("extroot",0,1);
2999 find_server_repository(2, 0);
 
3000 g.cgiOutput = 1;
3001 g.fNoHttpCompress = 1;
3002 g.fullHttpReply = 1;
3003 g.sslNotAvailable = 1; /* Avoid attempts to redirect */
3004 zIpAddr = bTest ? 0 : cgi_ssh_remote_addr(0);
@@ -3231,11 +3236,15 @@
3231 zNotFound = find_option("notfound", 0, 1);
3232 allowRepoList = find_option("repolist",0,0)!=0;
3233 if( find_option("nocompress",0,0)!=0 ) g.fNoHttpCompress = 1;
3234 zAltBase = find_option("baseurl", 0, 1);
3235 fCreate = find_option("create",0,0)!=0;
3236 if( find_option("scgi", 0, 0)!=0 ) flags |= HTTP_SERVER_SCGI;
 
 
 
 
3237 if( zAltBase ){
3238 set_base_url(zAltBase);
3239 }
3240 g.sslNotAvailable = find_option("nossl", 0, 0)!=0 || isUiCmd;
3241 fNoBrowser = find_option("nobrowser", "B", 0)!=0;
3242
--- src/main.c
+++ src/main.c
@@ -227,10 +227,11 @@
227 const char *zCkoutAlias; /* doc/ uses this branch as an alias for "ckout" */
228 const char *zMainMenuFile; /* --mainmenu FILE from server/ui/cgi */
229 const char *zSSLIdentity; /* Value of --ssl-identity option, filename of
230 ** SSL client identity */
231 const char *zCgiFile; /* Name of the CGI file */
232 const char *zReqType; /* Type of request: "HTTP", "CGI", "SCGI" */
233 #if USE_SEE
234 const char *zPidKey; /* Saved value of the --usepidkey option. Only
235 * applicable when using SEE on Windows or Linux. */
236 #endif
237 int useLocalauth; /* No login required if from 127.0.0.1 */
@@ -2363,10 +2364,11 @@
2364 g.httpOut = stdout;
2365 g.httpIn = stdin;
2366 fossil_binary_mode(g.httpOut);
2367 fossil_binary_mode(g.httpIn);
2368 g.cgiOutput = 1;
2369 g.zReqType = "CGI";
2370 fossil_set_timeout(FOSSIL_DEFAULT_TIMEOUT);
2371 /* Find the name of the CGI control file */
2372 if( g.argc==3 && fossil_strcmp(g.argv[1],"cgi")==0 ){
2373 g.zCgiFile = g.argv[2];
2374 }else if( g.argc>=2 ){
@@ -2846,10 +2848,11 @@
2848 g.useLocalauth = find_option("localauth", 0, 0)!=0;
2849 g.sslNotAvailable = find_option("nossl", 0, 0)!=0;
2850 g.fNoHttpCompress = find_option("nocompress",0,0)!=0;
2851 g.zExtRoot = find_option("extroot",0,1);
2852 g.zCkoutAlias = find_option("ckout-alias",0,1);
2853 g.zReqType = "HTTP";
2854 zInFile = find_option("in",0,1);
2855 if( zInFile ){
2856 backoffice_disable();
2857 g.httpIn = fossil_fopen(zInFile, "rb");
2858 if( g.httpIn==0 ) fossil_fatal("cannot open \"%s\" for reading", zInFile);
@@ -2869,10 +2872,11 @@
2872 _setmode(_fileno(stdout), _O_BINARY);
2873 #endif
2874 }
2875 zIpAddr = find_option("ipaddr",0,1);
2876 useSCGI = find_option("scgi", 0, 0)!=0;
2877 if( useSCGI ) g.zReqType = "SCGI";
2878 zAltBase = find_option("baseurl", 0, 1);
2879 if( find_option("nodelay",0,0)!=0 ) backoffice_no_delay();
2880 if( zAltBase ) set_base_url(zAltBase);
2881 if( find_option("https",0,0)!=0 ){
2882 zIpAddr = fossil_getenv("REMOTE_HOST"); /* From stunnel */
@@ -2995,10 +2999,11 @@
2999 g.httpOut = stdout;
3000 fossil_binary_mode(g.httpOut);
3001 fossil_binary_mode(g.httpIn);
3002 g.zExtRoot = find_option("extroot",0,1);
3003 find_server_repository(2, 0);
3004 g.zReqType = "HTTP";
3005 g.cgiOutput = 1;
3006 g.fNoHttpCompress = 1;
3007 g.fullHttpReply = 1;
3008 g.sslNotAvailable = 1; /* Avoid attempts to redirect */
3009 zIpAddr = bTest ? 0 : cgi_ssh_remote_addr(0);
@@ -3231,11 +3236,15 @@
3236 zNotFound = find_option("notfound", 0, 1);
3237 allowRepoList = find_option("repolist",0,0)!=0;
3238 if( find_option("nocompress",0,0)!=0 ) g.fNoHttpCompress = 1;
3239 zAltBase = find_option("baseurl", 0, 1);
3240 fCreate = find_option("create",0,0)!=0;
3241 g.zReqType = "HTTP";
3242 if( find_option("scgi", 0, 0)!=0 ){
3243 g.zReqType = "SCGI";
3244 flags |= HTTP_SERVER_SCGI;
3245 }
3246 if( zAltBase ){
3247 set_base_url(zAltBase);
3248 }
3249 g.sslNotAvailable = find_option("nossl", 0, 0)!=0 || isUiCmd;
3250 fNoBrowser = find_option("nobrowser", "B", 0)!=0;
3251

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button