Fossil SCM

Change SSH transport to use a single SSH connection if client/server willing. Add client header so server can detect when to use new mode. Also improve backwards compatability for older SSH clients by responding to probes.

andybradford 2013-08-17 23:05 ssh-test-http
Commit f0bb3c9b5a8d6e75ae22760348d52c56051c4f40
+234 -2
--- src/cgi.c
+++ src/cgi.c
@@ -59,10 +59,17 @@
5959
** Destinations for output text.
6060
*/
6161
#define CGI_HEADER 0
6262
#define CGI_BODY 1
6363
64
+/*
65
+** Flags for SSH HTTP clients
66
+*/
67
+#define CGI_SSH_CLIENT 0x0001 /* Client is SSH */
68
+#define CGI_SSH_COMPAT 0x0002 /* Compat for old SSH transport */
69
+#define CGI_SSH_FOSSIL 0x0004 /* Use new Fossil SSH transport */
70
+
6471
#endif /* INTERFACE */
6572
6673
/*
6774
** The HTTP reply is generated in two pieces: the header and the body.
6875
** These pieces are generated separately because they are not necessary
@@ -451,15 +458,15 @@
451458
*/
452459
void cgi_replace_parameter(const char *zName, const char *zValue){
453460
int i;
454461
for(i=0; i<nUsedQP; i++){
455462
if( fossil_strcmp(aParamQP[i].zName,zName)==0 ){
456
- aParamQP[i].zValue = zValue;
463
+ aParamQP[i].zValue = mprintf("%s",zValue);
457464
return;
458465
}
459466
}
460
- cgi_set_parameter_nocopy(zName, zValue);
467
+ cgi_set_parameter_nocopy(zName, mprintf("%s",zValue));
461468
}
462469
463470
/*
464471
** Add a query parameter. The zName portion is fixed but a copy
465472
** must be made of zValue.
@@ -1283,10 +1290,235 @@
12831290
}
12841291
}
12851292
cgi_init();
12861293
cgi_trace(0);
12871294
}
1295
+
1296
+/*
1297
+** This routine handles a single HTTP request from an SSH client which is
1298
+** coming in on g.httpIn and which replies on g.httpOut
1299
+**
1300
+** Once all the setup is finished, this procedure returns
1301
+** and subsequent code handles the actual generation of the webpage.
1302
+**
1303
+** It is called in a loop so some variables will need to be replaced
1304
+*/
1305
+void cgi_handle_ssh_http_request(const char *zIpAddr){
1306
+ static int nCycles = 0;
1307
+ static char *zCmd = 0;
1308
+ char *z, *zToken;
1309
+ const char *zType;
1310
+ int i, content_length;
1311
+ char zLine[2000]; /* A single line of input. */
1312
+
1313
+ if( zIpAddr ){
1314
+ if( nCycles==0 ){
1315
+ cgi_setenv("REMOTE_ADDR", zIpAddr);
1316
+ g.zIpAddr = mprintf("%s", zIpAddr);
1317
+ }
1318
+ }else{
1319
+ fossil_panic("missing SSH IP address");
1320
+ }
1321
+ if( fgets(zLine, sizeof(zLine),g.httpIn)==0 ){
1322
+ malformed_request("missing HTTP header");
1323
+ }
1324
+ cgi_trace(zLine);
1325
+ zToken = extract_token(zLine, &z);
1326
+ if( zToken==0 ){
1327
+ malformed_request("malformed HTTP header");
1328
+ }
1329
+
1330
+ if( fossil_strcmp(zToken, "echo")==0 ){
1331
+ /* start looking for probes to complete transport_open */
1332
+ zCmd = cgi_handle_ssh_probes(zLine, sizeof(zLine), z, zToken);
1333
+ if( fgets(zLine, sizeof(zLine),g.httpIn)==0 ){
1334
+ malformed_request("missing HTTP header");
1335
+ }
1336
+ cgi_trace(zLine);
1337
+ zToken = extract_token(zLine, &z);
1338
+ if( zToken==0 ){
1339
+ malformed_request("malformed HTTP header");
1340
+ }
1341
+ }else if( zToken && strlen(zToken)==0 ){
1342
+ /* transport_flip request and continued transport_open */
1343
+ cgi_handle_ssh_transport(zCmd);
1344
+ if( fgets(zLine, sizeof(zLine),g.httpIn)==0 ){
1345
+ malformed_request("missing HTTP header");
1346
+ }
1347
+ cgi_trace(zLine);
1348
+ zToken = extract_token(zLine, &z);
1349
+ if( zToken==0 ){
1350
+ malformed_request("malformed HTTP header");
1351
+ }
1352
+ }
1353
+
1354
+ if( fossil_strcmp(zToken,"GET")!=0 && fossil_strcmp(zToken,"POST")!=0
1355
+ && fossil_strcmp(zToken,"HEAD")!=0 ){
1356
+ malformed_request("unsupported HTTP method");
1357
+ }
1358
+
1359
+ if( nCycles==0 ){
1360
+ cgi_setenv("GATEWAY_INTERFACE","CGI/1.0");
1361
+ cgi_setenv("REQUEST_METHOD",zToken);
1362
+ }
1363
+
1364
+ zToken = extract_token(z, &z);
1365
+ if( zToken==0 ){
1366
+ malformed_request("malformed URL in HTTP header");
1367
+ }
1368
+ if( nCycles==0 ){
1369
+ cgi_setenv("REQUEST_URI", zToken);
1370
+ cgi_setenv("SCRIPT_NAME", "");
1371
+ }
1372
+
1373
+ for(i=0; zToken[i] && zToken[i]!='?'; i++){}
1374
+ if( zToken[i] ) zToken[i++] = 0;
1375
+ if( nCycles==0 ){
1376
+ cgi_setenv("PATH_INFO", zToken);
1377
+ }else{
1378
+ cgi_replace_parameter("PATH_INFO", zToken);
1379
+ }
1380
+
1381
+ /* Get all the optional fields that follow the first line.
1382
+ */
1383
+ while( fgets(zLine,sizeof(zLine),g.httpIn) ){
1384
+ char *zFieldName;
1385
+ char *zVal;
1386
+
1387
+ cgi_trace(zLine);
1388
+ zFieldName = extract_token(zLine,&zVal);
1389
+ if( zFieldName==0 || *zFieldName==0 ) break;
1390
+ while( fossil_isspace(*zVal) ){ zVal++; }
1391
+ i = strlen(zVal);
1392
+ while( i>0 && fossil_isspace(zVal[i-1]) ){ i--; }
1393
+ zVal[i] = 0;
1394
+ for(i=0; zFieldName[i]; i++){
1395
+ zFieldName[i] = fossil_tolower(zFieldName[i]);
1396
+ }
1397
+ if( fossil_strcmp(zFieldName,"content-length:")==0 ){
1398
+ content_length = atoi(zVal);
1399
+ }else if( fossil_strcmp(zFieldName,"content-type:")==0 ){
1400
+ g.zContentType = zType = mprintf("%s", zVal);
1401
+ }else if( fossil_strcmp(zFieldName,"host:")==0 ){
1402
+ if( nCycles==0 ){
1403
+ cgi_setenv("HTTP_HOST", zVal);
1404
+ }
1405
+ }else if( fossil_strcmp(zFieldName,"user-agent:")==0 ){
1406
+ if( nCycles==0 ){
1407
+ cgi_setenv("HTTP_USER_AGENT", zVal);
1408
+ }
1409
+ }else if( fossil_strcmp(zFieldName,"x-fossil-transport:")==0 ){
1410
+ if( fossil_strnicmp(zVal, "ssh", 3)==0 ){
1411
+ if( nCycles==0 ){
1412
+ g.fSshClient |= CGI_SSH_FOSSIL;
1413
+ g.fullHttpReply = 0;
1414
+ cgi_setenv("X-FOSSIL_TRANSPORT", zVal);
1415
+ }
1416
+ }
1417
+ }
1418
+ }
1419
+
1420
+ if( nCycles==0 ){
1421
+ if( ! ( g.fSshClient & CGI_SSH_FOSSIL ) ){
1422
+ /* did not find new fossil ssh transport */
1423
+ g.fSshClient &= ~CGI_SSH_CLIENT;
1424
+ g.fullHttpReply = 1;
1425
+ cgi_replace_parameter("REMOTE_ADDR", "127.0.0.1");
1426
+ }
1427
+ }
1428
+
1429
+ cgi_reset_content();
1430
+ cgi_destination(CGI_BODY);
1431
+
1432
+ if( content_length>0 && zType ){
1433
+ blob_zero(&g.cgiIn);
1434
+ if( fossil_strcmp(zType, "application/x-fossil")==0 ){
1435
+ blob_read_from_channel(&g.cgiIn, g.httpIn, content_length);
1436
+ blob_uncompress(&g.cgiIn, &g.cgiIn);
1437
+ }else if( fossil_strcmp(zType, "application/x-fossil-debug")==0 ){
1438
+ blob_read_from_channel(&g.cgiIn, g.httpIn, content_length);
1439
+ }else if( fossil_strcmp(zType, "application/x-fossil-uncompressed")==0 ){
1440
+ blob_read_from_channel(&g.cgiIn, g.httpIn, content_length);
1441
+ }
1442
+ }
1443
+ cgi_trace(0);
1444
+ nCycles++;
1445
+}
1446
+
1447
+/*
1448
+** This routine handles the old fossil SSH probes
1449
+*/
1450
+char *cgi_handle_ssh_probes(char *zLine, int zSize, char *z, char *zToken){
1451
+ /* Start looking for probes */
1452
+ while( fossil_strcmp(zToken, "echo")==0 ){
1453
+ zToken = extract_token(z, &z);
1454
+ if( zToken==0 ){
1455
+ malformed_request("malformed probe");
1456
+ }
1457
+ if( fossil_strncmp(zToken, "test", 4)==0 ||
1458
+ fossil_strncmp(zToken, "probe-", 6)==0 ){
1459
+ fprintf(g.httpOut, "%s\n", zToken);
1460
+ fflush(g.httpOut);
1461
+ }else{
1462
+ malformed_request("malformed probe");
1463
+ }
1464
+ if( fgets(zLine, zSize, g.httpIn)==0 ){
1465
+ malformed_request("malformed probe");
1466
+ }
1467
+ cgi_trace(zLine);
1468
+ zToken = extract_token(zLine, &z);
1469
+ if( zToken==0 ){
1470
+ malformed_request("malformed probe");
1471
+ }
1472
+ }
1473
+
1474
+ /* Got all probes now first transport_open is completed
1475
+ ** so return the command that was requested
1476
+ */
1477
+ /* *zCmd = mprintf("%s", zToken); */
1478
+ return mprintf("%s", zToken);
1479
+}
1480
+
1481
+/*
1482
+** This routine handles the old fossil SSH transport_flip
1483
+** and transport_open communications if detected.
1484
+*/
1485
+void cgi_handle_ssh_transport(const char *zCmd){
1486
+ char *z, *zToken;
1487
+ char zLine[2000]; /* A single line of input. */
1488
+
1489
+ /* look for second newline of transport_flip */
1490
+ if( fgets(zLine, sizeof(zLine),g.httpIn)==0 ){
1491
+ malformed_request("incorrect transport_flip");
1492
+ }
1493
+ cgi_trace(zLine);
1494
+ zToken = extract_token(zLine, &z);
1495
+ if( zToken && strlen(zToken)==0 ){
1496
+ /* look for path to fossil */
1497
+ if( fgets(zLine, sizeof(zLine),g.httpIn)==0 ){
1498
+ if ( zCmd==0 ){
1499
+ malformed_request("missing fossil command");
1500
+ }else{
1501
+ /* no new command so exit */
1502
+ fossil_exit(0);
1503
+ }
1504
+ }
1505
+ cgi_trace(zLine);
1506
+ zToken = extract_token(zLine, &z);
1507
+ if( zToken==0 ){
1508
+ malformed_request("malformed fossil command");
1509
+ }
1510
+ /* see if we've seen the command */
1511
+ if( zCmd && zCmd[0] && fossil_strcmp(zToken, zCmd)==0 ){
1512
+ return;
1513
+ }else{
1514
+ malformed_request("transport_open failed");
1515
+ }
1516
+ }else{
1517
+ malformed_request("transport_flip failed");
1518
+ }
1519
+}
12881520
12891521
/*
12901522
** This routine handles a single SCGI request which is coming in on
12911523
** g.httpIn and which replies on g.httpOut
12921524
**
12931525
--- src/cgi.c
+++ src/cgi.c
@@ -59,10 +59,17 @@
59 ** Destinations for output text.
60 */
61 #define CGI_HEADER 0
62 #define CGI_BODY 1
63
 
 
 
 
 
 
 
