Fossil SCM

Merge the enhanced SSH transport changes into trunk. The ssh command now runs a single instance of "fossil" directly on the remote side, obviating the need for a remote shell.

drh 2013-10-09 00:09 trunk merge
Commit dbb5e2d32a8c482e6fccfff89cda87081cc2d594
+249
--- src/cgi.c
+++ src/cgi.c
@@ -60,10 +60,17 @@
6060
** Destinations for output text.
6161
*/
6262
#define CGI_HEADER 0
6363
#define CGI_BODY 1
6464
65
+/*
66
+** Flags for SSH HTTP clients
67
+*/
68
+#define CGI_SSH_CLIENT 0x0001 /* Client is SSH */
69
+#define CGI_SSH_COMPAT 0x0002 /* Compat for old SSH transport */
70
+#define CGI_SSH_FOSSIL 0x0004 /* Use new Fossil SSH transport */
71
+
6572
#endif /* INTERFACE */
6673
6774
/*
6875
** The HTTP reply is generated in two pieces: the header and the body.
6976
** These pieces are generated separately because they are not necessary
@@ -1302,10 +1309,234 @@
13021309
}
13031310
}
13041311
cgi_init();
13051312
cgi_trace(0);
13061313
}
1314
+
1315
+/*
1316
+** This routine handles a single HTTP request from an SSH client which is
1317
+** coming in on g.httpIn and which replies on g.httpOut
1318
+**
1319
+** Once all the setup is finished, this procedure returns
1320
+** and subsequent code handles the actual generation of the webpage.
1321
+**
1322
+** It is called in a loop so some variables will need to be replaced
1323
+*/
1324
+void cgi_handle_ssh_http_request(const char *zIpAddr){
1325
+ static int nCycles = 0;
1326
+ static char *zCmd = 0;
1327
+ char *z, *zToken;
1328
+ const char *zType;
1329
+ int i, content_length;
1330
+ char zLine[2000]; /* A single line of input. */
1331
+
1332
+ if( zIpAddr ){
1333
+ if( nCycles==0 ){
1334
+ cgi_setenv("REMOTE_ADDR", zIpAddr);
1335
+ g.zIpAddr = mprintf("%s", zIpAddr);
1336
+ }
1337
+ }else{
1338
+ fossil_panic("missing SSH IP address");
1339
+ }
1340
+ if( fgets(zLine, sizeof(zLine),g.httpIn)==0 ){
1341
+ malformed_request("missing HTTP header");
1342
+ }
1343
+ cgi_trace(zLine);
1344
+ zToken = extract_token(zLine, &z);
1345
+ if( zToken==0 ){
1346
+ malformed_request("malformed HTTP header");
1347
+ }
1348
+
1349
+ if( fossil_strcmp(zToken, "echo")==0 ){
1350
+ /* start looking for probes to complete transport_open */
1351
+ zCmd = cgi_handle_ssh_probes(zLine, sizeof(zLine), z, zToken);
1352
+ if( fgets(zLine, sizeof(zLine),g.httpIn)==0 ){
1353
+ malformed_request("missing HTTP header");
1354
+ }
1355
+ cgi_trace(zLine);
1356
+ zToken = extract_token(zLine, &z);
1357
+ if( zToken==0 ){
1358
+ malformed_request("malformed HTTP header");
1359
+ }
1360
+ }else if( zToken && strlen(zToken)==0 && zCmd ){
1361
+ /* transport_flip request and continued transport_open */
1362
+ cgi_handle_ssh_transport(zCmd);
1363
+ if( fgets(zLine, sizeof(zLine),g.httpIn)==0 ){
1364
+ malformed_request("missing HTTP header");
1365
+ }
1366
+ cgi_trace(zLine);
1367
+ zToken = extract_token(zLine, &z);
1368
+ if( zToken==0 ){
1369
+ malformed_request("malformed HTTP header");
1370
+ }
1371
+ }
1372
+
1373
+ if( fossil_strcmp(zToken,"GET")!=0 && fossil_strcmp(zToken,"POST")!=0
1374
+ && fossil_strcmp(zToken,"HEAD")!=0 ){
1375
+ malformed_request("unsupported HTTP method");
1376
+ }
1377
+
1378
+ if( nCycles==0 ){
1379
+ cgi_setenv("GATEWAY_INTERFACE","CGI/1.0");
1380
+ cgi_setenv("REQUEST_METHOD",zToken);
1381
+ }
1382
+
1383
+ zToken = extract_token(z, &z);
1384
+ if( zToken==0 ){
1385
+ malformed_request("malformed URL in HTTP header");
1386
+ }
1387
+ if( nCycles==0 ){
1388
+ cgi_setenv("REQUEST_URI", zToken);
1389
+ cgi_setenv("SCRIPT_NAME", "");
1390
+ }
1391
+
1392
+ for(i=0; zToken[i] && zToken[i]!='?'; i++){}
1393
+ if( zToken[i] ) zToken[i++] = 0;
1394
+ if( nCycles==0 ){
1395
+ cgi_setenv("PATH_INFO", zToken);
1396
+ }else{
1397
+ cgi_replace_parameter("PATH_INFO", mprintf("%s",zToken));
1398
+ }
1399
+
1400
+ /* Get all the optional fields that follow the first line.
1401
+ */
1402
+ while( fgets(zLine,sizeof(zLine),g.httpIn) ){
1403
+ char *zFieldName;
1404
+ char *zVal;
1405
+
1406
+ cgi_trace(zLine);
1407
+ zFieldName = extract_token(zLine,&zVal);
1408
+ if( zFieldName==0 || *zFieldName==0 ) break;
1409
+ while( fossil_isspace(*zVal) ){ zVal++; }
1410
+ i = strlen(zVal);
1411
+ while( i>0 && fossil_isspace(zVal[i-1]) ){ i--; }
1412
+ zVal[i] = 0;
1413
+ for(i=0; zFieldName[i]; i++){
1414
+ zFieldName[i] = fossil_tolower(zFieldName[i]);
1415
+ }
1416
+ if( fossil_strcmp(zFieldName,"content-length:")==0 ){
1417
+ content_length = atoi(zVal);
1418
+ }else if( fossil_strcmp(zFieldName,"content-type:")==0 ){
1419
+ g.zContentType = zType = mprintf("%s", zVal);
1420
+ }else if( fossil_strcmp(zFieldName,"host:")==0 ){
1421
+ if( nCycles==0 ){
1422
+ cgi_setenv("HTTP_HOST", zVal);
1423
+ }
1424
+ }else if( fossil_strcmp(zFieldName,"user-agent:")==0 ){
1425
+ if( nCycles==0 ){
1426
+ cgi_setenv("HTTP_USER_AGENT", zVal);
1427
+ }
1428
+ }else if( fossil_strcmp(zFieldName,"x-fossil-transport:")==0 ){
1429
+ if( fossil_strnicmp(zVal, "ssh", 3)==0 ){
1430
+ if( nCycles==0 ){
1431
+ g.fSshClient |= CGI_SSH_FOSSIL;
1432
+ g.fullHttpReply = 0;
1433
+ }
1434
+ }
1435
+ }
1436
+ }
1437
+
1438
+ if( nCycles==0 ){
1439
+ if( ! ( g.fSshClient & CGI_SSH_FOSSIL ) ){
1440
+ /* did not find new fossil ssh transport */
1441
+ g.fSshClient &= ~CGI_SSH_CLIENT;
1442
+ g.fullHttpReply = 1;
1443
+ cgi_replace_parameter("REMOTE_ADDR", "127.0.0.1");
1444
+ }
1445
+ }
1446
+
1447
+ cgi_reset_content();
1448
+ cgi_destination(CGI_BODY);
1449
+
1450
+ if( content_length>0 && zType ){
1451
+ blob_zero(&g.cgiIn);
1452
+ if( fossil_strcmp(zType, "application/x-fossil")==0 ){
1453
+ blob_read_from_channel(&g.cgiIn, g.httpIn, content_length);
1454
+ blob_uncompress(&g.cgiIn, &g.cgiIn);
1455
+ }else if( fossil_strcmp(zType, "application/x-fossil-debug")==0 ){
1456
+ blob_read_from_channel(&g.cgiIn, g.httpIn, content_length);
1457
+ }else if( fossil_strcmp(zType, "application/x-fossil-uncompressed")==0 ){
1458
+ blob_read_from_channel(&g.cgiIn, g.httpIn, content_length);
1459
+ }
1460
+ }
1461
+ cgi_trace(0);
1462
+ nCycles++;
1463
+}
1464
+
1465
+/*
1466
+** This routine handles the old fossil SSH probes
1467
+*/
1468
+char *cgi_handle_ssh_probes(char *zLine, int zSize, char *z, char *zToken){
1469
+ /* Start looking for probes */
1470
+ while( fossil_strcmp(zToken, "echo")==0 ){
1471
+ zToken = extract_token(z, &z);
1472
+ if( zToken==0 ){
1473
+ malformed_request("malformed probe");
1474
+ }
1475
+ if( fossil_strncmp(zToken, "test", 4)==0 ||
1476
+ fossil_strncmp(zToken, "probe-", 6)==0 ){
1477
+ fprintf(g.httpOut, "%s\n", zToken);
1478
+ fflush(g.httpOut);
1479
+ }else{
1480
+ malformed_request("malformed probe");
1481
+ }
1482
+ if( fgets(zLine, zSize, g.httpIn)==0 ){
1483
+ malformed_request("malformed probe");
1484
+ }
1485
+ cgi_trace(zLine);
1486
+ zToken = extract_token(zLine, &z);
1487
+ if( zToken==0 ){
1488
+ malformed_request("malformed probe");
1489
+ }
1490
+ }
1491
+
1492
+ /* Got all probes now first transport_open is completed
1493
+ ** so return the command that was requested
1494
+ */
1495
+ g.fSshClient |= CGI_SSH_COMPAT;
1496
+ return mprintf("%s", zToken);
1497
+}
1498
+
1499
+/*
1500
+** This routine handles the old fossil SSH transport_flip
1501
+** and transport_open communications if detected.
1502
+*/
1503
+void cgi_handle_ssh_transport(const char *zCmd){
1504
+ char *z, *zToken;
1505
+ char zLine[2000]; /* A single line of input. */
1506
+
1507
+ /* look for second newline of transport_flip */
1508
+ if( fgets(zLine, sizeof(zLine),g.httpIn)==0 ){
1509
+ malformed_request("incorrect transport_flip");
1510
+ }
1511
+ cgi_trace(zLine);
1512
+ zToken = extract_token(zLine, &z);
1513
+ if( zToken && strlen(zToken)==0 ){
1514
+ /* look for path to fossil */
1515
+ if( fgets(zLine, sizeof(zLine),g.httpIn)==0 ){
1516
+ if ( zCmd==0 ){
1517
+ malformed_request("missing fossil command");
1518
+ }else{
1519
+ /* no new command so exit */
1520
+ fossil_exit(0);
1521
+ }
1522
+ }
1523
+ cgi_trace(zLine);
1524
+ zToken = extract_token(zLine, &z);
1525
+ if( zToken==0 ){
1526
+ malformed_request("malformed fossil command");
1527
+ }
1528
+ /* see if we've seen the command */
1529
+ if( zCmd && zCmd[0] && fossil_strcmp(zToken, zCmd)==0 ){
1530
+ return;
1531
+ }else{
1532
+ malformed_request("transport_open failed");
1533
+ }
1534
+ }else{
1535
+ malformed_request("transport_flip failed");
1536
+ }
1537
+}
13071538
13081539
/*
13091540
** This routine handles a single SCGI request which is coming in on
13101541
** g.httpIn and which replies on g.httpOut
13111542
**
@@ -1600,5 +1831,23 @@
16001831
cgi_set_status(304,"Not Modified");
16011832
cgi_reset_content();
16021833
cgi_reply();
16031834
fossil_exit(0);
16041835
}
1836
+
1837
+/*
1838
+** Check to see if the remote client is SSH and return
1839
+** its IP or return default
1840
+*/
1841
+const char *cgi_ssh_remote_addr(const char *zDefault){
1842
+ char *zIndex;
1843
+ const char *zSshConn = fossil_getenv("SSH_CONNECTION");
1844
+
1845
+ if( zSshConn && zSshConn[0] ){
1846
+ char *zSshClient = mprintf("%s",zSshConn);
1847
+ if( (zIndex = strchr(zSshClient,' '))!=0 ){
1848
+ zSshClient[zIndex-zSshClient] = '\0';
1849
+ return zSshClient;
1850
+ }
1851
+ }
1852
+ return zDefault;
1853
+}
16051854
--- src/cgi.c
+++ src/cgi.c
@@ -60,10 +60,17 @@
60 ** Destinations for output text.
61 */
62 #define CGI_HEADER 0
63 #define CGI_BODY 1
64
 
 
 
 
 
 
 