64 #endif /* INTERFACE */
65
66 /*
67 ** The HTTP reply is generated in two pieces: the header and the body.
68 ** These pieces are generated separately because they are not necessary
@@ -451,15 +458,15 @@
451 */
452 void cgi_replace_parameter(const char *zName, const char *zValue){
453 int i;
454 for(i=0; i<nUsedQP; i++){
455 if( fossil_strcmp(aParamQP[i].zName,zName)==0 ){
456 aParamQP[i].zValue = zValue;
457 return;
458 }
459 }
460 cgi_set_parameter_nocopy(zName, zValue);
461 }
462
463 /*
464 ** Add a query parameter. The zName portion is fixed but a copy
465 ** must be made of zValue.
@@ -1283,10 +1290,235 @@
1283 }
1284 }
1285 cgi_init();
1286 cgi_trace(0);
1287 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1288
1289 /*
1290 ** This routine handles a single SCGI request which is coming in on
1291 ** g.httpIn and which replies on g.httpOut
1292 **
1293
--- src/cgi.c
+++ src/cgi.c
@@ -59,10 +59,17 @@
59 ** Destinations for output text.
60 */
61 #define CGI_HEADER 0
62 #define CGI_BODY 1
63
64 /*
65 ** Flags for SSH HTTP clients
66 */
67 #define CGI_SSH_CLIENT 0x0001 /* Client is SSH */
68 #define CGI_SSH_COMPAT 0x0002 /* Compat for old SSH transport */
69 #define CGI_SSH_FOSSIL 0x0004 /* Use new Fossil SSH transport */
70
71 #endif /* INTERFACE */
72
73 /*
74 ** The HTTP reply is generated in two pieces: the header and the body.
75 ** These pieces are generated separately because they are not necessary
@@ -451,15 +458,15 @@
458 */
459 void cgi_replace_parameter(const char *zName, const char *zValue){
460 int i;
461 for(i=0; i<nUsedQP; i++){
462 if( fossil_strcmp(aParamQP[i].zName,zName)==0 ){
463 aParamQP[i].zValue = mprintf("%s",zValue);
464 return;
465 }
466 }
467 cgi_set_parameter_nocopy(zName, mprintf("%s",zValue));
468 }
469
470 /*
471 ** Add a query parameter. The zName portion is fixed but a copy
472 ** must be made of zValue.
@@ -1283,10 +1290,235 @@
1290 }
1291 }
1292 cgi_init();
1293 cgi_trace(0);
1294 }
1295
1296 /*
1297 ** This routine handles a single HTTP request from an SSH client which is
1298 ** coming in on g.httpIn and which replies on g.httpOut
1299 **
1300 ** Once all the setup is finished, this procedure returns
1301 ** and subsequent code handles the actual generation of the webpage.
1302 **
1303 ** It is called in a loop so some variables will need to be replaced
1304 */
1305 void cgi_handle_ssh_http_request(const char *zIpAddr){
1306 static int nCycles = 0;
1307 static char *zCmd = 0;
1308 char *z, *zToken;
1309 const char *zType;
1310 int i, content_length;
1311 char zLine[2000]; /* A single line of input. */
1312
1313 if( zIpAddr ){
1314 if( nCycles==0 ){
1315 cgi_setenv("REMOTE_ADDR", zIpAddr);
1316 g.zIpAddr = mprintf("%s", zIpAddr);
1317 }
1318 }else{
1319 fossil_panic("missing SSH IP address");
1320 }
1321 if( fgets(zLine, sizeof(zLine),g.httpIn)==0 ){
1322 malformed_request("missing HTTP header");
1323 }
1324 cgi_trace(zLine);
1325 zToken = extract_token(zLine, &z);
1326 if( zToken==0 ){
1327 malformed_request("malformed HTTP header");
1328 }
1329
1330 if( fossil_strcmp(zToken, "echo")==0 ){
1331 /* start looking for probes to complete transport_open */
1332 zCmd = cgi_handle_ssh_probes(zLine, sizeof(zLine), z, zToken);
1333 if( fgets(zLine, sizeof(zLine),g.httpIn)==0 ){
1334 malformed_request("missing HTTP header");
1335 }
1336 cgi_trace(zLine);
1337 zToken = extract_token(zLine, &z);
1338 if( zToken==0 ){
1339 malformed_request("malformed HTTP header");
1340 }
1341 }else if( zToken && strlen(zToken)==0 ){
1342 /* transport_flip request and continued transport_open */
1343 cgi_handle_ssh_transport(zCmd);
1344 if( fgets(zLine, sizeof(zLine),g.httpIn)==0 ){
1345 malformed_request("missing HTTP header");
1346 }
1347 cgi_trace(zLine);
1348 zToken = extract_token(zLine, &z);
1349 if( zToken==0 ){
1350 malformed_request("malformed HTTP header");
1351 }
1352 }
1353
1354 if( fossil_strcmp(zToken,"GET")!=0 && fossil_strcmp(zToken,"POST")!=0
1355 && fossil_strcmp(zToken,"HEAD")!=0 ){
1356 malformed_request("unsupported HTTP method");
1357 }
1358
1359 if( nCycles==0 ){
1360 cgi_setenv("GATEWAY_INTERFACE","CGI/1.0");
1361 cgi_setenv("REQUEST_METHOD",zToken);
1362 }
1363
1364 zToken = extract_token(z, &z);
1365 if( zToken==0 ){
1366 malformed_request("malformed URL in HTTP header");
1367 }
1368 if( nCycles==0 ){
1369 cgi_setenv("REQUEST_URI", zToken);
1370 cgi_setenv("SCRIPT_NAME", "");
1371 }
1372
1373 for(i=0; zToken[i] && zToken[i]!='?'; i++){}
1374 if( zToken[i] ) zToken[i++] = 0;
1375 if( nCycles==0 ){
1376 cgi_setenv("PATH_INFO", zToken);
1377 }else{
1378 cgi_replace_parameter("PATH_INFO", zToken);
1379 }
1380
1381 /* Get all the optional fields that follow the first line.
1382 */
1383 while( fgets(zLine,sizeof(zLine),g.httpIn) ){
1384 char *zFieldName;
1385 char *zVal;
1386
1387 cgi_trace(zLine);
1388 zFieldName = extract_token(zLine,&zVal);
1389 if( zFieldName==0 || *zFieldName==0 ) break;
1390 while( fossil_isspace(*zVal) ){ zVal++; }
1391 i = strlen(zVal);
1392 while( i>0 && fossil_isspace(zVal[i-1]) ){ i--; }
1393 zVal[i] = 0;
1394 for(i=0; zFieldName[i]; i++){
1395 zFieldName[i] = fossil_tolower(zFieldName[i]);
1396 }
1397 if( fossil_strcmp(zFieldName,"content-length:")==0 ){
1398 content_length = atoi(zVal);
1399 }else if( fossil_strcmp(zFieldName,"content-type:")==0 ){
1400 g.zContentType = zType = mprintf("%s", zVal);
1401 }else if( fossil_strcmp(zFieldName,"host:")==0 ){
1402 if( nCycles==0 ){
1403 cgi_setenv("HTTP_HOST", zVal);
1404 }
1405 }else if( fossil_strcmp(zFieldName,"user-agent:")==0 ){
1406 if( nCycles==0 ){
1407 cgi_setenv("HTTP_USER_AGENT", zVal);
1408 }
1409 }else if( fossil_strcmp(zFieldName,"x-fossil-transport:")==0 ){
1410 if( fossil_strnicmp(zVal, "ssh", 3)==0 ){
1411 if( nCycles==0 ){
1412 g.fSshClient |= CGI_SSH_FOSSIL;
1413 g.fullHttpReply = 0;
1414 cgi_setenv("X-FOSSIL_TRANSPORT", zVal);
1415 }
1416 }
1417 }
1418 }
1419
1420 if( nCycles==0 ){
1421 if( ! ( g.fSshClient & CGI_SSH_FOSSIL ) ){
1422 /* did not find new fossil ssh transport */
1423 g.fSshClient &= ~CGI_SSH_CLIENT;
1424 g.fullHttpReply = 1;
1425 cgi_replace_parameter("REMOTE_ADDR", "127.0.0.1");
1426 }
1427 }
1428
1429 cgi_reset_content();
1430 cgi_destination(CGI_BODY);
1431
1432 if( content_length>0 && zType ){
1433 blob_zero(&g.cgiIn);
1434 if( fossil_strcmp(zType, "application/x-fossil")==0 ){
1435 blob_read_from_channel(&g.cgiIn, g.httpIn, content_length);
1436 blob_uncompress(&g.cgiIn, &g.cgiIn);
1437 }else if( fossil_strcmp(zType, "application/x-fossil-debug")==0 ){
1438 blob_read_from_channel(&g.cgiIn, g.httpIn, content_length);
1439 }else if( fossil_strcmp(zType, "application/x-fossil-uncompressed")==0 ){
1440 blob_read_from_channel(&g.cgiIn, g.httpIn, content_length);
1441 }
1442 }
1443 cgi_trace(0);
1444 nCycles++;
1445 }
1446
1447 /*
1448 ** This routine handles the old fossil SSH probes
1449 */
1450 char *cgi_handle_ssh_probes(char *zLine, int zSize, char *z, char *zToken){
1451 /* Start looking for probes */
1452 while( fossil_strcmp(zToken, "echo")==0 ){
1453 zToken = extract_token(z, &z);
1454 if( zToken==0 ){
1455 malformed_request("malformed probe");
1456 }
1457 if( fossil_strncmp(zToken, "test", 4)==0 ||
1458 fossil_strncmp(zToken, "probe-", 6)==0 ){
1459 fprintf(g.httpOut, "%s\n", zToken);
1460 fflush(g.httpOut);
1461 }else{
1462 malformed_request("malformed probe");
1463 }
1464 if( fgets(zLine, zSize, g.httpIn)==0 ){
1465 malformed_request("malformed probe");
1466 }
1467 cgi_trace(zLine);
1468 zToken = extract_token(zLine, &z);
1469 if( zToken==0 ){
1470 malformed_request("malformed probe");
1471 }
1472 }
1473
1474 /* Got all probes now first transport_open is completed
1475 ** so return the command that was requested
1476 */
1477 /* *zCmd = mprintf("%s", zToken); */
1478 return mprintf("%s", zToken);
1479 }
1480
1481 /*
1482 ** This routine handles the old fossil SSH transport_flip
1483 ** and transport_open communications if detected.
1484 */
1485 void cgi_handle_ssh_transport(const char *zCmd){
1486 char *z, *zToken;
1487 char zLine[2000]; /* A single line of input. */
1488
1489 /* look for second newline of transport_flip */
1490 if( fgets(zLine, sizeof(zLine),g.httpIn)==0 ){
1491 malformed_request("incorrect transport_flip");
1492 }
1493 cgi_trace(zLine);
1494 zToken = extract_token(zLine, &z);
1495 if( zToken && strlen(zToken)==0 ){
1496 /* look for path to fossil */
1497 if( fgets(zLine, sizeof(zLine),g.httpIn)==0 ){
1498 if ( zCmd==0 ){
1499 malformed_request("missing fossil command");
1500 }else{
1501 /* no new command so exit */
1502 fossil_exit(0);
1503 }
1504 }
1505 cgi_trace(zLine);
1506 zToken = extract_token(zLine, &z);
1507 if( zToken==0 ){
1508 malformed_request("malformed fossil command");
1509 }
1510 /* see if we've seen the command */
1511 if( zCmd && zCmd[0] && fossil_strcmp(zToken, zCmd)==0 ){
1512 return;
1513 }else{
1514 malformed_request("transport_open failed");
1515 }
1516 }else{
1517 malformed_request("transport_flip failed");
1518 }
1519 }
1520
1521 /*
1522 ** This routine handles a single SCGI request which is coming in on
1523 ** g.httpIn and which replies on g.httpOut
1524 **
1525
+3 -2
--- src/clone.c
+++ src/clone.c
@@ -90,18 +90,19 @@
9090
** URL must be in one of the following form: ([...] mean optional)
9191
** HTTP/HTTPS protocol:
9292
** http[s]://[userid[:password]@]host[:port][/path]
9393
**
9494
** SSH protocol:
95
-** ssh://[userid[:password]@]host[:port]/path/to/repo.fossil\\
96
-** [?fossil=path/to/fossil.exe]
95
+** ssh://[userid[:password]@]host[:port]/path/to/repo.fossil
9796
**
9897
** Filesystem:
9998
** [file://]path/to/repo.fossil
10099
**
101100
** Note: For ssh and filesystem, path must have an extra leading
102101
** '/' to use an absolute path.
102
+**
103
+** Note: the userid for SSH is the SSH account, not the Fossil account.
103104
**
104105
** By default, your current login name is used to create the default
105106
** admin user. This can be overridden using the -A|--admin-user
106107
** parameter.
107108
**
108109
--- src/clone.c
+++ src/clone.c
@@ -90,18 +90,19 @@
90 ** URL must be in one of the following form: ([...] mean optional)
91 ** HTTP/HTTPS protocol:
92 ** http[s]://[userid[:password]@]host[:port][/path]
93 **
94 ** SSH protocol:
95 ** ssh://[userid[:password]@]host[:port]/path/to/repo.fossil\\
96 ** [?fossil=path/to/fossil.exe]
97 **
98 ** Filesystem:
99 ** [file://]path/to/repo.fossil
100 **
101 ** Note: For ssh and filesystem, path must have an extra leading
102 ** '/' to use an absolute path.
 
 
103 **
104 ** By default, your current login name is used to create the default
105 ** admin user. This can be overridden using the -A|--admin-user
106 ** parameter.
107 **
108
--- src/clone.c
+++ src/clone.c
@@ -90,18 +90,19 @@
90 ** URL must be in one of the following form: ([...] mean optional)
91 ** HTTP/HTTPS protocol:
92 ** http[s]://[userid[:password]@]host[:port][/path]
93 **
94 ** SSH protocol:
95 ** ssh://[userid[:password]@]host[:port]/path/to/repo.fossil
 
96 **
97 ** Filesystem:
98 ** [file://]path/to/repo.fossil
99 **
100 ** Note: For ssh and filesystem, path must have an extra leading
101 ** '/' to use an absolute path.
102 **
103 ** Note: the userid for SSH is the SSH account, not the Fossil account.
104 **
105 ** By default, your current login name is used to create the default
106 ** admin user. This can be overridden using the -A|--admin-user
107 ** parameter.
108 **
109
+14 -1
--- src/http.c
+++ src/http.c
@@ -112,10 +112,11 @@
112112
fossil_free(zCredentials);
113113
}
114114
blob_appendf(pHdr, "Host: %s\r\n", g.urlHostname);
115115
blob_appendf(pHdr, "User-Agent: Fossil/" RELEASE_VERSION
116116
" (" MANIFEST_DATE " " MANIFEST_VERSION ")\r\n");
117
+ if( g.urlIsSsh ) blob_appendf(pHdr, "X-Fossil-Transport: SSH\r\n");
117118
if( g.fHttpTrace ){
118119
blob_appendf(pHdr, "Content-Type: application/x-fossil-debug\r\n");
119120
}else{
120121
blob_appendf(pHdr, "Content-Type: application/x-fossil\r\n");
121122
}
@@ -217,10 +218,20 @@
217218
if( iHttpVersion==0 ){
218219
closeConnection = 1;
219220
}else{
220221
closeConnection = 0;
221222
}
223
+ }else if( g.urlIsSsh && fossil_strnicmp(zLine, "status:", 7)==0 ){
224
+ if( sscanf(zLine, "Status: %d", &rc)!=1 ) goto write_err;
225
+ if( rc!=200 && rc!=302 ){
226
+ int ii;
227
+ for(ii=7; zLine[ii] && zLine[ii]!=' '; ii++){}
228
+ while( zLine[ii]==' ' ) ii++;
229
+ fossil_warning("server says: %s", &zLine[ii]);
230
+ goto write_err;
231
+ }
232
+ closeConnection = 0;
222233
}else if( fossil_strnicmp(zLine, "content-length:", 15)==0 ){
223234
for(i=15; fossil_isspace(zLine[i]); i++){}
224235
iLength = atoi(&zLine[i]);
225236
}else if( fossil_strnicmp(zLine, "connection:", 11)==0 ){
226237
char c;
@@ -295,12 +306,14 @@
295306
** Close the connection to the server if appropriate.
296307
**
297308
** FIXME: There is some bug in the lower layers that prevents the
298309
** connection from remaining open. The easiest fix for now is to
299310
** simply close and restart the connection for each round-trip.
311
+ **
312
+ ** For SSH we will leave the connection open.
300313
*/
301
- closeConnection = 1; /* FIX ME */
314
+ if( ! g.urlIsSsh ) closeConnection = 1; /* FIX ME */
302315
if( closeConnection ){
303316
transport_close();
304317
}else{
305318
transport_rewind();
306319
}
307320
--- src/http.c
+++ src/http.c
@@ -112,10 +112,11 @@
112 fossil_free(zCredentials);
113 }
114 blob_appendf(pHdr, "Host: %s\r\n", g.urlHostname);
115 blob_appendf(pHdr, "User-Agent: Fossil/" RELEASE_VERSION
116 " (" MANIFEST_DATE " " MANIFEST_VERSION ")\r\n");
 
117 if( g.fHttpTrace ){
118 blob_appendf(pHdr, "Content-Type: application/x-fossil-debug\r\n");
119 }else{
120 blob_appendf(pHdr, "Content-Type: application/x-fossil\r\n");
121 }
@@ -217,10 +218,20 @@
217 if( iHttpVersion==0 ){
218 closeConnection = 1;
219 }else{
220 closeConnection = 0;
221 }
 
 
 
 
 
 
 
 
 
 
222 }else if( fossil_strnicmp(zLine, "content-length:", 15)==0 ){
223 for(i=15; fossil_isspace(zLine[i]); i++){}
224 iLength = atoi(&zLine[i]);
225 }else if( fossil_strnicmp(zLine, "connection:", 11)==0 ){
226 char c;
@@ -295,12 +306,14 @@
295 ** Close the connection to the server if appropriate.
296 **
297 ** FIXME: There is some bug in the lower layers that prevents the
298 ** connection from remaining open. The easiest fix for now is to
299 ** simply close and restart the connection for each round-trip.
 
 
300 */
301 closeConnection = 1; /* FIX ME */
302 if( closeConnection ){
303 transport_close();
304 }else{
305 transport_rewind();
306 }
307
--- src/http.c
+++ src/http.c
@@ -112,10 +112,11 @@
112 fossil_free(zCredentials);
113 }
114 blob_appendf(pHdr, "Host: %s\r\n", g.urlHostname);
115 blob_appendf(pHdr, "User-Agent: Fossil/" RELEASE_VERSION
116 " (" MANIFEST_DATE " " MANIFEST_VERSION ")\r\n");
117 if( g.urlIsSsh ) blob_appendf(pHdr, "X-Fossil-Transport: SSH\r\n");
118 if( g.fHttpTrace ){
119 blob_appendf(pHdr, "Content-Type: application/x-fossil-debug\r\n");
120 }else{
121 blob_appendf(pHdr, "Content-Type: application/x-fossil\r\n");
122 }
@@ -217,10 +218,20 @@
218 if( iHttpVersion==0 ){
219 closeConnection = 1;
220 }else{
221 closeConnection = 0;
222 }
223 }else if( g.urlIsSsh && fossil_strnicmp(zLine, "status:", 7)==0 ){
224 if( sscanf(zLine, "Status: %d", &rc)!=1 ) goto write_err;
225 if( rc!=200 && rc!=302 ){
226 int ii;
227 for(ii=7; zLine[ii] && zLine[ii]!=' '; ii++){}
228 while( zLine[ii]==' ' ) ii++;
229 fossil_warning("server says: %s", &zLine[ii]);
230 goto write_err;
231 }
232 closeConnection = 0;
233 }else if( fossil_strnicmp(zLine, "content-length:", 15)==0 ){
234 for(i=15; fossil_isspace(zLine[i]); i++){}
235 iLength = atoi(&zLine[i]);
236 }else if( fossil_strnicmp(zLine, "connection:", 11)==0 ){
237 char c;
@@ -295,12 +306,14 @@
306 ** Close the connection to the server if appropriate.
307 **
308 ** FIXME: There is some bug in the lower layers that prevents the
309 ** connection from remaining open. The easiest fix for now is to
310 ** simply close and restart the connection for each round-trip.
311 **
312 ** For SSH we will leave the connection open.
313 */
314 if( ! g.urlIsSsh ) closeConnection = 1; /* FIX ME */
315 if( closeConnection ){
316 transport_close();
317 }else{
318 transport_rewind();
319 }
320
+2 -1
--- src/login.c
+++ src/login.c
@@ -793,11 +793,12 @@
793793
**
794794
** This feature allows the "fossil ui" command to give the user
795795
** full access rights without having to log in.
796796
*/
797797
zRemoteAddr = ipPrefix(zIpAddr = PD("REMOTE_ADDR","nil"));
798
- if( fossil_strcmp(zIpAddr, "127.0.0.1")==0
798
+ if( ( fossil_strcmp(zIpAddr, "127.0.0.1")==0 ||
799
+ g.fSshClient & CGI_SSH_CLIENT )
799800
&& g.useLocalauth
800801
&& db_get_int("localauth",0)==0
801802
&& P("HTTPS")==0
802803
){
803804
uid = db_int(0, "SELECT uid FROM user WHERE cap LIKE '%%s%%'");
804805
--- src/login.c
+++ src/login.c
@@ -793,11 +793,12 @@
793 **
794 ** This feature allows the "fossil ui" command to give the user
795 ** full access rights without having to log in.
796 */
797 zRemoteAddr = ipPrefix(zIpAddr = PD("REMOTE_ADDR","nil"));
798 if( fossil_strcmp(zIpAddr, "127.0.0.1")==0
 
799 && g.useLocalauth
800 && db_get_int("localauth",0)==0
801 && P("HTTPS")==0
802 ){
803 uid = db_int(0, "SELECT uid FROM user WHERE cap LIKE '%%s%%'");
804
--- src/login.c
+++ src/login.c
@@ -793,11 +793,12 @@
793 **
794 ** This feature allows the "fossil ui" command to give the user
795 ** full access rights without having to log in.
796 */
797 zRemoteAddr = ipPrefix(zIpAddr = PD("REMOTE_ADDR","nil"));
798 if( ( fossil_strcmp(zIpAddr, "127.0.0.1")==0 ||
799 g.fSshClient & CGI_SSH_CLIENT )
800 && g.useLocalauth
801 && db_get_int("localauth",0)==0
802 && P("HTTPS")==0
803 ){
804 uid = db_int(0, "SELECT uid FROM user WHERE cap LIKE '%%s%%'");
805
+36 -4
--- src/main.c
+++ src/main.c
@@ -134,10 +134,11 @@
134134
int fSqlPrint; /* True if -sqlprint flag is present */
135135
int fQuiet; /* True if -quiet flag is present */
136136
int fHttpTrace; /* Trace outbound HTTP requests */
137137
int fSystemTrace; /* Trace calls to fossil_system(), --systemtrace */
138138
int fSshTrace; /* Trace the SSH setup traffic */
139
+ int fSshClient; /* HTTP client flags for SSH client */
139140
char *zSshFossilCmd; /* Path to remoe fossil command for SSH */
140141
char *zSshCmd; /* SSH command string */
141142
char *zFossilUser; /* Fossil user if different from URL user */
142143
int fNoSync; /* Do not do an autosync ever. --nosync */
143144
char *zPath; /* Name of webpage being served */
@@ -582,10 +583,11 @@
582583
g.fQuiet = find_option("quiet", 0, 0)!=0;
583584
g.fSqlTrace = find_option("sqltrace", 0, 0)!=0;
584585
g.fSqlStats = find_option("sqlstats", 0, 0)!=0;
585586
g.fSystemTrace = find_option("systemtrace", 0, 0)!=0;
586587
g.fSshTrace = find_option("sshtrace", 0, 0)!=0;
588
+ g.fSshClient = 0;
587589
g.zSshFossilCmd = 0;
588590
g.zSshCmd = 0;
589591
g.zFossilUser = 0;
590592
if( g.fSqlTrace ) g.fSqlStats = 1;
591593
g.fSqlPrint = find_option("sqlprint", 0, 0)!=0;
@@ -1273,11 +1275,12 @@
12731275
}
12741276
12751277
/* Find the page that the user has requested, construct and deliver that
12761278
** page.
12771279
*/
1278
- if( g.zContentType && memcmp(g.zContentType, "application/x-fossil", 20)==0 ){
1280
+ if( g.zContentType &&
1281
+ strncmp(g.zContentType, "application/x-fossil", 20)==0 ){
12791282
zPathInfo = "/xfer";
12801283
}
12811284
set_base_url(0);
12821285
if( zPathInfo==0 || zPathInfo[0]==0
12831286
|| (zPathInfo[0]=='/' && zPathInfo[1]==0) ){
@@ -1689,39 +1692,68 @@
16891692
g.httpOut = stdout;
16901693
zIpAddr = 0;
16911694
}
16921695
if( zIpAddr==0 ){
16931696
zIpAddr = cgi_ssh_remote_addr(0);
1697
+ if( zIpAddr && zIpAddr[0] ){
1698
+ g.fSshClient |= CGI_SSH_CLIENT;
1699
+ }
16941700
}
16951701
find_server_repository(0);
16961702
g.zRepositoryName = enter_chroot_jail(g.zRepositoryName);
16971703
if( useSCGI ){
16981704
cgi_handle_scgi_request();
1705
+ }else if( g.fSshClient & CGI_SSH_CLIENT ){
1706
+ ssh_request_loop(zIpAddr, glob_create(zFileGlob));
16991707
}else{
17001708
cgi_handle_http_request(zIpAddr);
17011709
}
17021710
process_one_web_page(zNotFound, glob_create(zFileGlob));
17031711
}
1712
+
1713
+/*
1714
+** Process all requests in a single SSH connection if possible.
1715
+*/
1716
+void ssh_request_loop(const char *zIpAddr, Glob *FileGlob){
1717
+ do{
1718
+ cgi_handle_ssh_http_request(zIpAddr);
1719
+ process_one_web_page(0, FileGlob);
1720
+ blob_reset(&g.cgiIn);
1721
+ } while ( g.fSshClient & CGI_SSH_FOSSIL ||
1722
+ g.fSshClient & CGI_SSH_COMPAT );
1723
+}
17041724
17051725
/*
17061726
** Note that the following command is used by ssh:// processing.
17071727
**
17081728
** COMMAND: test-http
17091729
** Works like the http command but gives setup permission to all users.
1730
+**
1731
+** Options:
1732
+** --ssh-compat Compatibility option for SSH keys and old clients
17101733
*/
17111734
void cmd_test_http(void){
1735
+ const char *zIpAddr; /* IP address of remote client */
1736
+
1737
+ if( find_option("ssh-compat", 0, 0)!=0 ) g.fSshClient |= CGI_SSH_COMPAT;
17121738
Th_InitTraceLog();
17131739
login_set_capabilities("sx", 0);
17141740
g.useLocalauth = 1;
1715
- cgi_set_parameter("REMOTE_ADDR", "127.0.0.1");
17161741
g.httpIn = stdin;
17171742
g.httpOut = stdout;
17181743
find_server_repository(0);
17191744
g.cgiOutput = 1;
17201745
g.fullHttpReply = 1;
1721
- cgi_handle_http_request(0);
1722
- process_one_web_page(0, 0);
1746
+ zIpAddr = cgi_ssh_remote_addr(0);
1747
+ if( zIpAddr && zIpAddr[0] ){
1748
+ g.fSshClient |= CGI_SSH_CLIENT;
1749
+ ssh_request_loop(zIpAddr, 0);
1750
+ }else{
1751
+ cgi_set_parameter("REMOTE_ADDR", "127.0.0.1");
1752
+ cgi_handle_http_request(0);
1753
+ process_one_web_page(0, 0);
1754
+ }
17231755
}
17241756
17251757
#if !defined(_WIN32)
17261758
#if !defined(__DARWIN__) && !defined(__APPLE__) && !defined(__HAIKU__)
17271759
/*
17281760
--- src/main.c
+++ src/main.c
@@ -134,10 +134,11 @@
134 int fSqlPrint; /* True if -sqlprint flag is present */
135 int fQuiet; /* True if -quiet flag is present */
136 int fHttpTrace; /* Trace outbound HTTP requests */
137 int fSystemTrace; /* Trace calls to fossil_system(), --systemtrace */
138 int fSshTrace; /* Trace the SSH setup traffic */
 
139 char *zSshFossilCmd; /* Path to remoe fossil command for SSH */
140 char *zSshCmd; /* SSH command string */
141 char *zFossilUser; /* Fossil user if different from URL user */
142 int fNoSync; /* Do not do an autosync ever. --nosync */
143 char *zPath; /* Name of webpage being served */
@@ -582,10 +583,11 @@
582 g.fQuiet = find_option("quiet", 0, 0)!=0;
583 g.fSqlTrace = find_option("sqltrace", 0, 0)!=0;
584 g.fSqlStats = find_option("sqlstats", 0, 0)!=0;
585 g.fSystemTrace = find_option("systemtrace", 0, 0)!=0;
586 g.fSshTrace = find_option("sshtrace", 0, 0)!=0;
 
587 g.zSshFossilCmd = 0;
588 g.zSshCmd = 0;
589 g.zFossilUser = 0;
590 if( g.fSqlTrace ) g.fSqlStats = 1;
591 g.fSqlPrint = find_option("sqlprint", 0, 0)!=0;
@@ -1273,11 +1275,12 @@
1273 }
1274
1275 /* Find the page that the user has requested, construct and deliver that
1276 ** page.
1277 */
1278 if( g.zContentType && memcmp(g.zContentType, "application/x-fossil", 20)==0 ){
 
1279 zPathInfo = "/xfer";
1280 }
1281 set_base_url(0);
1282 if( zPathInfo==0 || zPathInfo[0]==0
1283 || (zPathInfo[0]=='/' && zPathInfo[1]==0) ){
@@ -1689,39 +1692,68 @@
1689 g.httpOut = stdout;
1690 zIpAddr = 0;
1691 }
1692 if( zIpAddr==0 ){
1693 zIpAddr = cgi_ssh_remote_addr(0);
 
 
 
1694 }
1695 find_server_repository(0);
1696 g.zRepositoryName = enter_chroot_jail(g.zRepositoryName);
1697 if( useSCGI ){
1698 cgi_handle_scgi_request();
 
 
1699 }else{
1700 cgi_handle_http_request(zIpAddr);
1701 }
1702 process_one_web_page(zNotFound, glob_create(zFileGlob));
1703 }
 
 
 
 
 
 
 
 
 
 
 
 
1704
1705 /*
1706 ** Note that the following command is used by ssh:// processing.
1707 **
1708 ** COMMAND: test-http
1709 ** Works like the http command but gives setup permission to all users.
 
 
 
1710 */
1711 void cmd_test_http(void){
 
 
 
1712 Th_InitTraceLog();
1713 login_set_capabilities("sx", 0);
1714 g.useLocalauth = 1;
1715 cgi_set_parameter("REMOTE_ADDR", "127.0.0.1");
1716 g.httpIn = stdin;
1717 g.httpOut = stdout;
1718 find_server_repository(0);
1719 g.cgiOutput = 1;
1720 g.fullHttpReply = 1;
1721 cgi_handle_http_request(0);
1722 process_one_web_page(0, 0);
 
 
 
 
 
 
 
1723 }
1724
1725 #if !defined(_WIN32)
1726 #if !defined(__DARWIN__) && !defined(__APPLE__) && !defined(__HAIKU__)
1727 /*
1728
--- src/main.c
+++ src/main.c
@@ -134,10 +134,11 @@
134 int fSqlPrint; /* True if -sqlprint flag is present */
135 int fQuiet; /* True if -quiet flag is present */
136 int fHttpTrace; /* Trace outbound HTTP requests */
137 int fSystemTrace; /* Trace calls to fossil_system(), --systemtrace */
138 int fSshTrace; /* Trace the SSH setup traffic */
139 int fSshClient; /* HTTP client flags for SSH client */
140 char *zSshFossilCmd; /* Path to remoe fossil command for SSH */
141 char *zSshCmd; /* SSH command string */
142 char *zFossilUser; /* Fossil user if different from URL user */
143 int fNoSync; /* Do not do an autosync ever. --nosync */
144 char *zPath; /* Name of webpage being served */
@@ -582,10 +583,11 @@
583 g.fQuiet = find_option("quiet", 0, 0)!=0;
584 g.fSqlTrace = find_option("sqltrace", 0, 0)!=0;
585 g.fSqlStats = find_option("sqlstats", 0, 0)!=0;
586 g.fSystemTrace = find_option("systemtrace", 0, 0)!=0;
587 g.fSshTrace = find_option("sshtrace", 0, 0)!=0;
588 g.fSshClient = 0;
589 g.zSshFossilCmd = 0;
590 g.zSshCmd = 0;
591 g.zFossilUser = 0;
592 if( g.fSqlTrace ) g.fSqlStats = 1;
593 g.fSqlPrint = find_option("sqlprint", 0, 0)!=0;
@@ -1273,11 +1275,12 @@
1275 }
1276
1277 /* Find the page that the user has requested, construct and deliver that
1278 ** page.
1279 */
1280 if( g.zContentType &&
1281 strncmp(g.zContentType, "application/x-fossil", 20)==0 ){
1282 zPathInfo = "/xfer";
1283 }
1284 set_base_url(0);
1285 if( zPathInfo==0 || zPathInfo[0]==0
1286 || (zPathInfo[0]=='/' && zPathInfo[1]==0) ){
@@ -1689,39 +1692,68 @@
1692 g.httpOut = stdout;
1693 zIpAddr = 0;
1694 }
1695 if( zIpAddr==0 ){
1696 zIpAddr = cgi_ssh_remote_addr(0);
1697 if( zIpAddr && zIpAddr[0] ){
1698 g.fSshClient |= CGI_SSH_CLIENT;
1699 }
1700 }
1701 find_server_repository(0);
1702 g.zRepositoryName = enter_chroot_jail(g.zRepositoryName);
1703 if( useSCGI ){
1704 cgi_handle_scgi_request();
1705 }else if( g.fSshClient & CGI_SSH_CLIENT ){
1706 ssh_request_loop(zIpAddr, glob_create(zFileGlob));
1707 }else{
1708 cgi_handle_http_request(zIpAddr);
1709 }
1710 process_one_web_page(zNotFound, glob_create(zFileGlob));
1711 }
1712
1713 /*
1714 ** Process all requests in a single SSH connection if possible.
1715 */
1716 void ssh_request_loop(const char *zIpAddr, Glob *FileGlob){
1717 do{
1718 cgi_handle_ssh_http_request(zIpAddr);
1719 process_one_web_page(0, FileGlob);
1720 blob_reset(&g.cgiIn);
1721 } while ( g.fSshClient & CGI_SSH_FOSSIL ||
1722 g.fSshClient & CGI_SSH_COMPAT );
1723 }
1724
1725 /*
1726 ** Note that the following command is used by ssh:// processing.
1727 **
1728 ** COMMAND: test-http
1729 ** Works like the http command but gives setup permission to all users.
1730 **
1731 ** Options:
1732 ** --ssh-compat Compatibility option for SSH keys and old clients
1733 */
1734 void cmd_test_http(void){
1735 const char *zIpAddr; /* IP address of remote client */
1736
1737 if( find_option("ssh-compat", 0, 0)!=0 ) g.fSshClient |= CGI_SSH_COMPAT;
1738 Th_InitTraceLog();
1739 login_set_capabilities("sx", 0);
1740 g.useLocalauth = 1;
 
1741 g.httpIn = stdin;
1742 g.httpOut = stdout;
1743 find_server_repository(0);
1744 g.cgiOutput = 1;
1745 g.fullHttpReply = 1;
1746 zIpAddr = cgi_ssh_remote_addr(0);
1747 if( zIpAddr && zIpAddr[0] ){
1748 g.fSshClient |= CGI_SSH_CLIENT;
1749 ssh_request_loop(zIpAddr, 0);
1750 }else{
1751 cgi_set_parameter("REMOTE_ADDR", "127.0.0.1");
1752 cgi_handle_http_request(0);
1753 process_one_web_page(0, 0);
1754 }
1755 }
1756
1757 #if !defined(_WIN32)
1758 #if !defined(__DARWIN__) && !defined(__APPLE__) && !defined(__HAIKU__)
1759 /*
1760
+2
--- src/xfer.c
+++ src/xfer.c
@@ -1228,10 +1228,11 @@
12281228
{
12291229
cgi_reset_content();
12301230
@ error bad\scommand:\s%F(blob_str(&xfer.line))
12311231
}
12321232
blobarray_reset(xfer.aToken, xfer.nToken);
1233
+ blob_reset(&xfer.line);
12331234
}
12341235
if( isPush ){
12351236
if( run_push_script()==TH_ERROR ){
12361237
cgi_reset_content();
12371238
@ error push\sscript\sfailed:\s%F(Th_GetResult(g.interp, 0))
@@ -1255,10 +1256,11 @@
12551256
if( xfer.syncPrivate ) send_private(&xfer);
12561257
}
12571258
if( recvConfig ){
12581259
configure_finalize_receive();
12591260
}
1261
+ db_multi_exec("DROP TABLE onremote");
12601262
manifest_crosslink_end();
12611263
12621264
/* Send the server timestamp last, in case prior processing happened
12631265
** to use up a significant fraction of our time window.
12641266
*/
12651267
--- src/xfer.c
+++ src/xfer.c
@@ -1228,10 +1228,11 @@
1228 {
1229 cgi_reset_content();
1230 @ error bad\scommand:\s%F(blob_str(&xfer.line))
1231 }
1232 blobarray_reset(xfer.aToken, xfer.nToken);
 