65 #endif /* INTERFACE */
66
67 /*
68 ** The HTTP reply is generated in two pieces: the header and the body.
69 ** These pieces are generated separately because they are not necessary
@@ -1302,10 +1309,234 @@
1302 }
1303 }
1304 cgi_init();
1305 cgi_trace(0);
1306 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1307
1308 /*
1309 ** This routine handles a single SCGI request which is coming in on
1310 ** g.httpIn and which replies on g.httpOut
1311 **
@@ -1600,5 +1831,23 @@
1600 cgi_set_status(304,"Not Modified");
1601 cgi_reset_content();
1602 cgi_reply();
1603 fossil_exit(0);
1604 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1605
--- src/cgi.c
+++ src/cgi.c
@@ -60,10 +60,17 @@
60 ** Destinations for output text.
61 */
62 #define CGI_HEADER 0
63 #define CGI_BODY 1
64
65 /*
66 ** Flags for SSH HTTP clients
67 */
68 #define CGI_SSH_CLIENT 0x0001 /* Client is SSH */
69 #define CGI_SSH_COMPAT 0x0002 /* Compat for old SSH transport */
70 #define CGI_SSH_FOSSIL 0x0004 /* Use new Fossil SSH transport */
71
72 #endif /* INTERFACE */
73
74 /*
75 ** The HTTP reply is generated in two pieces: the header and the body.
76 ** These pieces are generated separately because they are not necessary
@@ -1302,10 +1309,234 @@
1309 }
1310 }
1311 cgi_init();
1312 cgi_trace(0);
1313 }
1314
1315 /*
1316 ** This routine handles a single HTTP request from an SSH client which is
1317 ** coming in on g.httpIn and which replies on g.httpOut
1318 **
1319 ** Once all the setup is finished, this procedure returns
1320 ** and subsequent code handles the actual generation of the webpage.
1321 **
1322 ** It is called in a loop so some variables will need to be replaced
1323 */
1324 void cgi_handle_ssh_http_request(const char *zIpAddr){
1325 static int nCycles = 0;
1326 static char *zCmd = 0;
1327 char *z, *zToken;
1328 const char *zType;
1329 int i, content_length;
1330 char zLine[2000]; /* A single line of input. */
1331
1332 if( zIpAddr ){
1333 if( nCycles==0 ){
1334 cgi_setenv("REMOTE_ADDR", zIpAddr);
1335 g.zIpAddr = mprintf("%s", zIpAddr);
1336 }
1337 }else{
1338 fossil_panic("missing SSH IP address");
1339 }
1340 if( fgets(zLine, sizeof(zLine),g.httpIn)==0 ){
1341 malformed_request("missing HTTP header");
1342 }
1343 cgi_trace(zLine);
1344 zToken = extract_token(zLine, &z);
1345 if( zToken==0 ){
1346 malformed_request("malformed HTTP header");
1347 }
1348
1349 if( fossil_strcmp(zToken, "echo")==0 ){
1350 /* start looking for probes to complete transport_open */
1351 zCmd = cgi_handle_ssh_probes(zLine, sizeof(zLine), z, zToken);
1352 if( fgets(zLine, sizeof(zLine),g.httpIn)==0 ){
1353 malformed_request("missing HTTP header");
1354 }
1355 cgi_trace(zLine);
1356 zToken = extract_token(zLine, &z);
1357 if( zToken==0 ){
1358 malformed_request("malformed HTTP header");
1359 }
1360 }else if( zToken && strlen(zToken)==0 && zCmd ){
1361 /* transport_flip request and continued transport_open */
1362 cgi_handle_ssh_transport(zCmd);
1363 if( fgets(zLine, sizeof(zLine),g.httpIn)==0 ){
1364 malformed_request("missing HTTP header");
1365 }
1366 cgi_trace(zLine);
1367 zToken = extract_token(zLine, &z);
1368 if( zToken==0 ){
1369 malformed_request("malformed HTTP header");
1370 }
1371 }
1372
1373 if( fossil_strcmp(zToken,"GET")!=0 && fossil_strcmp(zToken,"POST")!=0
1374 && fossil_strcmp(zToken,"HEAD")!=0 ){
1375 malformed_request("unsupported HTTP method");
1376 }
1377
1378 if( nCycles==0 ){
1379 cgi_setenv("GATEWAY_INTERFACE","CGI/1.0");
1380 cgi_setenv("REQUEST_METHOD",zToken);
1381 }
1382
1383 zToken = extract_token(z, &z);
1384 if( zToken==0 ){
1385 malformed_request("malformed URL in HTTP header");
1386 }
1387 if( nCycles==0 ){
1388 cgi_setenv("REQUEST_URI", zToken);
1389 cgi_setenv("SCRIPT_NAME", "");
1390 }
1391
1392 for(i=0; zToken[i] && zToken[i]!='?'; i++){}
1393 if( zToken[i] ) zToken[i++] = 0;
1394 if( nCycles==0 ){
1395 cgi_setenv("PATH_INFO", zToken);
1396 }else{
1397 cgi_replace_parameter("PATH_INFO", mprintf("%s",zToken));
1398 }
1399
1400 /* Get all the optional fields that follow the first line.
1401 */
1402 while( fgets(zLine,sizeof(zLine),g.httpIn) ){
1403 char *zFieldName;
1404 char *zVal;
1405
1406 cgi_trace(zLine);
1407 zFieldName = extract_token(zLine,&zVal);
1408 if( zFieldName==0 || *zFieldName==0 ) break;
1409 while( fossil_isspace(*zVal) ){ zVal++; }
1410 i = strlen(zVal);
1411 while( i>0 && fossil_isspace(zVal[i-1]) ){ i--; }
1412 zVal[i] = 0;
1413 for(i=0; zFieldName[i]; i++){
1414 zFieldName[i] = fossil_tolower(zFieldName[i]);
1415 }
1416 if( fossil_strcmp(zFieldName,"content-length:")==0 ){
1417 content_length = atoi(zVal);
1418 }else if( fossil_strcmp(zFieldName,"content-type:")==0 ){
1419 g.zContentType = zType = mprintf("%s", zVal);
1420 }else if( fossil_strcmp(zFieldName,"host:")==0 ){
1421 if( nCycles==0 ){
1422 cgi_setenv("HTTP_HOST", zVal);
1423 }
1424 }else if( fossil_strcmp(zFieldName,"user-agent:")==0 ){
1425 if( nCycles==0 ){
1426 cgi_setenv("HTTP_USER_AGENT", zVal);
1427 }
1428 }else if( fossil_strcmp(zFieldName,"x-fossil-transport:")==0 ){
1429 if( fossil_strnicmp(zVal, "ssh", 3)==0 ){
1430 if( nCycles==0 ){
1431 g.fSshClient |= CGI_SSH_FOSSIL;
1432 g.fullHttpReply = 0;
1433 }
1434 }
1435 }
1436 }
1437
1438 if( nCycles==0 ){
1439 if( ! ( g.fSshClient & CGI_SSH_FOSSIL ) ){
1440 /* did not find new fossil ssh transport */
1441 g.fSshClient &= ~CGI_SSH_CLIENT;
1442 g.fullHttpReply = 1;
1443 cgi_replace_parameter("REMOTE_ADDR", "127.0.0.1");
1444 }
1445 }
1446
1447 cgi_reset_content();
1448 cgi_destination(CGI_BODY);
1449
1450 if( content_length>0 && zType ){
1451 blob_zero(&g.cgiIn);
1452 if( fossil_strcmp(zType, "application/x-fossil")==0 ){
1453 blob_read_from_channel(&g.cgiIn, g.httpIn, content_length);
1454 blob_uncompress(&g.cgiIn, &g.cgiIn);
1455 }else if( fossil_strcmp(zType, "application/x-fossil-debug")==0 ){
1456 blob_read_from_channel(&g.cgiIn, g.httpIn, content_length);
1457 }else if( fossil_strcmp(zType, "application/x-fossil-uncompressed")==0 ){
1458 blob_read_from_channel(&g.cgiIn, g.httpIn, content_length);
1459 }
1460 }
1461 cgi_trace(0);
1462 nCycles++;
1463 }
1464
1465 /*
1466 ** This routine handles the old fossil SSH probes
1467 */
1468 char *cgi_handle_ssh_probes(char *zLine, int zSize, char *z, char *zToken){
1469 /* Start looking for probes */
1470 while( fossil_strcmp(zToken, "echo")==0 ){
1471 zToken = extract_token(z, &z);
1472 if( zToken==0 ){
1473 malformed_request("malformed probe");
1474 }
1475 if( fossil_strncmp(zToken, "test", 4)==0 ||
1476 fossil_strncmp(zToken, "probe-", 6)==0 ){
1477 fprintf(g.httpOut, "%s\n", zToken);
1478 fflush(g.httpOut);
1479 }else{
1480 malformed_request("malformed probe");
1481 }
1482 if( fgets(zLine, zSize, g.httpIn)==0 ){
1483 malformed_request("malformed probe");
1484 }
1485 cgi_trace(zLine);
1486 zToken = extract_token(zLine, &z);
1487 if( zToken==0 ){
1488 malformed_request("malformed probe");
1489 }
1490 }
1491
1492 /* Got all probes now first transport_open is completed
1493 ** so return the command that was requested
1494 */
1495 g.fSshClient |= CGI_SSH_COMPAT;
1496 return mprintf("%s", zToken);
1497 }
1498
1499 /*
1500 ** This routine handles the old fossil SSH transport_flip
1501 ** and transport_open communications if detected.
1502 */
1503 void cgi_handle_ssh_transport(const char *zCmd){
1504 char *z, *zToken;
1505 char zLine[2000]; /* A single line of input. */
1506
1507 /* look for second newline of transport_flip */
1508 if( fgets(zLine, sizeof(zLine),g.httpIn)==0 ){
1509 malformed_request("incorrect transport_flip");
1510 }
1511 cgi_trace(zLine);
1512 zToken = extract_token(zLine, &z);
1513 if( zToken && strlen(zToken)==0 ){
1514 /* look for path to fossil */
1515 if( fgets(zLine, sizeof(zLine),g.httpIn)==0 ){
1516 if ( zCmd==0 ){
1517 malformed_request("missing fossil command");
1518 }else{
1519 /* no new command so exit */
1520 fossil_exit(0);
1521 }
1522 }
1523 cgi_trace(zLine);
1524 zToken = extract_token(zLine, &z);
1525 if( zToken==0 ){
1526 malformed_request("malformed fossil command");
1527 }
1528 /* see if we've seen the command */
1529 if( zCmd && zCmd[0] && fossil_strcmp(zToken, zCmd)==0 ){
1530 return;
1531 }else{
1532 malformed_request("transport_open failed");
1533 }
1534 }else{
1535 malformed_request("transport_flip failed");
1536 }
1537 }
1538
1539 /*
1540 ** This routine handles a single SCGI request which is coming in on
1541 ** g.httpIn and which replies on g.httpOut
1542 **
@@ -1600,5 +1831,23 @@
1831 cgi_set_status(304,"Not Modified");
1832 cgi_reset_content();
1833 cgi_reply();
1834 fossil_exit(0);
1835 }
1836
1837 /*
1838 ** Check to see if the remote client is SSH and return
1839 ** its IP or return default
1840 */
1841 const char *cgi_ssh_remote_addr(const char *zDefault){
1842 char *zIndex;
1843 const char *zSshConn = fossil_getenv("SSH_CONNECTION");
1844
1845 if( zSshConn && zSshConn[0] ){
1846 char *zSshClient = mprintf("%s",zSshConn);
1847 if( (zIndex = strchr(zSshClient,' '))!=0 ){
1848 zSshClient[zIndex-zSshClient] = '\0';
1849 return zSshClient;
1850 }
1851 }
1852 return zDefault;
1853 }
1854
+249
--- src/cgi.c
+++ src/cgi.c
@@ -60,10 +60,17 @@
6060
** Destinations for output text.
6161
*/
6262
#define CGI_HEADER 0
6363
#define CGI_BODY 1
6464
65
+/*
66
+** Flags for SSH HTTP clients
67
+*/
68
+#define CGI_SSH_CLIENT 0x0001 /* Client is SSH */
69
+#define CGI_SSH_COMPAT 0x0002 /* Compat for old SSH transport */
70
+#define CGI_SSH_FOSSIL 0x0004 /* Use new Fossil SSH transport */
71
+
6572
#endif /* INTERFACE */
6673
6774
/*
6875
** The HTTP reply is generated in two pieces: the header and the body.
6976
** These pieces are generated separately because they are not necessary
@@ -1302,10 +1309,234 @@
13021309
}
13031310
}
13041311
cgi_init();
13051312
cgi_trace(0);
13061313
}
1314
+
1315
+/*
1316
+** This routine handles a single HTTP request from an SSH client which is
1317
+** coming in on g.httpIn and which replies on g.httpOut
1318
+**
1319
+** Once all the setup is finished, this procedure returns
1320
+** and subsequent code handles the actual generation of the webpage.
1321
+**
1322
+** It is called in a loop so some variables will need to be replaced
1323
+*/
1324
+void cgi_handle_ssh_http_request(const char *zIpAddr){
1325
+ static int nCycles = 0;
1326
+ static char *zCmd = 0;
1327
+ char *z, *zToken;
1328
+ const char *zType;
1329
+ int i, content_length;
1330
+ char zLine[2000]; /* A single line of input. */
1331
+
1332
+ if( zIpAddr ){
1333
+ if( nCycles==0 ){
1334
+ cgi_setenv("REMOTE_ADDR", zIpAddr);
1335
+ g.zIpAddr = mprintf("%s", zIpAddr);
1336
+ }
1337
+ }else{
1338
+ fossil_panic("missing SSH IP address");
1339
+ }
1340
+ if( fgets(zLine, sizeof(zLine),g.httpIn)==0 ){
1341
+ malformed_request("missing HTTP header");
1342
+ }
1343
+ cgi_trace(zLine);
1344
+ zToken = extract_token(zLine, &z);
1345
+ if( zToken==0 ){
1346
+ malformed_request("malformed HTTP header");
1347
+ }
1348
+
1349
+ if( fossil_strcmp(zToken, "echo")==0 ){
1350
+ /* start looking for probes to complete transport_open */
1351
+ zCmd = cgi_handle_ssh_probes(zLine, sizeof(zLine), z, zToken);
1352
+ if( fgets(zLine, sizeof(zLine),g.httpIn)==0 ){
1353
+ malformed_request("missing HTTP header");
1354
+ }
1355
+ cgi_trace(zLine);
1356
+ zToken = extract_token(zLine, &z);
1357
+ if( zToken==0 ){
1358
+ malformed_request("malformed HTTP header");
1359
+ }
1360
+ }else if( zToken && strlen(zToken)==0 && zCmd ){
1361
+ /* transport_flip request and continued transport_open */
1362
+ cgi_handle_ssh_transport(zCmd);
1363
+ if( fgets(zLine, sizeof(zLine),g.httpIn)==0 ){
1364
+ malformed_request("missing HTTP header");
1365
+ }
1366
+ cgi_trace(zLine);
1367
+ zToken = extract_token(zLine, &z);
1368
+ if( zToken==0 ){
1369
+ malformed_request("malformed HTTP header");
1370
+ }
1371
+ }
1372
+
1373
+ if( fossil_strcmp(zToken,"GET")!=0 && fossil_strcmp(zToken,"POST")!=0
1374
+ && fossil_strcmp(zToken,"HEAD")!=0 ){
1375
+ malformed_request("unsupported HTTP method");
1376
+ }
1377
+
1378
+ if( nCycles==0 ){
1379
+ cgi_setenv("GATEWAY_INTERFACE","CGI/1.0");
1380
+ cgi_setenv("REQUEST_METHOD",zToken);
1381
+ }
1382
+
1383
+ zToken = extract_token(z, &z);
1384
+ if( zToken==0 ){
1385
+ malformed_request("malformed URL in HTTP header");
1386
+ }
1387
+ if( nCycles==0 ){
1388
+ cgi_setenv("REQUEST_URI", zToken);
1389
+ cgi_setenv("SCRIPT_NAME", "");
1390
+ }
1391
+
1392
+ for(i=0; zToken[i] && zToken[i]!='?'; i++){}
1393
+ if( zToken[i] ) zToken[i++] = 0;
1394
+ if( nCycles==0 ){
1395
+ cgi_setenv("PATH_INFO", zToken);
1396
+ }else{
1397
+ cgi_replace_parameter("PATH_INFO", mprintf("%s",zToken));
1398
+ }
1399
+
1400
+ /* Get all the optional fields that follow the first line.
1401
+ */
1402
+ while( fgets(zLine,sizeof(zLine),g.httpIn) ){
1403
+ char *zFieldName;
1404
+ char *zVal;
1405
+
1406
+ cgi_trace(zLine);
1407
+ zFieldName = extract_token(zLine,&zVal);
1408
+ if( zFieldName==0 || *zFieldName==0 ) break;
1409
+ while( fossil_isspace(*zVal) ){ zVal++; }
1410
+ i = strlen(zVal);
1411
+ while( i>0 && fossil_isspace(zVal[i-1]) ){ i--; }
1412
+ zVal[i] = 0;
1413
+ for(i=0; zFieldName[i]; i++){
1414
+ zFieldName[i] = fossil_tolower(zFieldName[i]);
1415
+ }
1416
+ if( fossil_strcmp(zFieldName,"content-length:")==0 ){
1417
+ content_length = atoi(zVal);
1418
+ }else if( fossil_strcmp(zFieldName,"content-type:")==0 ){
1419
+ g.zContentType = zType = mprintf("%s", zVal);
1420
+ }else if( fossil_strcmp(zFieldName,"host:")==0 ){
1421
+ if( nCycles==0 ){
1422
+ cgi_setenv("HTTP_HOST", zVal);
1423
+ }
1424
+ }else if( fossil_strcmp(zFieldName,"user-agent:")==0 ){
1425
+ if( nCycles==0 ){
1426
+ cgi_setenv("HTTP_USER_AGENT", zVal);
1427
+ }
1428
+ }else if( fossil_strcmp(zFieldName,"x-fossil-transport:")==0 ){
1429
+ if( fossil_strnicmp(zVal, "ssh", 3)==0 ){
1430
+ if( nCycles==0 ){
1431
+ g.fSshClient |= CGI_SSH_FOSSIL;
1432
+ g.fullHttpReply = 0;
1433
+ }
1434
+ }
1435
+ }
1436
+ }
1437
+
1438
+ if( nCycles==0 ){
1439
+ if( ! ( g.fSshClient & CGI_SSH_FOSSIL ) ){
1440
+ /* did not find new fossil ssh transport */
1441
+ g.fSshClient &= ~CGI_SSH_CLIENT;
1442
+ g.fullHttpReply = 1;
1443
+ cgi_replace_parameter("REMOTE_ADDR", "127.0.0.1");
1444
+ }
1445
+ }
1446
+
1447
+ cgi_reset_content();
1448
+ cgi_destination(CGI_BODY);
1449
+
1450
+ if( content_length>0 && zType ){
1451
+ blob_zero(&g.cgiIn);
1452
+ if( fossil_strcmp(zType, "application/x-fossil")==0 ){
1453
+ blob_read_from_channel(&g.cgiIn, g.httpIn, content_length);
1454
+ blob_uncompress(&g.cgiIn, &g.cgiIn);
1455
+ }else if( fossil_strcmp(zType, "application/x-fossil-debug")==0 ){
1456
+ blob_read_from_channel(&g.cgiIn, g.httpIn, content_length);
1457
+ }else if( fossil_strcmp(zType, "application/x-fossil-uncompressed")==0 ){
1458
+ blob_read_from_channel(&g.cgiIn, g.httpIn, content_length);
1459
+ }
1460
+ }
1461
+ cgi_trace(0);
1462
+ nCycles++;
1463
+}
1464
+
1465
+/*
1466
+** This routine handles the old fossil SSH probes
1467
+*/
1468
+char *cgi_handle_ssh_probes(char *zLine, int zSize, char *z, char *zToken){
1469
+ /* Start looking for probes */
1470
+ while( fossil_strcmp(zToken, "echo")==0 ){
1471
+ zToken = extract_token(z, &z);
1472
+ if( zToken==0 ){
1473
+ malformed_request("malformed probe");
1474
+ }
1475
+ if( fossil_strncmp(zToken, "test", 4)==0 ||
1476
+ fossil_strncmp(zToken, "probe-", 6)==0 ){
1477
+ fprintf(g.httpOut, "%s\n", zToken);
1478
+ fflush(g.httpOut);
1479
+ }else{
1480
+ malformed_request("malformed probe");
1481
+ }
1482
+ if( fgets(zLine, zSize, g.httpIn)==0 ){
1483
+ malformed_request("malformed probe");
1484
+ }
1485
+ cgi_trace(zLine);
1486
+ zToken = extract_token(zLine, &z);
1487
+ if( zToken==0 ){
1488
+ malformed_request("malformed probe");
1489
+ }
1490
+ }
1491
+
1492
+ /* Got all probes now first transport_open is completed
1493
+ ** so return the command that was requested
1494
+ */
1495
+ g.fSshClient |= CGI_SSH_COMPAT;
1496
+ return mprintf("%s", zToken);
1497
+}
1498
+
1499
+/*
1500
+** This routine handles the old fossil SSH transport_flip
1501
+** and transport_open communications if detected.
1502
+*/
1503
+void cgi_handle_ssh_transport(const char *zCmd){
1504
+ char *z, *zToken;
1505
+ char zLine[2000]; /* A single line of input. */
1506
+
1507
+ /* look for second newline of transport_flip */
1508
+ if( fgets(zLine, sizeof(zLine),g.httpIn)==0 ){
1509
+ malformed_request("incorrect transport_flip");
1510
+ }
1511
+ cgi_trace(zLine);
1512
+ zToken = extract_token(zLine, &z);
1513
+ if( zToken && strlen(zToken)==0 ){
1514
+ /* look for path to fossil */
1515
+ if( fgets(zLine, sizeof(zLine),g.httpIn)==0 ){
1516
+ if ( zCmd==0 ){
1517
+ malformed_request("missing fossil command");
1518
+ }else{
1519
+ /* no new command so exit */
1520
+ fossil_exit(0);
1521
+ }
1522
+ }
1523
+ cgi_trace(zLine);
1524
+ zToken = extract_token(zLine, &z);
1525
+ if( zToken==0 ){
1526
+ malformed_request("malformed fossil command");
1527
+ }
1528
+ /* see if we've seen the command */
1529
+ if( zCmd && zCmd[0] && fossil_strcmp(zToken, zCmd)==0 ){
1530
+ return;
1531
+ }else{
1532
+ malformed_request("transport_open failed");
1533
+ }
1534
+ }else{
1535
+ malformed_request("transport_flip failed");
1536
+ }
1537
+}
13071538
13081539
/*
13091540
** This routine handles a single SCGI request which is coming in on
13101541
** g.httpIn and which replies on g.httpOut
13111542
**
@@ -1600,5 +1831,23 @@
16001831
cgi_set_status(304,"Not Modified");
16011832
cgi_reset_content();
16021833
cgi_reply();
16031834
fossil_exit(0);
16041835
}
1836
+
1837
+/*
1838
+** Check to see if the remote client is SSH and return
1839
+** its IP or return default
1840
+*/
1841
+const char *cgi_ssh_remote_addr(const char *zDefault){
1842
+ char *zIndex;
1843
+ const char *zSshConn = fossil_getenv("SSH_CONNECTION");
1844
+
1845
+ if( zSshConn && zSshConn[0] ){
1846
+ char *zSshClient = mprintf("%s",zSshConn);
1847
+ if( (zIndex = strchr(zSshClient,' '))!=0 ){
1848
+ zSshClient[zIndex-zSshClient] = '\0';
1849
+ return zSshClient;
1850
+ }
1851
+ }
1852
+ return zDefault;
1853
+}
16051854
--- src/cgi.c
+++ src/cgi.c
@@ -60,10 +60,17 @@
60 ** Destinations for output text.
61 */
62 #define CGI_HEADER 0
63 #define CGI_BODY 1
64
 
 
 
 
 
 
 
65 #endif /* INTERFACE */
66
67 /*
68 ** The HTTP reply is generated in two pieces: the header and the body.
69 ** These pieces are generated separately because they are not necessary
@@ -1302,10 +1309,234 @@
1302 }
1303 }
1304 cgi_init();
1305 cgi_trace(0);
1306 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1307
1308 /*
1309 ** This routine handles a single SCGI request which is coming in on
1310 ** g.httpIn and which replies on g.httpOut
1311 **
@@ -1600,5 +1831,23 @@
1600 cgi_set_status(304,"Not Modified");
1601 cgi_reset_content();
1602 cgi_reply();
1603 fossil_exit(0);
1604 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1605
--- src/cgi.c
+++ src/cgi.c
@@ -60,10 +60,17 @@
60 ** Destinations for output text.
61 */
62 #define CGI_HEADER 0
63 #define CGI_BODY 1
64
65 /*
66 ** Flags for SSH HTTP clients
67 */
68 #define CGI_SSH_CLIENT 0x0001 /* Client is SSH */
69 #define CGI_SSH_COMPAT 0x0002 /* Compat for old SSH transport */
70 #define CGI_SSH_FOSSIL 0x0004 /* Use new Fossil SSH transport */
71
72 #endif /* INTERFACE */
73
74 /*
75 ** The HTTP reply is generated in two pieces: the header and the body.
76 ** These pieces are generated separately because they are not necessary
@@ -1302,10 +1309,234 @@
1309 }
1310 }
1311 cgi_init();
1312 cgi_trace(0);
1313 }
1314
1315 /*
1316 ** This routine handles a single HTTP request from an SSH client which is
1317 ** coming in on g.httpIn and which replies on g.httpOut
1318 **
1319 ** Once all the setup is finished, this procedure returns
1320 ** and subsequent code handles the actual generation of the webpage.
1321 **
1322 ** It is called in a loop so some variables will need to be replaced
1323 */
1324 void cgi_handle_ssh_http_request(const char *zIpAddr){
1325 static int nCycles = 0;
1326 static char *zCmd = 0;
1327 char *z, *zToken;
1328 const char *zType;
1329 int i, content_length;
1330 char zLine[2000]; /* A single line of input. */
1331
1332 if( zIpAddr ){
1333 if( nCycles==0 ){
1334 cgi_setenv("REMOTE_ADDR", zIpAddr);
1335 g.zIpAddr = mprintf("%s", zIpAddr);
1336 }
1337 }else{
1338 fossil_panic("missing SSH IP address");
1339 }
1340 if( fgets(zLine, sizeof(zLine),g.httpIn)==0 ){
1341 malformed_request("missing HTTP header");
1342 }
1343 cgi_trace(zLine);
1344 zToken = extract_token(zLine, &z);
1345 if( zToken==0 ){
1346 malformed_request("malformed HTTP header");
1347 }
1348
1349 if( fossil_strcmp(zToken, "echo")==0 ){
1350 /* start looking for probes to complete transport_open */
1351 zCmd = cgi_handle_ssh_probes(zLine, sizeof(zLine), z, zToken);
1352 if( fgets(zLine, sizeof(zLine),g.httpIn)==0 ){
1353 malformed_request("missing HTTP header");
1354 }
1355 cgi_trace(zLine);
1356 zToken = extract_token(zLine, &z);
1357 if( zToken==0 ){
1358 malformed_request("malformed HTTP header");
1359 }
1360 }else if( zToken && strlen(zToken)==0 && zCmd ){
1361 /* transport_flip request and continued transport_open */
1362 cgi_handle_ssh_transport(zCmd);
1363 if( fgets(zLine, sizeof(zLine),g.httpIn)==0 ){
1364 malformed_request("missing HTTP header");
1365 }
1366 cgi_trace(zLine);
1367 zToken = extract_token(zLine, &z);
1368 if( zToken==0 ){
1369 malformed_request("malformed HTTP header");
1370 }
1371 }
1372
1373 if( fossil_strcmp(zToken,"GET")!=0 && fossil_strcmp(zToken,"POST")!=0
1374 && fossil_strcmp(zToken,"HEAD")!=0 ){
1375 malformed_request("unsupported HTTP method");
1376 }
1377
1378 if( nCycles==0 ){
1379 cgi_setenv("GATEWAY_INTERFACE","CGI/1.0");
1380 cgi_setenv("REQUEST_METHOD",zToken);
1381 }
1382
1383 zToken = extract_token(z, &z);
1384 if( zToken==0 ){
1385 malformed_request("malformed URL in HTTP header");
1386 }
1387 if( nCycles==0 ){
1388 cgi_setenv("REQUEST_URI", zToken);
1389 cgi_setenv("SCRIPT_NAME", "");
1390 }
1391
1392 for(i=0; zToken[i] && zToken[i]!='?'; i++){}
1393 if( zToken[i] ) zToken[i++] = 0;
1394 if( nCycles==0 ){
1395 cgi_setenv("PATH_INFO", zToken);
1396 }else{
1397 cgi_replace_parameter("PATH_INFO", mprintf("%s",zToken));
1398 }
1399
1400 /* Get all the optional fields that follow the first line.
1401 */
1402 while( fgets(zLine,sizeof(zLine),g.httpIn) ){
1403 char *zFieldName;
1404 char *zVal;
1405
1406 cgi_trace(zLine);
1407 zFieldName = extract_token(zLine,&zVal);
1408 if( zFieldName==0 || *zFieldName==0 ) break;
1409 while( fossil_isspace(*zVal) ){ zVal++; }
1410 i = strlen(zVal);
1411 while( i>0 && fossil_isspace(zVal[i-1]) ){ i--; }
1412 zVal[i] = 0;
1413 for(i=0; zFieldName[i]; i++){
1414 zFieldName[i] = fossil_tolower(zFieldName[i]);
1415 }
1416 if( fossil_strcmp(zFieldName,"content-length:")==0 ){
1417 content_length = atoi(zVal);
1418 }else if( fossil_strcmp(zFieldName,"content-type:")==0 ){
1419 g.zContentType = zType = mprintf("%s", zVal);
1420 }else if( fossil_strcmp(zFieldName,"host:")==0 ){
1421 if( nCycles==0 ){
1422 cgi_setenv("HTTP_HOST", zVal);
1423 }
1424 }else if( fossil_strcmp(zFieldName,"user-agent:")==0 ){
1425 if( nCycles==0 ){
1426 cgi_setenv("HTTP_USER_AGENT", zVal);
1427 }
1428 }else if( fossil_strcmp(zFieldName,"x-fossil-transport:")==0 ){
1429 if( fossil_strnicmp(zVal, "ssh", 3)==0 ){
1430 if( nCycles==0 ){
1431 g.fSshClient |= CGI_SSH_FOSSIL;
1432 g.fullHttpReply = 0;
1433 }
1434 }
1435 }
1436 }
1437
1438 if( nCycles==0 ){
1439 if( ! ( g.fSshClient & CGI_SSH_FOSSIL ) ){
1440 /* did not find new fossil ssh transport */
1441 g.fSshClient &= ~CGI_SSH_CLIENT;
1442 g.fullHttpReply = 1;
1443 cgi_replace_parameter("REMOTE_ADDR", "127.0.0.1");
1444 }
1445 }
1446
1447 cgi_reset_content();
1448 cgi_destination(CGI_BODY);
1449
1450 if( content_length>0 && zType ){
1451 blob_zero(&g.cgiIn);
1452 if( fossil_strcmp(zType, "application/x-fossil")==0 ){
1453 blob_read_from_channel(&g.cgiIn, g.httpIn, content_length);
1454 blob_uncompress(&g.cgiIn, &g.cgiIn);
1455 }else if( fossil_strcmp(zType, "application/x-fossil-debug")==0 ){
1456 blob_read_from_channel(&g.cgiIn, g.httpIn, content_length);
1457 }else if( fossil_strcmp(zType, "application/x-fossil-uncompressed")==0 ){
1458 blob_read_from_channel(&g.cgiIn, g.httpIn, content_length);
1459 }
1460 }
1461 cgi_trace(0);
1462 nCycles++;
1463 }
1464
1465 /*
1466 ** This routine handles the old fossil SSH probes
1467 */
1468 char *cgi_handle_ssh_probes(char *zLine, int zSize, char *z, char *zToken){
1469 /* Start looking for probes */
1470 while( fossil_strcmp(zToken, "echo")==0 ){
1471 zToken = extract_token(z, &z);
1472 if( zToken==0 ){
1473 malformed_request("malformed probe");
1474 }
1475 if( fossil_strncmp(zToken, "test", 4)==0 ||
1476 fossil_strncmp(zToken, "probe-", 6)==0 ){
1477 fprintf(g.httpOut, "%s\n", zToken);
1478 fflush(g.httpOut);
1479 }else{
1480 malformed_request("malformed probe");
1481 }
1482 if( fgets(zLine, zSize, g.httpIn)==0 ){
1483 malformed_request("malformed probe");
1484 }
1485 cgi_trace(zLine);
1486 zToken = extract_token(zLine, &z);
1487 if( zToken==0 ){
1488 malformed_request("malformed probe");
1489 }
1490 }
1491
1492 /* Got all probes now first transport_open is completed
1493 ** so return the command that was requested
1494 */
1495 g.fSshClient |= CGI_SSH_COMPAT;
1496 return mprintf("%s", zToken);
1497 }
1498
1499 /*
1500 ** This routine handles the old fossil SSH transport_flip
1501 ** and transport_open communications if detected.
1502 */
1503 void cgi_handle_ssh_transport(const char *zCmd){
1504 char *z, *zToken;
1505 char zLine[2000]; /* A single line of input. */
1506
1507 /* look for second newline of transport_flip */
1508 if( fgets(zLine, sizeof(zLine),g.httpIn)==0 ){
1509 malformed_request("incorrect transport_flip");
1510 }
1511 cgi_trace(zLine);
1512 zToken = extract_token(zLine, &z);
1513 if( zToken && strlen(zToken)==0 ){
1514 /* look for path to fossil */
1515 if( fgets(zLine, sizeof(zLine),g.httpIn)==0 ){
1516 if ( zCmd==0 ){
1517 malformed_request("missing fossil command");
1518 }else{
1519 /* no new command so exit */
1520 fossil_exit(0);
1521 }
1522 }
1523 cgi_trace(zLine);
1524 zToken = extract_token(zLine, &z);
1525 if( zToken==0 ){
1526 malformed_request("malformed fossil command");
1527 }
1528 /* see if we've seen the command */
1529 if( zCmd && zCmd[0] && fossil_strcmp(zToken, zCmd)==0 ){
1530 return;
1531 }else{
1532 malformed_request("transport_open failed");
1533 }
1534 }else{
1535 malformed_request("transport_flip failed");
1536 }
1537 }
1538
1539 /*
1540 ** This routine handles a single SCGI request which is coming in on
1541 ** g.httpIn and which replies on g.httpOut
1542 **
@@ -1600,5 +1831,23 @@
1831 cgi_set_status(304,"Not Modified");
1832 cgi_reset_content();
1833 cgi_reply();
1834 fossil_exit(0);
1835 }
1836
1837 /*
1838 ** Check to see if the remote client is SSH and return
1839 ** its IP or return default
1840 */
1841 const char *cgi_ssh_remote_addr(const char *zDefault){
1842 char *zIndex;
1843 const char *zSshConn = fossil_getenv("SSH_CONNECTION");
1844
1845 if( zSshConn && zSshConn[0] ){
1846 char *zSshClient = mprintf("%s",zSshConn);
1847 if( (zIndex = strchr(zSshClient,' '))!=0 ){
1848 zSshClient[zIndex-zSshClient] = '\0';
1849 return zSshClient;
1850 }
1851 }
1852 return zDefault;
1853 }
1854
+25
--- src/clone.c
+++ src/clone.c
@@ -107,10 +107,11 @@
107107
**
108108
** Options:
109109
** --admin-user|-A USERNAME Make USERNAME the administrator
110110
** --private Also clone private branches
111111
** --ssl-identity=filename Use the SSL identity if requested by the server
112
+** --ssh-command|-c 'command' Use this SSH command
112113
**
113114
** See also: init
114115
*/
115116
void clone_cmd(void){
116117
char *zPassword;
@@ -117,10 +118,11 @@
117118
const char *zDefaultUser; /* Optional name of the default user */
118119
int nErr = 0;
119120
int bPrivate = 0; /* Also clone private branches */
120121
121122
if( find_option("private",0,0)!=0 ) bPrivate = SYNC_PRIVATE;
123
+ clone_ssh_find_options();
122124
url_proxy_options();
123125
if( g.argc < 4 ){
124126
usage("?OPTIONS? FILE-OR-URL NEW-REPOSITORY");
125127
}
126128
db_open_config(0);
@@ -167,10 +169,11 @@
167169
db_multi_exec(
168170
"REPLACE INTO config(name,value,mtime)"
169171
" VALUES('server-code', lower(hex(randomblob(20))), now());"
170172
);
171173
url_enable_proxy(0);
174
+ clone_ssh_db_set_options();
172175
url_get_password_if_needed();
173176
g.xlinkClusterOnly = 1;
174177
nErr = client_sync(SYNC_CLONE | bPrivate,CONFIGSET_ALL,0);
175178
g.xlinkClusterOnly = 0;
176179
verify_cancel();
@@ -188,5 +191,27 @@
188191
fossil_print("project-id: %s\n", db_get("project-code", 0));
189192
zPassword = db_text(0, "SELECT pw FROM user WHERE login=%Q", g.zLogin);
190193
fossil_print("admin-user: %s (password is \"%s\")\n", g.zLogin, zPassword);
191194
db_end_transaction(0);
192195
}
196
+
197
+/*
198
+** Look for SSH clone command line options and setup in globals.
199
+*/
200
+void clone_ssh_find_options(void){
201
+ const char *zSshCmd; /* SSH command string */
202
+
203
+ zSshCmd = find_option("ssh-command","c",1);
204
+ if( zSshCmd && zSshCmd[0] ){
205
+ g.zSshCmd = mprintf("%s", zSshCmd);
206
+ }
207
+}
208
+
209
+/*
210
+** Set SSH options discovered in global variables (set from command line
211
+** options).
212
+*/
213
+void clone_ssh_db_set_options(void){
214
+ if( g.zSshCmd && g.zSshCmd[0] ){
215
+ db_set("ssh-command", g.zSshCmd, 0);
216
+ }
217
+}
193218
--- src/clone.c
+++ src/clone.c
@@ -107,10 +107,11 @@
107 **
108 ** Options:
109 ** --admin-user|-A USERNAME Make USERNAME the administrator
110 ** --private Also clone private branches
111 ** --ssl-identity=filename Use the SSL identity if requested by the server
 