1233 }
1234 if( isPush ){
1235 if( run_push_script()==TH_ERROR ){
1236 cgi_reset_content();
1237 @ error push\sscript\sfailed:\s%F(Th_GetResult(g.interp, 0))
@@ -1255,10 +1256,11 @@
1255 if( xfer.syncPrivate ) send_private(&xfer);
1256 }
1257 if( recvConfig ){
1258 configure_finalize_receive();
1259 }
 
1260 manifest_crosslink_end();
1261
1262 /* Send the server timestamp last, in case prior processing happened
1263 ** to use up a significant fraction of our time window.
1264 */
1265
--- src/xfer.c
+++ src/xfer.c
@@ -1228,10 +1228,11 @@
1228 {
1229 cgi_reset_content();
1230 @ error bad\scommand:\s%F(blob_str(&xfer.line))
1231 }
1232 blobarray_reset(xfer.aToken, xfer.nToken);
1233 blob_reset(&xfer.line);
1234 }
1235 if( isPush ){
1236 if( run_push_script()==TH_ERROR ){
1237 cgi_reset_content();
1238 @ error push\sscript\sfailed:\s%F(Th_GetResult(g.interp, 0))
@@ -1255,10 +1256,11 @@
1256 if( xfer.syncPrivate ) send_private(&xfer);
1257 }
1258 if( recvConfig ){
1259 configure_finalize_receive();
1260 }
1261 db_multi_exec("DROP TABLE onremote");
1262 manifest_crosslink_end();
1263
1264 /* Send the server timestamp last, in case prior processing happened
1265 ** to use up a significant fraction of our time window.
1266 */
1267

Keyboard Shortcuts

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