112 **
113 ** See also: init
114 */
115 void clone_cmd(void){
116 char *zPassword;
@@ -117,10 +118,11 @@
117 const char *zDefaultUser; /* Optional name of the default user */
118 int nErr = 0;
119 int bPrivate = 0; /* Also clone private branches */
120
121 if( find_option("private",0,0)!=0 ) bPrivate = SYNC_PRIVATE;
 
122 url_proxy_options();
123 if( g.argc < 4 ){
124 usage("?OPTIONS? FILE-OR-URL NEW-REPOSITORY");
125 }
126 db_open_config(0);
@@ -167,10 +169,11 @@
167 db_multi_exec(
168 "REPLACE INTO config(name,value,mtime)"
169 " VALUES('server-code', lower(hex(randomblob(20))), now());"
170 );
171 url_enable_proxy(0);
 
172 url_get_password_if_needed();
173 g.xlinkClusterOnly = 1;
174 nErr = client_sync(SYNC_CLONE | bPrivate,CONFIGSET_ALL,0);
175 g.xlinkClusterOnly = 0;
176 verify_cancel();
@@ -188,5 +191,27 @@
188 fossil_print("project-id: %s\n", db_get("project-code", 0));
189 zPassword = db_text(0, "SELECT pw FROM user WHERE login=%Q", g.zLogin);
190 fossil_print("admin-user: %s (password is \"%s\")\n", g.zLogin, zPassword);
191 db_end_transaction(0);
192 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
193
--- src/clone.c
+++ src/clone.c
@@ -107,10 +107,11 @@
107 **
108 ** Options:
109 ** --admin-user|-A USERNAME Make USERNAME the administrator
110 ** --private Also clone private branches
111 ** --ssl-identity=filename Use the SSL identity if requested by the server
112 ** --ssh-command|-c 'command' Use this SSH command
113 **
114 ** See also: init
115 */
116 void clone_cmd(void){
117 char *zPassword;
@@ -117,10 +118,11 @@
118 const char *zDefaultUser; /* Optional name of the default user */
119 int nErr = 0;
120 int bPrivate = 0; /* Also clone private branches */
121
122 if( find_option("private",0,0)!=0 ) bPrivate = SYNC_PRIVATE;
123 clone_ssh_find_options();
124 url_proxy_options();
125 if( g.argc < 4 ){
126 usage("?OPTIONS? FILE-OR-URL NEW-REPOSITORY");
127 }
128 db_open_config(0);
@@ -167,10 +169,11 @@
169 db_multi_exec(
170 "REPLACE INTO config(name,value,mtime)"
171 " VALUES('server-code', lower(hex(randomblob(20))), now());"
172 );
173 url_enable_proxy(0);
174 clone_ssh_db_set_options();
175 url_get_password_if_needed();
176 g.xlinkClusterOnly = 1;
177 nErr = client_sync(SYNC_CLONE | bPrivate,CONFIGSET_ALL,0);
178 g.xlinkClusterOnly = 0;
179 verify_cancel();
@@ -188,5 +191,27 @@
191 fossil_print("project-id: %s\n", db_get("project-code", 0));
192 zPassword = db_text(0, "SELECT pw FROM user WHERE login=%Q", g.zLogin);
193 fossil_print("admin-user: %s (password is \"%s\")\n", g.zLogin, zPassword);
194 db_end_transaction(0);
195 }
196
197 /*
198 ** Look for SSH clone command line options and setup in globals.
199 */
200 void clone_ssh_find_options(void){
201 const char *zSshCmd; /* SSH command string */
202
203 zSshCmd = find_option("ssh-command","c",1);
204 if( zSshCmd && zSshCmd[0] ){
205 g.zSshCmd = mprintf("%s", zSshCmd);
206 }
207 }
208
209 /*
210 ** Set SSH options discovered in global variables (set from command line
211 ** options).
212 */
213 void clone_ssh_db_set_options(void){
214 if( g.zSshCmd && g.zSshCmd[0] ){
215 db_set("ssh-command", g.zSshCmd, 0);
216 }
217 }
218
+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
--- src/http_socket.c
+++ src/http_socket.c
@@ -209,5 +209,22 @@
209209
N -= (size_t)got;
210210
pContent = (void*)&((char*)pContent)[got];
211211
}
212212
return total;
213213
}
214
+
215
+/*
216
+** Attempt to resolve g.urlName to IP and setup g.zIpAddr so rcvfrom gets
217
+** populated. For hostnames with more than one IP (or if overridden in
218
+** ~/.ssh/config) the rcvfrom may not match the host to which we connect.
219
+*/
220
+void socket_ssh_resolve_addr(void){
221
+ struct hostent *pHost; /* Used to make best effort for rcvfrom */
222
+ struct sockaddr_in addr;
223
+
224
+ memset(&addr, 0, sizeof(addr));
225
+ pHost = gethostbyname(g.urlName);
226
+ if( pHost!=0 ){
227
+ memcpy(&addr.sin_addr,pHost->h_addr_list[0],pHost->h_length);
228
+ g.zIpAddr = mprintf("%s", inet_ntoa(addr.sin_addr));
229
+ }
230
+}
214231
--- src/http_socket.c
+++ src/http_socket.c
@@ -209,5 +209,22 @@
209 N -= (size_t)got;
210 pContent = (void*)&((char*)pContent)[got];
211 }
212 return total;
213 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
214
--- src/http_socket.c
+++ src/http_socket.c
@@ -209,5 +209,22 @@
209 N -= (size_t)got;
210 pContent = (void*)&((char*)pContent)[got];
211 }
212 return total;
213 }
214
215 /*
216 ** Attempt to resolve g.urlName to IP and setup g.zIpAddr so rcvfrom gets
217 ** populated. For hostnames with more than one IP (or if overridden in
218 ** ~/.ssh/config) the rcvfrom may not match the host to which we connect.
219 */
220 void socket_ssh_resolve_addr(void){
221 struct hostent *pHost; /* Used to make best effort for rcvfrom */
222 struct sockaddr_in addr;
223
224 memset(&addr, 0, sizeof(addr));
225 pHost = gethostbyname(g.urlName);
226 if( pHost!=0 ){
227 memcpy(&addr.sin_addr,pHost->h_addr_list[0],pHost->h_length);
228 g.zIpAddr = mprintf("%s", inet_ntoa(addr.sin_addr));
229 }
230 }
231
--- src/http_transport.c
+++ src/http_transport.c
@@ -74,25 +74,10 @@
7474
transport.nSent = 0;
7575
transport.nRcvd = 0;
7676
}
7777
}
7878
79
-/*
80
-** Read text from sshIn. Zero-terminate and remove trailing
81
-** whitespace.
82
-*/
83
-static void sshin_read(char *zBuf, int szBuf){
84
- int got;
85
- zBuf[0] = 0;
86
- got = read(sshIn, zBuf, szBuf-1);
87
- while( got>=0 ){
88
- zBuf[got] = 0;
89
- if( got==0 || !fossil_isspace(zBuf[got-1]) ) break;
90
- got--;
91
- }
92
-}
93
-
9479
/*
9580
** Default SSH command
9681
*/
9782
#ifdef __MINGW32__
9883
static char zDefaultSshCmd[] = "ssh -T";
@@ -99,183 +84,60 @@
9984
#else
10085
static char zDefaultSshCmd[] = "ssh -e none -T";
10186
#endif
10287
10388
/*
104
-** Generate a random SSH link problem keyword
105
-*/
106
-static int random_probe(char *zProbe, int nProbe){
107
- unsigned r[4];
108
- sqlite3_randomness(sizeof(r), r);
109
- sqlite3_snprintf(nProbe, zProbe, "probe-%08x%08x%08x%08x",
110
- r[0], r[1], r[2], r[3]);
111
- return (int)strlen(zProbe);
112
-}
113
-
114
-/*
115
-** Bring up an SSH link. This involves sending some "echo" commands and
116
-** get back appropriate responses. The point is to move past the MOTD and
117
-** verify that the link is working.
118
-*/
119
-static void transport_ssh_startup(void){
120
- char *zIn; /* An input line received back from remote */
121
- int nWait; /* Number of times waiting for the MOTD */
122
- char zProbe[40]; /* Text of the random probe */
123
- int nProbe; /* Size of probe message */
124
- int nIn; /* Size of input */
125
- static const int nBuf = 10000; /* Size of input buffer */
126
-
127
- zIn = fossil_malloc(nBuf);
128
- nProbe = random_probe(zProbe, sizeof(zProbe));
129
- fprintf(sshOut, "echo %s\n", zProbe);
130
- fflush(sshOut);
131
- if( g.fSshTrace ){
132
- printf("Sent: [echo %s]\n", zProbe);
133
- fflush(stdout);
134
- }
135
- memset(zIn, '*', nProbe);
136
- for(nWait=1; nWait<=10; nWait++){
137
- sshin_read(zIn+nProbe, nBuf-nProbe);
138
- if( g.fSshTrace ){
139
- printf("Got back-----------------------------------------------\n"
140
- "%s\n"
141
- "-------------------------------------------------------\n",
142
- zIn+nProbe);
143
- }
144
- if( strstr(zIn, zProbe) ) break;
145
- sqlite3_sleep(100*nWait);
146
- nIn = (int)strlen(zIn);
147
- memcpy(zIn, zIn+(nIn-nProbe), nProbe);
148
- if( g.fSshTrace ){
149
- printf("Fetching more text. Looking for [%s]...\n", zProbe);
150
- fflush(stdout);
151
- }
152
- }
153
- nProbe = random_probe(zProbe, sizeof(zProbe));
154
- fprintf(sshOut, "echo %s\n", zProbe);
155
- fflush(sshOut);
156
- if( g.fSshTrace ){
157
- printf("Sent: [echo %s]\n", zProbe);
158
- fflush(stdout);
159
- }
160
- sshin_read(zIn, nBuf);
161
- if( zIn[0]==0 ){
162
- sqlite3_sleep(250);
163
- sshin_read(zIn, nBuf);
164
- }
165
- if( g.fSshTrace ){
166
- printf("Got back-----------------------------------------------\n"
167
- "%s\n"
168
- "-------------------------------------------------------\n", zIn);
169
- }
170
- if( memcmp(zIn, zProbe, nProbe)!=0 ){
171
- pclose2(sshIn, sshOut, sshPid);
172
- fossil_fatal("ssh connection failed: [%s]", zIn);
173
- }
174
- fossil_free(zIn);
175
-}
176
-
177
-/*
178
-** Global initialization of the transport layer
179
-*/
180
-void transport_global_startup(void){
181
- if( g.urlIsSsh ){
182
- /* Only SSH requires a global initialization. For SSH we need to create
183
- ** and run an SSH command to talk to the remote machine.
184
- */
185
- const char *zSsh; /* The base SSH command */
186
- Blob zCmd; /* The SSH command */
187
- char *zHost; /* The host name to contact */
188
- int n; /* Size of prefix string */
189
-
190
- zSsh = db_get("ssh-command", zDefaultSshCmd);
191
- blob_init(&zCmd, zSsh, -1);
192
- if( g.urlPort!=g.urlDfltPort ){
193
-#ifdef __MINGW32__
194
- blob_appendf(&zCmd, " -P %d", g.urlPort);
195
-#else
196
- blob_appendf(&zCmd, " -p %d", g.urlPort);
197
-#endif
198
- }
199
- fossil_force_newline();
200
- fossil_print("%s", blob_str(&zCmd)); /* Show the base of the SSH command */
201
- if( g.urlUser && g.urlUser[0] ){
202
- zHost = mprintf("%s@%s", g.urlUser, g.urlName);
203
-#ifdef __MINGW32__
204
- /* Only win32 (and specifically PLINK.EXE) support the -pw option */
205
- if( g.urlPasswd && g.urlPasswd[0] ){
206
- Blob pw;
207
- blob_zero(&pw);
208
- if( g.urlPasswd[0]=='*' ){
209
- char *zPrompt;
210
- zPrompt = mprintf("Password for [%s]: ", zHost);
211
- prompt_for_password(zPrompt, &pw, 0);
212
- free(zPrompt);
213
- }else{
214
- blob_init(&pw, g.urlPasswd, -1);
215
- }
216
- blob_append(&zCmd, " -pw ", -1);
217
- shell_escape(&zCmd, blob_str(&pw));
218
- blob_reset(&pw);
219
- fossil_print(" -pw ********"); /* Do not show the password text */
220
- }
221
-#endif
222
- }else{
223
- zHost = mprintf("%s", g.urlName);
224
- }
225
- n = blob_size(&zCmd);
226
- blob_append(&zCmd, " ", 1);
227
- shell_escape(&zCmd, zHost);
228
- if( g.urlShell ){
229
- blob_appendf(&zCmd, " %s", g.urlShell);
230
- }else{
231
-#if defined(FOSSIL_ENABLE_SSH_FAR_SIDE)
232
- /* The following works. But only if the fossil on the remote side
233
- ** is recent enough to support the test-ssh-far-side command. That
234
- ** command was added on 2013-02-06. We will leave this turned off
235
- ** until most fossil servers have upgraded to that version or a later
236
- ** version. The sync will still work as long as the shell on the far
237
- ** side is bash and not tcsh. And if the default far side shell is
238
- ** tcsh, then the shell=/bin/bash query parameter can be used as a
239
- ** work-around. Enable this code after about a year...
240
- */
241
- blob_appendf(&zCmd, " exec %s test-ssh-far-side", g.urlFossil);
242
-#endif
243
- }
244
- fossil_print("%s\n", blob_str(&zCmd)+n); /* Show tail of SSH command */
245
- free(zHost);
246
- popen2(blob_str(&zCmd), &sshIn, &sshOut, &sshPid);
247
- if( sshPid==0 ){
248
- fossil_fatal("cannot start ssh tunnel using [%b]", &zCmd);
249
- }
250
- blob_reset(&zCmd);
251
- transport_ssh_startup();
252
- }
253
-}
254
-
255
-/*
256
-** COMMAND: test-ssh-far-side
257
-**
258
-** Read lines of input text, one by one, and evaluate each line using
259
-** system(). The ssh: sync protocol uses this on the far side of the
260
-** SSH link.
261
-*/
262
-void test_ssh_far_side_cmd(void){
263
- int i = 0;
264
- int got;
265
- char zLine[5000];
266
- while( i<sizeof(zLine) ){
267
- got = read(0, zLine+i, 1);
268
- if( got==0 ) return;
269
- if( zLine[i]=='\n' ){
270
- zLine[i] = 0;
271
- system(zLine);
272
- i = 0;
273
- }else{
274
- i++;
275
- }
276
- }
89
+** SSH initialization of the transport layer
90
+*/
91
+int transport_ssh_open(void){
92
+ /* For SSH we need to create and run SSH fossil http
93
+ ** to talk to the remote machine.
94
+ */
95
+ const char *zSsh; /* The base SSH command */
96
+ Blob zCmd; /* The SSH command */
97
+ char *zHost; /* The host name to contact */
98
+ int n; /* Size of prefix string */
99
+
100
+ socket_ssh_resolve_addr();
101
+ zSsh = db_get("ssh-command", zDefaultSshCmd);
102
+ blob_init(&zCmd, zSsh, -1);
103
+ if( g.urlPort!=g.urlDfltPort && g.urlPort ){
104
+#ifdef __MINGW32__
105
+ blob_appendf(&zCmd, " -P %d", g.urlPort);
106
+#else
107
+ blob_appendf(&zCmd, " -p %d", g.urlPort);
108
+#endif
109
+ }
110
+ if( g.fSshTrace ){
111
+ fossil_force_newline();
112
+ fossil_print("%s", blob_str(&zCmd)); /* Show the base of the SSH command */
113
+ }
114
+ if( g.urlUser && g.urlUser[0] ){
115
+ zHost = mprintf("%s@%s", g.urlUser, g.urlName);
116
+ }else{
117
+ zHost = mprintf("%s", g.urlName);
118
+ }
119
+ n = blob_size(&zCmd);
120
+ blob_append(&zCmd, " ", 1);
121
+ shell_escape(&zCmd, zHost);
122
+ blob_append(&zCmd, " ", 1);
123
+ shell_escape(&zCmd, mprintf("%s", g.urlFossil));
124
+ blob_append(&zCmd, " test-http", 10);
125
+ if( g.urlPath && g.urlPath[0] ){
126
+ blob_append(&zCmd, " ", 1);
127
+ shell_escape(&zCmd, mprintf("%s", g.urlPath));
128
+ }
129
+ if( g.fSshTrace ){
130
+ fossil_print("%s\n", blob_str(&zCmd)+n); /* Show tail of SSH command */
131
+ }
132
+ free(zHost);
133
+ popen2(blob_str(&zCmd), &sshIn, &sshOut, &sshPid);
134
+ if( sshPid==0 ){
135
+ socket_set_errmsg("cannot start ssh tunnel using [%b]", &zCmd);
136
+ }
137
+ blob_reset(&zCmd);
138
+ return sshPid==0;
277139
}
278140
279141
/*
280142
** Open a connection to the server. The server is defined by the following
281143
** global variables:
@@ -288,19 +150,12 @@
288150
*/
289151
int transport_open(void){
290152
int rc = 0;
291153
if( transport.isOpen==0 ){
292154
if( g.urlIsSsh ){
293
- Blob cmd;
294
- blob_zero(&cmd);
295
- shell_escape(&cmd, g.urlFossil);
296
- blob_append(&cmd, " test-http ", -1);
297
- shell_escape(&cmd, g.urlPath);
298
- fprintf(sshOut, "%s || true\n", blob_str(&cmd));
299
- fflush(sshOut);
300
- if( g.fSshTrace ) printf("Sent: [%s]\n", blob_str(&cmd));
301
- blob_reset(&cmd);
155
+ rc = transport_ssh_open();
156
+ if( rc==0 ) transport.isOpen = 1;
302157
}else if( g.urlIsHttps ){
303158
#ifdef FOSSIL_ENABLE_SSL
304159
rc = ssl_open();
305160
if( rc==0 ) transport.isOpen = 1;
306161
#else
@@ -340,11 +195,11 @@
340195
if( transport.pLog ){
341196
fclose(transport.pLog);
342197
transport.pLog = 0;
343198
}
344199
if( g.urlIsSsh ){
345
- /* No-op */
200
+ transport_ssh_close();
346201
}else if( g.urlIsHttps ){
347202
#ifdef FOSSIL_ENABLE_SSL
348203
ssl_close();
349204
#endif
350205
}else if( g.urlIsFile ){
@@ -399,13 +254,11 @@
399254
/*
400255
** This routine is called when the outbound message is complete and
401256
** it is time to being receiving a reply.
402257
*/
403258
void transport_flip(void){
404
- if( g.urlIsSsh ){
405
- fprintf(sshOut, "\n\n");
406
- }else if( g.urlIsFile ){
259
+ if( g.urlIsFile ){
407260
char *zCmd;
408261
fclose(transport.pFile);
409262
zCmd = mprintf("\"%s\" http \"%s\" \"%s\" \"%s\" 127.0.0.1 --localauth",
410263
g.nameOfExe, g.urlName, transport.zOutFile, transport.zInFile
411264
);
@@ -581,20 +434,32 @@
581434
}
582435
if( g.fSshTrace ) printf("Got line: [%s]\n", &transport.pBuf[iStart]);
583436
return &transport.pBuf[iStart];
584437
}
585438
439
+/*
440
+** Global transport shutdown
441
+*/
586442
void transport_global_shutdown(void){
587
- if( g.urlIsSsh && sshPid ){
588
- /*printf("Closing SSH tunnel: ");*/
589
- fflush(stdout);
590
- pclose2(sshIn, sshOut, sshPid);
591
- sshPid = 0;
443
+ if( g.urlIsSsh ){
444
+ transport_ssh_close();
592445
}
593446
if( g.urlIsHttps ){
594447
#ifdef FOSSIL_ENABLE_SSL
595448
ssl_global_shutdown();
596449
#endif
597450
}else{
598451
socket_global_shutdown();
599452
}
600453
}
454
+
455
+/*
456
+** Close SSH transport.
457
+*/
458
+void transport_ssh_close(void){
459
+ if( sshPid ){
460
+ /*printf("Closing SSH tunnel: ");*/
461
+ fflush(stdout);
462
+ pclose2(sshIn, sshOut, sshPid);
463
+ sshPid = 0;
464
+ }
465
+}
601466
--- src/http_transport.c
+++ src/http_transport.c
@@ -74,25 +74,10 @@
74 transport.nSent = 0;
75 transport.nRcvd = 0;
76 }
77 }
78
79 /*
80 ** Read text from sshIn. Zero-terminate and remove trailing
81 ** whitespace.
82 */
83 static void sshin_read(char *zBuf, int szBuf){
84 int got;
85 zBuf[0] = 0;
86 got = read(sshIn, zBuf, szBuf-1);
87 while( got>=0 ){
88 zBuf[got] = 0;
89 if( got==0 || !fossil_isspace(zBuf[got-1]) ) break;
90 got--;
91 }
92 }
93
94 /*
95 ** Default SSH command
96 */
97 #ifdef __MINGW32__
98 static char zDefaultSshCmd[] = "ssh -T";
@@ -99,183 +84,60 @@
99 #else
100 static char zDefaultSshCmd[] = "ssh -e none -T";
101 #endif
102
103 /*
104 ** Generate a random SSH link problem keyword
105 */
106 static int random_probe(char *zProbe, int nProbe){
107 unsigned r[4];
108 sqlite3_randomness(sizeof(r), r);
109 sqlite3_snprintf(nProbe, zProbe, "probe-%08x%08x%08x%08x",
110 r[0], r[1], r[2], r[3]);
111 return (int)strlen(zProbe);
112 }
113
114 /*
115 ** Bring up an SSH link. This involves sending some "echo" commands and
116 ** get back appropriate responses. The point is to move past the MOTD and
117 ** verify that the link is working.
118 */
119 static void transport_ssh_startup(void){
120 char *zIn; /* An input line received back from remote */
121 int nWait; /* Number of times waiting for the MOTD */
122 char zProbe[40]; /* Text of the random probe */
123 int nProbe; /* Size of probe message */
124 int nIn; /* Size of input */
125 static const int nBuf = 10000; /* Size of input buffer */
126
127 zIn = fossil_malloc(nBuf);
128 nProbe = random_probe(zProbe, sizeof(zProbe));
129 fprintf(sshOut, "echo %s\n", zProbe);
130 fflush(sshOut);
131 if( g.fSshTrace ){
132 printf("Sent: [echo %s]\n", zProbe);
133 fflush(stdout);
134 }
135 memset(zIn, '*', nProbe);
136 for(nWait=1; nWait<=10; nWait++){
137 sshin_read(zIn+nProbe, nBuf-nProbe);
138 if( g.fSshTrace ){
139 printf("Got back-----------------------------------------------\n"
140 "%s\n"
141 "-------------------------------------------------------\n",
142 zIn+nProbe);
143 }
144 if( strstr(zIn, zProbe) ) break;
145 sqlite3_sleep(100*nWait);
146 nIn = (int)strlen(zIn);
147 memcpy(zIn, zIn+(nIn-nProbe), nProbe);
148 if( g.fSshTrace ){
149 printf("Fetching more text. Looking for [%s]...\n", zProbe);
150 fflush(stdout);
151 }
152 }
153 nProbe = random_probe(zProbe, sizeof(zProbe));
154 fprintf(sshOut, "echo %s\n", zProbe);
155 fflush(sshOut);
156 if( g.fSshTrace ){
157 printf("Sent: [echo %s]\n", zProbe);
158 fflush(stdout);
159 }
160 sshin_read(zIn, nBuf);
161 if( zIn[0]==0 ){
162 sqlite3_sleep(250);
163 sshin_read(zIn, nBuf);
164 }
165 if( g.fSshTrace ){
166 printf("Got back-----------------------------------------------\n"
167 "%s\n"
168 "-------------------------------------------------------\n", zIn);
169 }
170 if( memcmp(zIn, zProbe, nProbe)!=0 ){
171 pclose2(sshIn, sshOut, sshPid);
172 fossil_fatal("ssh connection failed: [%s]", zIn);
173 }
174 fossil_free(zIn);
175 }
176
177 /*
178 ** Global initialization of the transport layer
179 */
180 void transport_global_startup(void){
181 if( g.urlIsSsh ){
182 /* Only SSH requires a global initialization. For SSH we need to create
183 ** and run an SSH command to talk to the remote machine.
184 */
185 const char *zSsh; /* The base SSH command */
186 Blob zCmd; /* The SSH command */
187 char *zHost; /* The host name to contact */
188 int n; /* Size of prefix string */
189
190 zSsh = db_get("ssh-command", zDefaultSshCmd);
191 blob_init(&zCmd, zSsh, -1);
192 if( g.urlPort!=g.urlDfltPort ){
193 #ifdef __MINGW32__
194 blob_appendf(&zCmd, " -P %d", g.urlPort);
195 #else
196 blob_appendf(&zCmd, " -p %d", g.urlPort);
197 #endif
198 }
199 fossil_force_newline();
200 fossil_print("%s", blob_str(&zCmd)); /* Show the base of the SSH command */
201 if( g.urlUser && g.urlUser[0] ){
202 zHost = mprintf("%s@%s", g.urlUser, g.urlName);
203 #ifdef __MINGW32__
204 /* Only win32 (and specifically PLINK.EXE) support the -pw option */
205 if( g.urlPasswd && g.urlPasswd[0] ){
206 Blob pw;
207 blob_zero(&pw);
208 if( g.urlPasswd[0]=='*' ){
209 char *zPrompt;
210 zPrompt = mprintf("Password for [%s]: ", zHost);
211 prompt_for_password(zPrompt, &pw, 0);
212 free(zPrompt);
213 }else{
214 blob_init(&pw, g.urlPasswd, -1);
215 }
216 blob_append(&zCmd, " -pw ", -1);
217 shell_escape(&zCmd, blob_str(&pw));
218 blob_reset(&pw);
219 fossil_print(" -pw ********"); /* Do not show the password text */
220 }
221 #endif
222 }else{
223 zHost = mprintf("%s", g.urlName);
224 }
225 n = blob_size(&zCmd);
226 blob_append(&zCmd, " ", 1);
227 shell_escape(&zCmd, zHost);
228 if( g.urlShell ){
229 blob_appendf(&zCmd, " %s", g.urlShell);
230 }else{
231 #if defined(FOSSIL_ENABLE_SSH_FAR_SIDE)
232 /* The following works. But only if the fossil on the remote side
233 ** is recent enough to support the test-ssh-far-side command. That
234 ** command was added on 2013-02-06. We will leave this turned off
235 ** until most fossil servers have upgraded to that version or a later
236 ** version. The sync will still work as long as the shell on the far
237 ** side is bash and not tcsh. And if the default far side shell is
238 ** tcsh, then the shell=/bin/bash query parameter can be used as a
239 ** work-around. Enable this code after about a year...
240 */
241 blob_appendf(&zCmd, " exec %s test-ssh-far-side", g.urlFossil);
242 #endif
243 }
244 fossil_print("%s\n", blob_str(&zCmd)+n); /* Show tail of SSH command */
245 free(zHost);
246 popen2(blob_str(&zCmd), &sshIn, &sshOut, &sshPid);
247 if( sshPid==0 ){
248 fossil_fatal("cannot start ssh tunnel using [%b]", &zCmd);
249 }
250 blob_reset(&zCmd);
251 transport_ssh_startup();
252 }
253 }
254
255 /*
256 ** COMMAND: test-ssh-far-side
257 **
258 ** Read lines of input text, one by one, and evaluate each line using
259 ** system(). The ssh: sync protocol uses this on the far side of the
260 ** SSH link.
261 */
262 void test_ssh_far_side_cmd(void){
263 int i = 0;
264 int got;
265 char zLine[5000];
266 while( i<sizeof(zLine) ){
267 got = read(0, zLine+i, 1);
268 if( got==0 ) return;
269 if( zLine[i]=='\n' ){
270 zLine[i] = 0;
271 system(zLine);
272 i = 0;
273 }else{
274 i++;
275 }
276 }
277 }
278
279 /*
280 ** Open a connection to the server. The server is defined by the following
281 ** global variables:
@@ -288,19 +150,12 @@
288 */
289 int transport_open(void){
290 int rc = 0;
291 if( transport.isOpen==0 ){
292 if( g.urlIsSsh ){
293 Blob cmd;
294 blob_zero(&cmd);
295 shell_escape(&cmd, g.urlFossil);
296 blob_append(&cmd, " test-http ", -1);
297 shell_escape(&cmd, g.urlPath);
298 fprintf(sshOut, "%s || true\n", blob_str(&cmd));
299 fflush(sshOut);
300 if( g.fSshTrace ) printf("Sent: [%s]\n", blob_str(&cmd));
301 blob_reset(&cmd);
302 }else if( g.urlIsHttps ){
303 #ifdef FOSSIL_ENABLE_SSL
304 rc = ssl_open();
305 if( rc==0 ) transport.isOpen = 1;
306 #else
@@ -340,11 +195,11 @@
340 if( transport.pLog ){
341 fclose(transport.pLog);
342 transport.pLog = 0;
343 }
344 if( g.urlIsSsh ){
345 /* No-op */
346 }else if( g.urlIsHttps ){
347 #ifdef FOSSIL_ENABLE_SSL
348 ssl_close();
349 #endif
350 }else if( g.urlIsFile ){
@@ -399,13 +254,11 @@
399 /*
400 ** This routine is called when the outbound message is complete and
401 ** it is time to being receiving a reply.
402 */
403 void transport_flip(void){
404 if( g.urlIsSsh ){
405 fprintf(sshOut, "\n\n");
406 }else if( g.urlIsFile ){
407 char *zCmd;
408 fclose(transport.pFile);
409 zCmd = mprintf("\"%s\" http \"%s\" \"%s\" \"%s\" 127.0.0.1 --localauth",
410 g.nameOfExe, g.urlName, transport.zOutFile, transport.zInFile
411 );
@@ -581,20 +434,32 @@
581 }
582 if( g.fSshTrace ) printf("Got line: [%s]\n", &transport.pBuf[iStart]);
583 return &transport.pBuf[iStart];
584 }
585
 
 
 
586 void transport_global_shutdown(void){
587 if( g.urlIsSsh && sshPid ){
588 /*printf("Closing SSH tunnel: ");*/
589 fflush(stdout);
590 pclose2(sshIn, sshOut, sshPid);
591 sshPid = 0;
592 }
593 if( g.urlIsHttps ){
594 #ifdef FOSSIL_ENABLE_SSL
595 ssl_global_shutdown();
596 #endif
597 }else{
598 socket_global_shutdown();
599 }
600 }
 
 
 
 
 
 
 
 
 
 
 
 
601
--- src/http_transport.c
+++ src/http_transport.c
@@ -74,25 +74,10 @@
74 transport.nSent = 0;
75 transport.nRcvd = 0;
76 }
77 }
78
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
79 /*
80 ** Default SSH command
81 */
82 #ifdef __MINGW32__
83 static char zDefaultSshCmd[] = "ssh -T";
@@ -99,183 +84,60 @@
84 #else
85 static char zDefaultSshCmd[] = "ssh -e none -T";
86 #endif
87
88 /*
89 ** SSH initialization of the transport layer
90 */
91 int transport_ssh_open(void){
92 /* For SSH we need to create and run SSH fossil http
93 ** to talk to the remote machine.
94 */
95 const char *zSsh; /* The base SSH command */
96 Blob zCmd; /* The SSH command */
97 char *zHost; /* The host name to contact */
98 int n; /* Size of prefix string */
99
100 socket_ssh_resolve_addr();
101 zSsh = db_get("ssh-command", zDefaultSshCmd);
102 blob_init(&zCmd, zSsh, -1);
103 if( g.urlPort!=g.urlDfltPort && g.urlPort ){
104 #ifdef __MINGW32__
105 blob_appendf(&zCmd, " -P %d", g.urlPort);
106 #else
107 blob_appendf(&zCmd, " -p %d", g.urlPort);
108 #endif
109 }
110 if( g.fSshTrace ){
111 fossil_force_newline();
112 fossil_print("%s", blob_str(&zCmd)); /* Show the base of the SSH command */
113 }
114 if( g.urlUser && g.urlUser[0] ){
115 zHost = mprintf("%s@%s", g.urlUser, g.urlName);
116 }else{
117 zHost = mprintf("%s", g.urlName);
118 }
119 n = blob_size(&zCmd);
120 blob_append(&zCmd, " ", 1);
121 shell_escape(&zCmd, zHost);
122 blob_append(&zCmd, " ", 1);
123 shell_escape(&zCmd, mprintf("%s", g.urlFossil));
124 blob_append(&zCmd, " test-http", 10);
125 if( g.urlPath && g.urlPath[0] ){
126 blob_append(&zCmd, " ", 1);
127 shell_escape(&zCmd, mprintf("%s", g.urlPath));
128 }
129 if( g.fSshTrace ){
130 fossil_print("%s\n", blob_str(&zCmd)+n); /* Show tail of SSH command */
131 }
132 free(zHost);
133 popen2(blob_str(&zCmd), &sshIn, &sshOut, &sshPid);
134 if( sshPid==0 ){
135 socket_set_errmsg("cannot start ssh tunnel using [%b]", &zCmd);
136 }
137 blob_reset(&zCmd);
138 return sshPid==0;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
139 }
140
141 /*
142 ** Open a connection to the server. The server is defined by the following
143 ** global variables:
@@ -288,19 +150,12 @@
150 */
151 int transport_open(void){
152 int rc = 0;
153 if( transport.isOpen==0 ){
154 if( g.urlIsSsh ){
155 rc = transport_ssh_open();
156 if( rc==0 ) transport.isOpen = 1;
 
 
 
 
 
 
 
157 }else if( g.urlIsHttps ){
158 #ifdef FOSSIL_ENABLE_SSL
159 rc = ssl_open();
160 if( rc==0 ) transport.isOpen = 1;
161 #else
@@ -340,11 +195,11 @@
195 if( transport.pLog ){
196 fclose(transport.pLog);
197 transport.pLog = 0;
198 }
199 if( g.urlIsSsh ){
200 transport_ssh_close();
201 }else if( g.urlIsHttps ){
202 #ifdef FOSSIL_ENABLE_SSL
203 ssl_close();
204 #endif
205 }else if( g.urlIsFile ){
@@ -399,13 +254,11 @@
254 /*
255 ** This routine is called when the outbound message is complete and
256 ** it is time to being receiving a reply.
257 */
258 void transport_flip(void){
259 if( g.urlIsFile ){
 
 
260 char *zCmd;
261 fclose(transport.pFile);
262 zCmd = mprintf("\"%s\" http \"%s\" \"%s\" \"%s\" 127.0.0.1 --localauth",
263 g.nameOfExe, g.urlName, transport.zOutFile, transport.zInFile
264 );
@@ -581,20 +434,32 @@
434 }
435 if( g.fSshTrace ) printf("Got line: [%s]\n", &transport.pBuf[iStart]);
436 return &transport.pBuf[iStart];
437 }
438
439 /*
440 ** Global transport shutdown
441 */
442 void transport_global_shutdown(void){
443 if( g.urlIsSsh ){
444 transport_ssh_close();
 
 
 
445 }
446 if( g.urlIsHttps ){
447 #ifdef FOSSIL_ENABLE_SSL
448 ssl_global_shutdown();
449 #endif
450 }else{
451 socket_global_shutdown();
452 }
453 }
454
455 /*
456 ** Close SSH transport.
457 */
458 void transport_ssh_close(void){
459 if( sshPid ){
460 /*printf("Closing SSH tunnel: ");*/
461 fflush(stdout);
462 pclose2(sshIn, sshOut, sshPid);
463 sshPid = 0;
464 }
465 }
466
+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
+38 -5
--- src/main.c
+++ src/main.c
@@ -138,10 +138,12 @@
138138
int fSqlPrint; /* True if -sqlprint flag is present */
139139
int fQuiet; /* True if -quiet flag is present */
140140
int fHttpTrace; /* Trace outbound HTTP requests */
141141
int fSystemTrace; /* Trace calls to fossil_system(), --systemtrace */
142142
int fSshTrace; /* Trace the SSH setup traffic */
143
+ int fSshClient; /* HTTP client flags for SSH client */
144
+ char *zSshCmd; /* SSH command string */
143145
int fNoSync; /* Do not do an autosync ever. --nosync */
144146
char *zPath; /* Name of webpage being served */
145147
char *zExtra; /* Extra path information past the webpage name */
146148
char *zBaseURL; /* Full text of the URL being served */
147149
char *zTop; /* Parent directory of zPath */
@@ -179,11 +181,10 @@
179181
char *urlUser; /* User id for http: */
180182
char *urlPasswd; /* Password for http: */
181183
char *urlCanonical; /* Canonical representation of the URL */
182184
char *urlProxyAuth; /* Proxy-Authorizer: string */
183185
char *urlFossil; /* The fossil query parameter on ssh: */
184
- char *urlShell; /* The shell query parameter on ssh: */
185186
unsigned urlFlags; /* Boolean flags controlling URL processing */
186187
187188
const char *zLogin; /* Login name. "" if not logged in. */
188189
const char *zSSLIdentity; /* Value of --ssl-identity option, filename of
189190
** SSL client identity */
@@ -603,10 +604,12 @@
603604
g.fQuiet = find_option("quiet", 0, 0)!=0;
604605
g.fSqlTrace = find_option("sqltrace", 0, 0)!=0;
605606
g.fSqlStats = find_option("sqlstats", 0, 0)!=0;
606607
g.fSystemTrace = find_option("systemtrace", 0, 0)!=0;
607608
g.fSshTrace = find_option("sshtrace", 0, 0)!=0;
609
+ g.fSshClient = 0;
610
+ g.zSshCmd = 0;
608611
if( g.fSqlTrace ) g.fSqlStats = 1;
609612
g.fSqlPrint = find_option("sqlprint", 0, 0)!=0;
610613
g.fHttpTrace = find_option("httptrace", 0, 0)!=0;
611614
g.zLogin = find_option("user", "U", 1);
612615
g.zSSLIdentity = find_option("ssl-identity", 0, 1);
@@ -1314,11 +1317,12 @@
13141317
}
13151318
13161319
/* Find the page that the user has requested, construct and deliver that
13171320
** page.
13181321
*/
1319
- if( g.zContentType && memcmp(g.zContentType, "application/x-fossil", 20)==0 ){
1322
+ if( g.zContentType &&
1323
+ strncmp(g.zContentType, "application/x-fossil", 20)==0 ){
13201324
zPathInfo = "/xfer";
13211325
}
13221326
set_base_url(0);
13231327
if( zPathInfo==0 || zPathInfo[0]==0
13241328
|| (zPathInfo[0]=='/' && zPathInfo[1]==0) ){
@@ -1731,39 +1735,68 @@
17311735
zIpAddr = g.argv[5];
17321736
}else{
17331737
g.httpIn = stdin;
17341738
g.httpOut = stdout;
17351739
zIpAddr = 0;
1740
+ }
1741
+ if( zIpAddr==0 ){
1742
+ zIpAddr = cgi_ssh_remote_addr(0);
1743
+ if( zIpAddr && zIpAddr[0] ){
1744
+ g.fSshClient |= CGI_SSH_CLIENT;
1745
+ }
17361746
}
17371747
find_server_repository(0);
17381748
g.zRepositoryName = enter_chroot_jail(g.zRepositoryName);
17391749
if( useSCGI ){
17401750
cgi_handle_scgi_request();
1751
+ }else if( g.fSshClient & CGI_SSH_CLIENT ){
1752
+ ssh_request_loop(zIpAddr, glob_create(zFileGlob));
17411753
}else{
17421754
cgi_handle_http_request(zIpAddr);
17431755
}
17441756
process_one_web_page(zNotFound, glob_create(zFileGlob));
17451757
}
1758
+
1759
+/*
1760
+** Process all requests in a single SSH connection if possible.
1761
+*/
1762
+void ssh_request_loop(const char *zIpAddr, Glob *FileGlob){
1763
+ do{
1764
+ cgi_handle_ssh_http_request(zIpAddr);
1765
+ process_one_web_page(0, FileGlob);
1766
+ blob_reset(&g.cgiIn);
1767
+ } while ( g.fSshClient & CGI_SSH_FOSSIL ||
1768
+ g.fSshClient & CGI_SSH_COMPAT );
1769
+}
17461770
17471771
/*
17481772
** Note that the following command is used by ssh:// processing.
17491773
**
17501774
** COMMAND: test-http
17511775
** Works like the http command but gives setup permission to all users.
1776
+**
17521777
*/
17531778
void cmd_test_http(void){
1779
+ const char *zIpAddr; /* IP address of remote client */
1780
+
17541781
Th_InitTraceLog();
17551782
login_set_capabilities("sx", 0);
17561783
g.useLocalauth = 1;
1757
- cgi_set_parameter("REMOTE_ADDR", "127.0.0.1");
17581784
g.httpIn = stdin;
17591785
g.httpOut = stdout;
17601786
find_server_repository(0);
17611787
g.cgiOutput = 1;
17621788
g.fullHttpReply = 1;
1763
- cgi_handle_http_request(0);
1764
- process_one_web_page(0, 0);
1789
+ zIpAddr = cgi_ssh_remote_addr(0);
1790
+ if( zIpAddr && zIpAddr[0] ){
1791
+ g.fSshClient |= CGI_SSH_CLIENT;
1792
+ ssh_request_loop(zIpAddr, 0);
1793
+ }else{
1794
+ cgi_set_parameter("REMOTE_ADDR", "127.0.0.1");
1795
+ cgi_handle_http_request(0);
1796
+ process_one_web_page(0, 0);
1797
+ }
17651798
}
17661799
17671800
#if !defined(_WIN32)
17681801
#if !defined(__DARWIN__) && !defined(__APPLE__) && !defined(__HAIKU__)
17691802
/*
17701803
--- src/main.c
+++ src/main.c
@@ -138,10 +138,12 @@
138 int fSqlPrint; /* True if -sqlprint flag is present */
139 int fQuiet; /* True if -quiet flag is present */
140 int fHttpTrace; /* Trace outbound HTTP requests */
141 int fSystemTrace; /* Trace calls to fossil_system(), --systemtrace */
142 int fSshTrace; /* Trace the SSH setup traffic */
 
 
143 int fNoSync; /* Do not do an autosync ever. --nosync */
144 char *zPath; /* Name of webpage being served */
145 char *zExtra; /* Extra path information past the webpage name */
146 char *zBaseURL; /* Full text of the URL being served */
147 char *zTop; /* Parent directory of zPath */
@@ -179,11 +181,10 @@
179 char *urlUser; /* User id for http: */
180 char *urlPasswd; /* Password for http: */
181 char *urlCanonical; /* Canonical representation of the URL */
182 char *urlProxyAuth; /* Proxy-Authorizer: string */
183 char *urlFossil; /* The fossil query parameter on ssh: */
184 char *urlShell; /* The shell query parameter on ssh: */
185 unsigned urlFlags; /* Boolean flags controlling URL processing */
186
187 const char *zLogin; /* Login name. "" if not logged in. */
188 const char *zSSLIdentity; /* Value of --ssl-identity option, filename of
189 ** SSL client identity */
@@ -603,10 +604,12 @@
603 g.fQuiet = find_option("quiet", 0, 0)!=0;
604 g.fSqlTrace = find_option("sqltrace", 0, 0)!=0;
605 g.fSqlStats = find_option("sqlstats", 0, 0)!=0;
606 g.fSystemTrace = find_option("systemtrace", 0, 0)!=0;
607 g.fSshTrace = find_option("sshtrace", 0, 0)!=0;
 
 
608 if( g.fSqlTrace ) g.fSqlStats = 1;
609 g.fSqlPrint = find_option("sqlprint", 0, 0)!=0;
610 g.fHttpTrace = find_option("httptrace", 0, 0)!=0;
611 g.zLogin = find_option("user", "U", 1);
612 g.zSSLIdentity = find_option("ssl-identity", 0, 1);
@@ -1314,11 +1317,12 @@
1314 }
1315
1316 /* Find the page that the user has requested, construct and deliver that
1317 ** page.
1318 */
1319 if( g.zContentType && memcmp(g.zContentType, "application/x-fossil", 20)==0 ){
 
1320 zPathInfo = "/xfer";
1321 }
1322 set_base_url(0);
1323 if( zPathInfo==0 || zPathInfo[0]==0
1324 || (zPathInfo[0]=='/' && zPathInfo[1]==0) ){
@@ -1731,39 +1735,68 @@
1731 zIpAddr = g.argv[5];
1732 }else{
1733 g.httpIn = stdin;
1734 g.httpOut = stdout;
1735 zIpAddr = 0;
 
 
 
 
 
 
1736 }
1737 find_server_repository(0);
1738 g.zRepositoryName = enter_chroot_jail(g.zRepositoryName);
1739 if( useSCGI ){
1740 cgi_handle_scgi_request();
 
 
1741 }else{
1742 cgi_handle_http_request(zIpAddr);
1743 }
1744 process_one_web_page(zNotFound, glob_create(zFileGlob));
1745 }
 
 
 
 
 
 
 
 
 
 
 
 
1746
1747 /*
1748 ** Note that the following command is used by ssh:// processing.
1749 **
1750 ** COMMAND: test-http
1751 ** Works like the http command but gives setup permission to all users.
 
1752 */
1753 void cmd_test_http(void){
 
 
1754 Th_InitTraceLog();
1755 login_set_capabilities("sx", 0);
1756 g.useLocalauth = 1;
1757 cgi_set_parameter("REMOTE_ADDR", "127.0.0.1");
1758 g.httpIn = stdin;
1759 g.httpOut = stdout;
1760 find_server_repository(0);
1761 g.cgiOutput = 1;
1762 g.fullHttpReply = 1;
1763 cgi_handle_http_request(0);
1764 process_one_web_page(0, 0);
 
 
 
 
 
 
 
1765 }
1766
1767 #if !defined(_WIN32)
1768 #if !defined(__DARWIN__) && !defined(__APPLE__) && !defined(__HAIKU__)
1769 /*
1770
--- src/main.c
+++ src/main.c
@@ -138,10 +138,12 @@
138 int fSqlPrint; /* True if -sqlprint flag is present */
139 int fQuiet; /* True if -quiet flag is present */
140 int fHttpTrace; /* Trace outbound HTTP requests */
141 int fSystemTrace; /* Trace calls to fossil_system(), --systemtrace */
142 int fSshTrace; /* Trace the SSH setup traffic */
143 int fSshClient; /* HTTP client flags for SSH client */
144 char *zSshCmd; /* SSH command string */
145 int fNoSync; /* Do not do an autosync ever. --nosync */
146 char *zPath; /* Name of webpage being served */
147 char *zExtra; /* Extra path information past the webpage name */
148 char *zBaseURL; /* Full text of the URL being served */
149 char *zTop; /* Parent directory of zPath */
@@ -179,11 +181,10 @@
181 char *urlUser; /* User id for http: */
182 char *urlPasswd; /* Password for http: */
183 char *urlCanonical; /* Canonical representation of the URL */
184 char *urlProxyAuth; /* Proxy-Authorizer: string */
185 char *urlFossil; /* The fossil query parameter on ssh: */
 
186 unsigned urlFlags; /* Boolean flags controlling URL processing */
187
188 const char *zLogin; /* Login name. "" if not logged in. */
189 const char *zSSLIdentity; /* Value of --ssl-identity option, filename of
190 ** SSL client identity */
@@ -603,10 +604,12 @@
604 g.fQuiet = find_option("quiet", 0, 0)!=0;
605 g.fSqlTrace = find_option("sqltrace", 0, 0)!=0;
606 g.fSqlStats = find_option("sqlstats", 0, 0)!=0;
607 g.fSystemTrace = find_option("systemtrace", 0, 0)!=0;
608 g.fSshTrace = find_option("sshtrace", 0, 0)!=0;
609 g.fSshClient = 0;
610 g.zSshCmd = 0;
611 if( g.fSqlTrace ) g.fSqlStats = 1;
612 g.fSqlPrint = find_option("sqlprint", 0, 0)!=0;
613 g.fHttpTrace = find_option("httptrace", 0, 0)!=0;
614 g.zLogin = find_option("user", "U", 1);
615 g.zSSLIdentity = find_option("ssl-identity", 0, 1);
@@ -1314,11 +1317,12 @@
1317 }
1318
1319 /* Find the page that the user has requested, construct and deliver that
1320 ** page.
1321 */
1322 if( g.zContentType &&
1323 strncmp(g.zContentType, "application/x-fossil", 20)==0 ){
1324 zPathInfo = "/xfer";
1325 }
1326 set_base_url(0);
1327 if( zPathInfo==0 || zPathInfo[0]==0
1328 || (zPathInfo[0]=='/' && zPathInfo[1]==0) ){
@@ -1731,39 +1735,68 @@
1735 zIpAddr = g.argv[5];
1736 }else{
1737 g.httpIn = stdin;
1738 g.httpOut = stdout;
1739 zIpAddr = 0;
1740 }
1741 if( zIpAddr==0 ){
1742 zIpAddr = cgi_ssh_remote_addr(0);
1743 if( zIpAddr && zIpAddr[0] ){
1744 g.fSshClient |= CGI_SSH_CLIENT;
1745 }
1746 }
1747 find_server_repository(0);
1748 g.zRepositoryName = enter_chroot_jail(g.zRepositoryName);
1749 if( useSCGI ){
1750 cgi_handle_scgi_request();
1751 }else if( g.fSshClient & CGI_SSH_CLIENT ){
1752 ssh_request_loop(zIpAddr, glob_create(zFileGlob));
1753 }else{
1754 cgi_handle_http_request(zIpAddr);
1755 }
1756 process_one_web_page(zNotFound, glob_create(zFileGlob));
1757 }
1758
1759 /*
1760 ** Process all requests in a single SSH connection if possible.
1761 */
1762 void ssh_request_loop(const char *zIpAddr, Glob *FileGlob){
1763 do{
1764 cgi_handle_ssh_http_request(zIpAddr);
1765 process_one_web_page(0, FileGlob);
1766 blob_reset(&g.cgiIn);
1767 } while ( g.fSshClient & CGI_SSH_FOSSIL ||
1768 g.fSshClient & CGI_SSH_COMPAT );
1769 }
1770
1771 /*
1772 ** Note that the following command is used by ssh:// processing.
1773 **
1774 ** COMMAND: test-http
1775 ** Works like the http command but gives setup permission to all users.
1776 **
1777 */
1778 void cmd_test_http(void){
1779 const char *zIpAddr; /* IP address of remote client */
1780
1781 Th_InitTraceLog();
1782 login_set_capabilities("sx", 0);
1783 g.useLocalauth = 1;
 
1784 g.httpIn = stdin;
1785 g.httpOut = stdout;
1786 find_server_repository(0);
1787 g.cgiOutput = 1;
1788 g.fullHttpReply = 1;
1789 zIpAddr = cgi_ssh_remote_addr(0);
1790 if( zIpAddr && zIpAddr[0] ){
1791 g.fSshClient |= CGI_SSH_CLIENT;
1792 ssh_request_loop(zIpAddr, 0);
1793 }else{
1794 cgi_set_parameter("REMOTE_ADDR", "127.0.0.1");
1795 cgi_handle_http_request(0);
1796 process_one_web_page(0, 0);
1797 }
1798 }
1799
1800 #if !defined(_WIN32)
1801 #if !defined(__DARWIN__) && !defined(__APPLE__) && !defined(__HAIKU__)
1802 /*
1803
--- src/popen.c
+++ src/popen.c
@@ -27,10 +27,13 @@
2727
** Print a fatal error and quit.
2828
*/
2929
static void win32_fatal_error(const char *zMsg){
3030
fossil_fatal("%s", zMsg);
3131
}
32
+#else
33
+#include <signal.h>
34
+#include <sys/wait.h>
3235
#endif
3336
3437
/*
3538
** The following macros are used to cast pointers to integers and
3639
** integers to pointers. The way you do this varies from one compiler
@@ -171,10 +174,11 @@
171174
close(pout[0]);
172175
close(pout[1]);
173176
*pChildPid = 0;
174177
return 1;
175178
}
179
+ signal(SIGPIPE,SIG_IGN);
176180
if( *pChildPid==0 ){
177181
int fd;
178182
int nErr = 0;
179183
/* This is the child process */
180184
close(0);
@@ -211,7 +215,8 @@
211215
fclose(pOut);
212216
#else
213217
close(fdIn);
214218
fclose(pOut);
215219
kill(childPid, SIGINT);
220
+ while( waitpid(0, 0, WNOHANG)>0 ) {}
216221
#endif
217222
}
218223
--- src/popen.c
+++ src/popen.c
@@ -27,10 +27,13 @@
27 ** Print a fatal error and quit.
28 */
29 static void win32_fatal_error(const char *zMsg){
30 fossil_fatal("%s", zMsg);
31 }
 
 
 
32 #endif
33
34 /*
35 ** The following macros are used to cast pointers to integers and
36 ** integers to pointers. The way you do this varies from one compiler
@@ -171,10 +174,11 @@
171 close(pout[0]);
172 close(pout[1]);
173 *pChildPid = 0;
174 return 1;
175 }
 
176 if( *pChildPid==0 ){
177 int fd;
178 int nErr = 0;
179 /* This is the child process */
180 close(0);
@@ -211,7 +215,8 @@
211 fclose(pOut);
212 #else
213 close(fdIn);
214 fclose(pOut);
215 kill(childPid, SIGINT);
 
216 #endif
217 }
218
--- src/popen.c
+++ src/popen.c
@@ -27,10 +27,13 @@
27 ** Print a fatal error and quit.
28 */
29 static void win32_fatal_error(const char *zMsg){
30 fossil_fatal("%s", zMsg);
31 }
32 #else
33 #include <signal.h>
34 #include <sys/wait.h>
35 #endif
36
37 /*
38 ** The following macros are used to cast pointers to integers and
39 ** integers to pointers. The way you do this varies from one compiler
@@ -171,10 +174,11 @@
174 close(pout[0]);
175 close(pout[1]);
176 *pChildPid = 0;
177 return 1;
178 }
179 signal(SIGPIPE,SIG_IGN);
180 if( *pChildPid==0 ){
181 int fd;
182 int nErr = 0;
183 /* This is the child process */
184 close(0);
@@ -211,7 +215,8 @@
215 fclose(pOut);
216 #else
217 close(fdIn);
218 fclose(pOut);
219 kill(childPid, SIGINT);
220 while( waitpid(0, 0, WNOHANG)>0 ) {}
221 #endif
222 }
223
+6
--- src/sync.c
+++ src/sync.c
@@ -51,10 +51,12 @@
5151
}
5252
url_parse(0, URL_REMEMBER);
5353
if( g.urlProtocol==0 ) return 0;
5454
if( g.urlUser!=0 && g.urlPasswd==0 ){
5555
g.urlPasswd = unobscure(db_get("last-sync-pw", 0));
56
+ g.urlFlags |= URL_PROMPT_PW;
57
+ url_prompt_for_password();
5658
}
5759
#if 0 /* Disabled for now */
5860
if( (flags & AUTOSYNC_PULL)!=0 && db_get_boolean("auto-shun",1) ){
5961
/* When doing an automatic pull, also automatically pull shuns from
6062
** the server if pull_shuns is enabled.
@@ -101,16 +103,20 @@
101103
*/
102104
if( find_option("verily",0,0)!=0 ){
103105
*pSyncFlags |= SYNC_RESYNC;
104106
}
105107
url_proxy_options();
108
+ clone_ssh_find_options();
106109
db_find_and_open_repository(0, 0);
107110
db_open_config(0);
108111
if( g.argc==2 ){
109112
if( db_get_boolean("auto-shun",1) ) configSync = CONFIGSET_SHUN;
110113
}else if( g.argc==3 ){
111114
zUrl = g.argv[2];
115
+ }
116
+ if( urlFlags & URL_REMEMBER ){
117
+ clone_ssh_db_set_options();
112118
}
113119
url_parse(zUrl, urlFlags);
114120
if( g.urlProtocol==0 ){
115121
if( urlOptional ) fossil_exit(0);
116122
usage("URL");
117123
--- src/sync.c
+++ src/sync.c
@@ -51,10 +51,12 @@
51 }
52 url_parse(0, URL_REMEMBER);
53 if( g.urlProtocol==0 ) return 0;
54 if( g.urlUser!=0 && g.urlPasswd==0 ){
55 g.urlPasswd = unobscure(db_get("last-sync-pw", 0));
 
 
56 }
57 #if 0 /* Disabled for now */
58 if( (flags & AUTOSYNC_PULL)!=0 && db_get_boolean("auto-shun",1) ){
59 /* When doing an automatic pull, also automatically pull shuns from
60 ** the server if pull_shuns is enabled.
@@ -101,16 +103,20 @@
101 */
102 if( find_option("verily",0,0)!=0 ){
103 *pSyncFlags |= SYNC_RESYNC;
104 }
105 url_proxy_options();
 
106 db_find_and_open_repository(0, 0);
107 db_open_config(0);
108 if( g.argc==2 ){
109 if( db_get_boolean("auto-shun",1) ) configSync = CONFIGSET_SHUN;
110 }else if( g.argc==3 ){
111 zUrl = g.argv[2];
 
 
 
112 }
113 url_parse(zUrl, urlFlags);
114 if( g.urlProtocol==0 ){
115 if( urlOptional ) fossil_exit(0);
116 usage("URL");
117
--- src/sync.c
+++ src/sync.c
@@ -51,10 +51,12 @@
51 }
52 url_parse(0, URL_REMEMBER);
53 if( g.urlProtocol==0 ) return 0;
54 if( g.urlUser!=0 && g.urlPasswd==0 ){
55 g.urlPasswd = unobscure(db_get("last-sync-pw", 0));
56 g.urlFlags |= URL_PROMPT_PW;
57 url_prompt_for_password();
58 }
59 #if 0 /* Disabled for now */
60 if( (flags & AUTOSYNC_PULL)!=0 && db_get_boolean("auto-shun",1) ){
61 /* When doing an automatic pull, also automatically pull shuns from
62 ** the server if pull_shuns is enabled.
@@ -101,16 +103,20 @@
103 */
104 if( find_option("verily",0,0)!=0 ){
105 *pSyncFlags |= SYNC_RESYNC;
106 }
107 url_proxy_options();
108 clone_ssh_find_options();
109 db_find_and_open_repository(0, 0);
110 db_open_config(0);
111 if( g.argc==2 ){
112 if( db_get_boolean("auto-shun",1) ) configSync = CONFIGSET_SHUN;
113 }else if( g.argc==3 ){
114 zUrl = g.argv[2];
115 }
116 if( urlFlags & URL_REMEMBER ){
117 clone_ssh_db_set_options();
118 }
119 url_parse(zUrl, urlFlags);
120 if( g.urlProtocol==0 ){
121 if( urlOptional ) fossil_exit(0);
122 usage("URL");
123
+2 -20
--- src/url.c
+++ src/url.c
@@ -100,11 +100,10 @@
100100
}else if( zUrl[0]=='s' ){
101101
g.urlIsSsh = 1;
102102
g.urlProtocol = "ssh";
103103
g.urlDfltPort = 22;
104104
g.urlFossil = "fossil";
105
- g.urlShell = 0;
106105
iStart = 6;
107106
}else{
108107
g.urlIsHttps = 0;
109108
g.urlProtocol = "http";
110109
g.urlDfltPort = 80;
@@ -173,16 +172,10 @@
173172
g.urlFossil = zValue;
174173
dehttpize(g.urlFossil);
175174
zExe = mprintf("%cfossil=%T", cQuerySep, g.urlFossil);
176175
cQuerySep = '&';
177176
}
178
- if( fossil_strcmp(zName,"shell")==0 ){
179
- g.urlShell = zValue;
180
- dehttpize(g.urlShell);
181
- zExe = mprintf("%cshell=%T", cQuerySep, g.urlFossil);
182
- cQuerySep = '&';
183
- }
184177
}
185178
186179
dehttpize(g.urlPath);
187180
if( g.urlDfltPort==g.urlPort ){
188181
g.urlCanonical = mprintf(
@@ -444,26 +437,16 @@
444437
if( g.urlIsSsh || g.urlIsFile ) return;
445438
if( isatty(fileno(stdin))
446439
&& (g.urlFlags & URL_PROMPT_PW)!=0
447440
&& (g.urlFlags & URL_PROMPTED)==0
448441
){
449
- char *zPrompt = mprintf("\rpassword for %s: ", g.urlUser);
450
- Blob x;
451
- fossil_force_newline();
452
- prompt_for_password(zPrompt, &x, 0);
453
- free(zPrompt);
454
- g.urlPasswd = mprintf("%b", &x);
455
- blob_reset(&x);
456442
g.urlFlags |= URL_PROMPTED;
443
+ g.urlPasswd = prompt_for_user_password(g.urlUser);
457444
if( g.urlPasswd[0]
458445
&& (g.urlFlags & (URL_REMEMBER|URL_ASK_REMEMBER_PW))!=0
459446
){
460
- char c;
461
- prompt_user("remember password (Y/n)? ", &x);
462
- c = blob_str(&x)[0];
463
- blob_reset(&x);
464
- if( c!='n' && c!='N' ){
447
+ if( save_password_prompt() ){
465448
g.urlFlags |= URL_REMEMBER_PW;
466449
if( g.urlFlags & URL_REMEMBER ){
467450
db_set("last-sync-pw", obscure(g.urlPasswd), 0);
468451
}
469452
}
@@ -490,10 +473,9 @@
490473
*/
491474
void url_get_password_if_needed(void){
492475
if( (g.urlUser && g.urlUser[0])
493476
&& (g.urlPasswd==0 || g.urlPasswd[0]==0)
494477
&& isatty(fileno(stdin))
495
- && g.urlIsSsh==0
496478
){
497479
url_prompt_for_password();
498480
}
499481
}
500482
--- src/url.c
+++ src/url.c
@@ -100,11 +100,10 @@
100 }else if( zUrl[0]=='s' ){
101 g.urlIsSsh = 1;
102 g.urlProtocol = "ssh";
103 g.urlDfltPort = 22;
104 g.urlFossil = "fossil";
105 g.urlShell = 0;
106 iStart = 6;
107 }else{
108 g.urlIsHttps = 0;
109 g.urlProtocol = "http";
110 g.urlDfltPort = 80;
@@ -173,16 +172,10 @@
173 g.urlFossil = zValue;
174 dehttpize(g.urlFossil);
175 zExe = mprintf("%cfossil=%T", cQuerySep, g.urlFossil);
176 cQuerySep = '&';
177 }
178 if( fossil_strcmp(zName,"shell")==0 ){
179 g.urlShell = zValue;
180 dehttpize(g.urlShell);
181 zExe = mprintf("%cshell=%T", cQuerySep, g.urlFossil);
182 cQuerySep = '&';
183 }
184 }
185
186 dehttpize(g.urlPath);
187 if( g.urlDfltPort==g.urlPort ){
188 g.urlCanonical = mprintf(
@@ -444,26 +437,16 @@
444 if( g.urlIsSsh || g.urlIsFile ) return;
445 if( isatty(fileno(stdin))
446 && (g.urlFlags & URL_PROMPT_PW)!=0
447 && (g.urlFlags & URL_PROMPTED)==0
448 ){
449 char *zPrompt = mprintf("\rpassword for %s: ", g.urlUser);
450 Blob x;
451 fossil_force_newline();
452 prompt_for_password(zPrompt, &x, 0);
453 free(zPrompt);
454 g.urlPasswd = mprintf("%b", &x);
455 blob_reset(&x);
456 g.urlFlags |= URL_PROMPTED;
 
457 if( g.urlPasswd[0]
458 && (g.urlFlags & (URL_REMEMBER|URL_ASK_REMEMBER_PW))!=0
459 ){
460 char c;
461 prompt_user("remember password (Y/n)? ", &x);
462 c = blob_str(&x)[0];
463 blob_reset(&x);
464 if( c!='n' && c!='N' ){
465 g.urlFlags |= URL_REMEMBER_PW;
466 if( g.urlFlags & URL_REMEMBER ){
467 db_set("last-sync-pw", obscure(g.urlPasswd), 0);
468 }
469 }
@@ -490,10 +473,9 @@
490 */
491 void url_get_password_if_needed(void){
492 if( (g.urlUser && g.urlUser[0])
493 && (g.urlPasswd==0 || g.urlPasswd[0]==0)
494 && isatty(fileno(stdin))
495 && g.urlIsSsh==0
496 ){
497 url_prompt_for_password();
498 }
499 }
500
--- src/url.c
+++ src/url.c
@@ -100,11 +100,10 @@
100 }else if( zUrl[0]=='s' ){
101 g.urlIsSsh = 1;
102 g.urlProtocol = "ssh";
103 g.urlDfltPort = 22;
104 g.urlFossil = "fossil";
 
105 iStart = 6;
106 }else{
107 g.urlIsHttps = 0;
108 g.urlProtocol = "http";
109 g.urlDfltPort = 80;
@@ -173,16 +172,10 @@
172 g.urlFossil = zValue;
173 dehttpize(g.urlFossil);
174 zExe = mprintf("%cfossil=%T", cQuerySep, g.urlFossil);
175 cQuerySep = '&';
176 }
 
 
 
 
 
 
177 }
178
179 dehttpize(g.urlPath);
180 if( g.urlDfltPort==g.urlPort ){
181 g.urlCanonical = mprintf(
@@ -444,26 +437,16 @@
437 if( g.urlIsSsh || g.urlIsFile ) return;
438 if( isatty(fileno(stdin))
439 && (g.urlFlags & URL_PROMPT_PW)!=0
440 && (g.urlFlags & URL_PROMPTED)==0
441 ){
 
 
 
 
 
 
 
442 g.urlFlags |= URL_PROMPTED;
443 g.urlPasswd = prompt_for_user_password(g.urlUser);
444 if( g.urlPasswd[0]
445 && (g.urlFlags & (URL_REMEMBER|URL_ASK_REMEMBER_PW))!=0
446 ){
447 if( save_password_prompt() ){
 
 
 
 
448 g.urlFlags |= URL_REMEMBER_PW;
449 if( g.urlFlags & URL_REMEMBER ){
450 db_set("last-sync-pw", obscure(g.urlPasswd), 0);
451 }
452 }
@@ -490,10 +473,9 @@
473 */
474 void url_get_password_if_needed(void){
475 if( (g.urlUser && g.urlUser[0])
476 && (g.urlPasswd==0 || g.urlPasswd[0]==0)
477 && isatty(fileno(stdin))
 
478 ){
479 url_prompt_for_password();
480 }
481 }
482
+27
--- src/user.c
+++ src/user.c
@@ -128,10 +128,37 @@
128128
break;
129129
}
130130
}
131131
blob_reset(&secondTry);
132132
}
133
+
134
+/*
135
+** Prompt to save Fossil user password
136
+*/
137
+int save_password_prompt(){
138
+ Blob x;
139
+ char c;
140
+ prompt_user("remember password (Y/n)? ", &x);
141
+ c = blob_str(&x)[0];
142
+ blob_reset(&x);
143
+ return ( c!='n' && c!='N' );
144
+}
145
+
146
+/*
147
+** Prompt for Fossil user password
148
+*/
149
+char *prompt_for_user_password(const char *zUser){
150
+ char *zPrompt = mprintf("\rpassword for %s: ", zUser);
151
+ char *zPw;
152
+ Blob x;
153
+ fossil_force_newline();
154
+ prompt_for_password(zPrompt, &x, 0);
155
+ free(zPrompt);
156
+ zPw = mprintf("%b", &x);
157
+ blob_reset(&x);
158
+ return zPw;
159
+}
133160
134161
/*
135162
** Prompt the user to enter a single line of text.
136163
*/
137164
void prompt_user(const char *zPrompt, Blob *pIn){
138165
--- src/user.c
+++ src/user.c
@@ -128,10 +128,37 @@
128 break;
129 }
130 }
131 blob_reset(&secondTry);
132 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
133
134 /*
135 ** Prompt the user to enter a single line of text.
136 */
137 void prompt_user(const char *zPrompt, Blob *pIn){
138
--- src/user.c
+++ src/user.c
@@ -128,10 +128,37 @@
128 break;
129 }
130 }
131 blob_reset(&secondTry);
132 }
133
134 /*
135 ** Prompt to save Fossil user password
136 */
137 int save_password_prompt(){
138 Blob x;
139 char c;
140 prompt_user("remember password (Y/n)? ", &x);
141 c = blob_str(&x)[0];
142 blob_reset(&x);
143 return ( c!='n' && c!='N' );
144 }
145
146 /*
147 ** Prompt for Fossil user password
148 */
149 char *prompt_for_user_password(const char *zUser){
150 char *zPrompt = mprintf("\rpassword for %s: ", zUser);
151 char *zPw;
152 Blob x;
153 fossil_force_newline();
154 prompt_for_password(zPrompt, &x, 0);
155 free(zPrompt);
156 zPw = mprintf("%b", &x);
157 blob_reset(&x);
158 return zPw;
159 }
160
161 /*
162 ** Prompt the user to enter a single line of text.
163 */
164 void prompt_user(const char *zPrompt, Blob *pIn){
165
+18 -11
--- 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
*/
@@ -1425,11 +1427,10 @@
14251427
nCardSent++;
14261428
if( (syncFlags & SYNC_PULL)==0 ) zOpType = "Push";
14271429
if( (syncFlags & SYNC_RESYNC)!=0 ) xfer.resync = 0x7fffffff;
14281430
}
14291431
manifest_crosslink_begin();
1430
- transport_global_startup();
14311432
if( syncFlags & SYNC_VERBOSE ){
14321433
fossil_print(zLabelFormat, "", "Bytes", "Cards", "Artifacts", "Deltas");
14331434
}
14341435
14351436
while( go ){
@@ -1504,11 +1505,22 @@
15041505
*/
15051506
zRandomness = db_text(0, "SELECT hex(randomblob(20))");
15061507
blob_appendf(&send, "# %s\n", zRandomness);
15071508
free(zRandomness);
15081509
1510
+ if( syncFlags & SYNC_VERBOSE ){
1511
+ fossil_print("waiting for server...");
1512
+ }
1513
+ fflush(stdout);
15091514
/* Exchange messages with the server */
1515
+ if( http_exchange(&send, &recv, (syncFlags & SYNC_CLONE)==0 || nCycle>0,
1516
+ MAX_REDIRECTS) ){
1517
+ nErr++;
1518
+ break;
1519
+ }
1520
+
1521
+ /* Output current stats */
15101522
if( syncFlags & SYNC_VERBOSE ){
15111523
fossil_print(zValueFormat, "Sent:",
15121524
blob_size(&send), nCardSent+xfer.nGimmeSent+xfer.nIGotSent,
15131525
xfer.nFileSent, xfer.nDeltaSent);
15141526
}else{
@@ -1520,19 +1532,11 @@
15201532
nCardRcvd = 0;
15211533
xfer.nFileSent = 0;
15221534
xfer.nDeltaSent = 0;
15231535
xfer.nGimmeSent = 0;
15241536
xfer.nIGotSent = 0;
1525
- if( syncFlags & SYNC_VERBOSE ){
1526
- fossil_print("waiting for server...");
1527
- }
1528
- fflush(stdout);
1529
- if( http_exchange(&send, &recv, (syncFlags & SYNC_CLONE)==0 || nCycle>0,
1530
- MAX_REDIRECTS) ){
1531
- nErr++;
1532
- break;
1533
- }
1537
+
15341538
lastPctDone = -1;
15351539
blob_reset(&send);
15361540
rArrivalTime = db_double(0.0, "SELECT julianday('now')");
15371541
15381542
/* Send the send-private pragma if we are trying to sync private data */
@@ -1775,11 +1779,14 @@
17751779
fossil_print("Error: %s\n", zMsg);
17761780
if( fossil_strcmp(zMsg, "login failed")==0 ){
17771781
if( nCycle<2 ){
17781782
g.urlPasswd = 0;
17791783
go = 1;
1780
- if( g.cgiOutput==0 ) url_prompt_for_password();
1784
+ if( g.cgiOutput==0 ){
1785
+ g.urlFlags |= URL_PROMPT_PW;
1786
+ url_prompt_for_password();
1787
+ }
17811788
}
17821789
}else{
17831790
blob_appendf(&xfer.err, "server says: %s\n", zMsg);
17841791
nErr++;
17851792
}
17861793
--- 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 */
@@ -1425,11 +1427,10 @@
1425 nCardSent++;
1426 if( (syncFlags & SYNC_PULL)==0 ) zOpType = "Push";
1427 if( (syncFlags & SYNC_RESYNC)!=0 ) xfer.resync = 0x7fffffff;
1428 }
1429 manifest_crosslink_begin();
1430 transport_global_startup();
1431 if( syncFlags & SYNC_VERBOSE ){
1432 fossil_print(zLabelFormat, "", "Bytes", "Cards", "Artifacts", "Deltas");
1433 }
1434
1435 while( go ){
@@ -1504,11 +1505,22 @@
1504 */
1505 zRandomness = db_text(0, "SELECT hex(randomblob(20))");
1506 blob_appendf(&send, "# %s\n", zRandomness);
1507 free(zRandomness);
1508
 
 
 
 
1509 /* Exchange messages with the server */
 
 
 
 
 
 
 
1510 if( syncFlags & SYNC_VERBOSE ){
1511 fossil_print(zValueFormat, "Sent:",
1512 blob_size(&send), nCardSent+xfer.nGimmeSent+xfer.nIGotSent,
1513 xfer.nFileSent, xfer.nDeltaSent);
1514 }else{
@@ -1520,19 +1532,11 @@
1520 nCardRcvd = 0;
1521 xfer.nFileSent = 0;
1522 xfer.nDeltaSent = 0;
1523 xfer.nGimmeSent = 0;
1524 xfer.nIGotSent = 0;
1525 if( syncFlags & SYNC_VERBOSE ){
1526 fossil_print("waiting for server...");
1527 }
1528 fflush(stdout);
1529 if( http_exchange(&send, &recv, (syncFlags & SYNC_CLONE)==0 || nCycle>0,
1530 MAX_REDIRECTS) ){
1531 nErr++;
1532 break;
1533 }
1534 lastPctDone = -1;
1535 blob_reset(&send);
1536 rArrivalTime = db_double(0.0, "SELECT julianday('now')");
1537
1538 /* Send the send-private pragma if we are trying to sync private data */
@@ -1775,11 +1779,14 @@
1775 fossil_print("Error: %s\n", zMsg);
1776 if( fossil_strcmp(zMsg, "login failed")==0 ){
1777 if( nCycle<2 ){
1778 g.urlPasswd = 0;
1779 go = 1;
1780 if( g.cgiOutput==0 ) url_prompt_for_password();
 
 
 
1781 }
1782 }else{
1783 blob_appendf(&xfer.err, "server says: %s\n", zMsg);
1784 nErr++;
1785 }
1786
--- 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 */
@@ -1425,11 +1427,10 @@
1427 nCardSent++;
1428 if( (syncFlags & SYNC_PULL)==0 ) zOpType = "Push";
1429 if( (syncFlags & SYNC_RESYNC)!=0 ) xfer.resync = 0x7fffffff;
1430 }
1431 manifest_crosslink_begin();
 
1432 if( syncFlags & SYNC_VERBOSE ){
1433 fossil_print(zLabelFormat, "", "Bytes", "Cards", "Artifacts", "Deltas");
1434 }
1435
1436 while( go ){
@@ -1504,11 +1505,22 @@
1505 */
1506 zRandomness = db_text(0, "SELECT hex(randomblob(20))");
1507 blob_appendf(&send, "# %s\n", zRandomness);
1508 free(zRandomness);
1509
1510 if( syncFlags & SYNC_VERBOSE ){
1511 fossil_print("waiting for server...");
1512 }
1513 fflush(stdout);
1514 /* Exchange messages with the server */
1515 if( http_exchange(&send, &recv, (syncFlags & SYNC_CLONE)==0 || nCycle>0,
1516 MAX_REDIRECTS) ){
1517 nErr++;
1518 break;
1519 }
1520
1521 /* Output current stats */
1522 if( syncFlags & SYNC_VERBOSE ){
1523 fossil_print(zValueFormat, "Sent:",
1524 blob_size(&send), nCardSent+xfer.nGimmeSent+xfer.nIGotSent,
1525 xfer.nFileSent, xfer.nDeltaSent);
1526 }else{
@@ -1520,19 +1532,11 @@
1532 nCardRcvd = 0;
1533 xfer.nFileSent = 0;
1534 xfer.nDeltaSent = 0;
1535 xfer.nGimmeSent = 0;
1536 xfer.nIGotSent = 0;
1537
 
 
 
 
 
 
 
 
1538 lastPctDone = -1;
1539 blob_reset(&send);
1540 rArrivalTime = db_double(0.0, "SELECT julianday('now')");
1541
1542 /* Send the send-private pragma if we are trying to sync private data */
@@ -1775,11 +1779,14 @@
1779 fossil_print("Error: %s\n", zMsg);
1780 if( fossil_strcmp(zMsg, "login failed")==0 ){
1781 if( nCycle<2 ){
1782 g.urlPasswd = 0;
1783 go = 1;
1784 if( g.cgiOutput==0 ){
1785 g.urlFlags |= URL_PROMPT_PW;
1786 url_prompt_for_password();
1787 }
1788 }
1789 }else{
1790 blob_appendf(&xfer.err, "server says: %s\n", zMsg);
1791 nErr++;
1792 }
1793

Keyboard Shortcuts

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