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.
Commit
dbb5e2d32a8c482e6fccfff89cda87081cc2d594
Parent
760f80cd8f6df3e…
13 files changed
+249
+249
+25
+14
-1
+17
+71
-206
+2
-1
+38
-5
+5
+6
+2
-20
+27
+18
-11
+249
| --- src/cgi.c | ||
| +++ src/cgi.c | ||
| @@ -60,10 +60,17 @@ | ||
| 60 | 60 | ** Destinations for output text. |
| 61 | 61 | */ |
| 62 | 62 | #define CGI_HEADER 0 |
| 63 | 63 | #define CGI_BODY 1 |
| 64 | 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 | + | |
| 65 | 72 | #endif /* INTERFACE */ |
| 66 | 73 | |
| 67 | 74 | /* |
| 68 | 75 | ** The HTTP reply is generated in two pieces: the header and the body. |
| 69 | 76 | ** These pieces are generated separately because they are not necessary |
| @@ -1302,10 +1309,234 @@ | ||
| 1302 | 1309 | } |
| 1303 | 1310 | } |
| 1304 | 1311 | cgi_init(); |
| 1305 | 1312 | cgi_trace(0); |
| 1306 | 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 | +} | |
| 1307 | 1538 | |
| 1308 | 1539 | /* |
| 1309 | 1540 | ** This routine handles a single SCGI request which is coming in on |
| 1310 | 1541 | ** g.httpIn and which replies on g.httpOut |
| 1311 | 1542 | ** |
| @@ -1600,5 +1831,23 @@ | ||
| 1600 | 1831 | cgi_set_status(304,"Not Modified"); |
| 1601 | 1832 | cgi_reset_content(); |
| 1602 | 1833 | cgi_reply(); |
| 1603 | 1834 | fossil_exit(0); |
| 1604 | 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 | +} | |
| 1605 | 1854 |
| --- 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 @@ | ||
| 60 | 60 | ** Destinations for output text. |
| 61 | 61 | */ |
| 62 | 62 | #define CGI_HEADER 0 |
| 63 | 63 | #define CGI_BODY 1 |
| 64 | 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 | + | |
| 65 | 72 | #endif /* INTERFACE */ |
| 66 | 73 | |
| 67 | 74 | /* |
| 68 | 75 | ** The HTTP reply is generated in two pieces: the header and the body. |
| 69 | 76 | ** These pieces are generated separately because they are not necessary |
| @@ -1302,10 +1309,234 @@ | ||
| 1302 | 1309 | } |
| 1303 | 1310 | } |
| 1304 | 1311 | cgi_init(); |
| 1305 | 1312 | cgi_trace(0); |
| 1306 | 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 | +} | |
| 1307 | 1538 | |
| 1308 | 1539 | /* |
| 1309 | 1540 | ** This routine handles a single SCGI request which is coming in on |
| 1310 | 1541 | ** g.httpIn and which replies on g.httpOut |
| 1311 | 1542 | ** |
| @@ -1600,5 +1831,23 @@ | ||
| 1600 | 1831 | cgi_set_status(304,"Not Modified"); |
| 1601 | 1832 | cgi_reset_content(); |
| 1602 | 1833 | cgi_reply(); |
| 1603 | 1834 | fossil_exit(0); |
| 1604 | 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 | +} | |
| 1605 | 1854 |
| --- 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 @@ | ||
| 107 | 107 | ** |
| 108 | 108 | ** Options: |
| 109 | 109 | ** --admin-user|-A USERNAME Make USERNAME the administrator |
| 110 | 110 | ** --private Also clone private branches |
| 111 | 111 | ** --ssl-identity=filename Use the SSL identity if requested by the server |
| 112 | +** --ssh-command|-c 'command' Use this SSH command | |
| 112 | 113 | ** |
| 113 | 114 | ** See also: init |
| 114 | 115 | */ |
| 115 | 116 | void clone_cmd(void){ |
| 116 | 117 | char *zPassword; |
| @@ -117,10 +118,11 @@ | ||
| 117 | 118 | const char *zDefaultUser; /* Optional name of the default user */ |
| 118 | 119 | int nErr = 0; |
| 119 | 120 | int bPrivate = 0; /* Also clone private branches */ |
| 120 | 121 | |
| 121 | 122 | if( find_option("private",0,0)!=0 ) bPrivate = SYNC_PRIVATE; |
| 123 | + clone_ssh_find_options(); | |
| 122 | 124 | url_proxy_options(); |
| 123 | 125 | if( g.argc < 4 ){ |
| 124 | 126 | usage("?OPTIONS? FILE-OR-URL NEW-REPOSITORY"); |
| 125 | 127 | } |
| 126 | 128 | db_open_config(0); |
| @@ -167,10 +169,11 @@ | ||
| 167 | 169 | db_multi_exec( |
| 168 | 170 | "REPLACE INTO config(name,value,mtime)" |
| 169 | 171 | " VALUES('server-code', lower(hex(randomblob(20))), now());" |
| 170 | 172 | ); |
| 171 | 173 | url_enable_proxy(0); |
| 174 | + clone_ssh_db_set_options(); | |
| 172 | 175 | url_get_password_if_needed(); |
| 173 | 176 | g.xlinkClusterOnly = 1; |
| 174 | 177 | nErr = client_sync(SYNC_CLONE | bPrivate,CONFIGSET_ALL,0); |
| 175 | 178 | g.xlinkClusterOnly = 0; |
| 176 | 179 | verify_cancel(); |
| @@ -188,5 +191,27 @@ | ||
| 188 | 191 | fossil_print("project-id: %s\n", db_get("project-code", 0)); |
| 189 | 192 | zPassword = db_text(0, "SELECT pw FROM user WHERE login=%Q", g.zLogin); |
| 190 | 193 | fossil_print("admin-user: %s (password is \"%s\")\n", g.zLogin, zPassword); |
| 191 | 194 | db_end_transaction(0); |
| 192 | 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 | +} | |
| 193 | 218 |
| --- 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 @@ | ||
| 112 | 112 | fossil_free(zCredentials); |
| 113 | 113 | } |
| 114 | 114 | blob_appendf(pHdr, "Host: %s\r\n", g.urlHostname); |
| 115 | 115 | blob_appendf(pHdr, "User-Agent: Fossil/" RELEASE_VERSION |
| 116 | 116 | " (" MANIFEST_DATE " " MANIFEST_VERSION ")\r\n"); |
| 117 | + if( g.urlIsSsh ) blob_appendf(pHdr, "X-Fossil-Transport: SSH\r\n"); | |
| 117 | 118 | if( g.fHttpTrace ){ |
| 118 | 119 | blob_appendf(pHdr, "Content-Type: application/x-fossil-debug\r\n"); |
| 119 | 120 | }else{ |
| 120 | 121 | blob_appendf(pHdr, "Content-Type: application/x-fossil\r\n"); |
| 121 | 122 | } |
| @@ -217,10 +218,20 @@ | ||
| 217 | 218 | if( iHttpVersion==0 ){ |
| 218 | 219 | closeConnection = 1; |
| 219 | 220 | }else{ |
| 220 | 221 | closeConnection = 0; |
| 221 | 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; | |
| 222 | 233 | }else if( fossil_strnicmp(zLine, "content-length:", 15)==0 ){ |
| 223 | 234 | for(i=15; fossil_isspace(zLine[i]); i++){} |
| 224 | 235 | iLength = atoi(&zLine[i]); |
| 225 | 236 | }else if( fossil_strnicmp(zLine, "connection:", 11)==0 ){ |
| 226 | 237 | char c; |
| @@ -295,12 +306,14 @@ | ||
| 295 | 306 | ** Close the connection to the server if appropriate. |
| 296 | 307 | ** |
| 297 | 308 | ** FIXME: There is some bug in the lower layers that prevents the |
| 298 | 309 | ** connection from remaining open. The easiest fix for now is to |
| 299 | 310 | ** simply close and restart the connection for each round-trip. |
| 311 | + ** | |
| 312 | + ** For SSH we will leave the connection open. | |
| 300 | 313 | */ |
| 301 | - closeConnection = 1; /* FIX ME */ | |
| 314 | + if( ! g.urlIsSsh ) closeConnection = 1; /* FIX ME */ | |
| 302 | 315 | if( closeConnection ){ |
| 303 | 316 | transport_close(); |
| 304 | 317 | }else{ |
| 305 | 318 | transport_rewind(); |
| 306 | 319 | } |
| 307 | 320 |
| --- 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 |
+17
| --- src/http_socket.c | ||
| +++ src/http_socket.c | ||
| @@ -209,5 +209,22 @@ | ||
| 209 | 209 | N -= (size_t)got; |
| 210 | 210 | pContent = (void*)&((char*)pContent)[got]; |
| 211 | 211 | } |
| 212 | 212 | return total; |
| 213 | 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 | +} | |
| 214 | 231 |
| --- 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 |
+71
-206
| --- src/http_transport.c | ||
| +++ src/http_transport.c | ||
| @@ -74,25 +74,10 @@ | ||
| 74 | 74 | transport.nSent = 0; |
| 75 | 75 | transport.nRcvd = 0; |
| 76 | 76 | } |
| 77 | 77 | } |
| 78 | 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 | 79 | /* |
| 95 | 80 | ** Default SSH command |
| 96 | 81 | */ |
| 97 | 82 | #ifdef __MINGW32__ |
| 98 | 83 | static char zDefaultSshCmd[] = "ssh -T"; |
| @@ -99,183 +84,60 @@ | ||
| 99 | 84 | #else |
| 100 | 85 | static char zDefaultSshCmd[] = "ssh -e none -T"; |
| 101 | 86 | #endif |
| 102 | 87 | |
| 103 | 88 | /* |
| 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; | |
| 277 | 139 | } |
| 278 | 140 | |
| 279 | 141 | /* |
| 280 | 142 | ** Open a connection to the server. The server is defined by the following |
| 281 | 143 | ** global variables: |
| @@ -288,19 +150,12 @@ | ||
| 288 | 150 | */ |
| 289 | 151 | int transport_open(void){ |
| 290 | 152 | int rc = 0; |
| 291 | 153 | if( transport.isOpen==0 ){ |
| 292 | 154 | 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; | |
| 302 | 157 | }else if( g.urlIsHttps ){ |
| 303 | 158 | #ifdef FOSSIL_ENABLE_SSL |
| 304 | 159 | rc = ssl_open(); |
| 305 | 160 | if( rc==0 ) transport.isOpen = 1; |
| 306 | 161 | #else |
| @@ -340,11 +195,11 @@ | ||
| 340 | 195 | if( transport.pLog ){ |
| 341 | 196 | fclose(transport.pLog); |
| 342 | 197 | transport.pLog = 0; |
| 343 | 198 | } |
| 344 | 199 | if( g.urlIsSsh ){ |
| 345 | - /* No-op */ | |
| 200 | + transport_ssh_close(); | |
| 346 | 201 | }else if( g.urlIsHttps ){ |
| 347 | 202 | #ifdef FOSSIL_ENABLE_SSL |
| 348 | 203 | ssl_close(); |
| 349 | 204 | #endif |
| 350 | 205 | }else if( g.urlIsFile ){ |
| @@ -399,13 +254,11 @@ | ||
| 399 | 254 | /* |
| 400 | 255 | ** This routine is called when the outbound message is complete and |
| 401 | 256 | ** it is time to being receiving a reply. |
| 402 | 257 | */ |
| 403 | 258 | void transport_flip(void){ |
| 404 | - if( g.urlIsSsh ){ | |
| 405 | - fprintf(sshOut, "\n\n"); | |
| 406 | - }else if( g.urlIsFile ){ | |
| 259 | + if( g.urlIsFile ){ | |
| 407 | 260 | char *zCmd; |
| 408 | 261 | fclose(transport.pFile); |
| 409 | 262 | zCmd = mprintf("\"%s\" http \"%s\" \"%s\" \"%s\" 127.0.0.1 --localauth", |
| 410 | 263 | g.nameOfExe, g.urlName, transport.zOutFile, transport.zInFile |
| 411 | 264 | ); |
| @@ -581,20 +434,32 @@ | ||
| 581 | 434 | } |
| 582 | 435 | if( g.fSshTrace ) printf("Got line: [%s]\n", &transport.pBuf[iStart]); |
| 583 | 436 | return &transport.pBuf[iStart]; |
| 584 | 437 | } |
| 585 | 438 | |
| 439 | +/* | |
| 440 | +** Global transport shutdown | |
| 441 | +*/ | |
| 586 | 442 | 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(); | |
| 592 | 445 | } |
| 593 | 446 | if( g.urlIsHttps ){ |
| 594 | 447 | #ifdef FOSSIL_ENABLE_SSL |
| 595 | 448 | ssl_global_shutdown(); |
| 596 | 449 | #endif |
| 597 | 450 | }else{ |
| 598 | 451 | socket_global_shutdown(); |
| 599 | 452 | } |
| 600 | 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 | +} | |
| 601 | 466 |
| --- 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 @@ | ||
| 793 | 793 | ** |
| 794 | 794 | ** This feature allows the "fossil ui" command to give the user |
| 795 | 795 | ** full access rights without having to log in. |
| 796 | 796 | */ |
| 797 | 797 | 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 ) | |
| 799 | 800 | && g.useLocalauth |
| 800 | 801 | && db_get_int("localauth",0)==0 |
| 801 | 802 | && P("HTTPS")==0 |
| 802 | 803 | ){ |
| 803 | 804 | uid = db_int(0, "SELECT uid FROM user WHERE cap LIKE '%%s%%'"); |
| 804 | 805 |
| --- 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 @@ | ||
| 138 | 138 | int fSqlPrint; /* True if -sqlprint flag is present */ |
| 139 | 139 | int fQuiet; /* True if -quiet flag is present */ |
| 140 | 140 | int fHttpTrace; /* Trace outbound HTTP requests */ |
| 141 | 141 | int fSystemTrace; /* Trace calls to fossil_system(), --systemtrace */ |
| 142 | 142 | int fSshTrace; /* Trace the SSH setup traffic */ |
| 143 | + int fSshClient; /* HTTP client flags for SSH client */ | |
| 144 | + char *zSshCmd; /* SSH command string */ | |
| 143 | 145 | int fNoSync; /* Do not do an autosync ever. --nosync */ |
| 144 | 146 | char *zPath; /* Name of webpage being served */ |
| 145 | 147 | char *zExtra; /* Extra path information past the webpage name */ |
| 146 | 148 | char *zBaseURL; /* Full text of the URL being served */ |
| 147 | 149 | char *zTop; /* Parent directory of zPath */ |
| @@ -179,11 +181,10 @@ | ||
| 179 | 181 | char *urlUser; /* User id for http: */ |
| 180 | 182 | char *urlPasswd; /* Password for http: */ |
| 181 | 183 | char *urlCanonical; /* Canonical representation of the URL */ |
| 182 | 184 | char *urlProxyAuth; /* Proxy-Authorizer: string */ |
| 183 | 185 | char *urlFossil; /* The fossil query parameter on ssh: */ |
| 184 | - char *urlShell; /* The shell query parameter on ssh: */ | |
| 185 | 186 | unsigned urlFlags; /* Boolean flags controlling URL processing */ |
| 186 | 187 | |
| 187 | 188 | const char *zLogin; /* Login name. "" if not logged in. */ |
| 188 | 189 | const char *zSSLIdentity; /* Value of --ssl-identity option, filename of |
| 189 | 190 | ** SSL client identity */ |
| @@ -603,10 +604,12 @@ | ||
| 603 | 604 | g.fQuiet = find_option("quiet", 0, 0)!=0; |
| 604 | 605 | g.fSqlTrace = find_option("sqltrace", 0, 0)!=0; |
| 605 | 606 | g.fSqlStats = find_option("sqlstats", 0, 0)!=0; |
| 606 | 607 | g.fSystemTrace = find_option("systemtrace", 0, 0)!=0; |
| 607 | 608 | g.fSshTrace = find_option("sshtrace", 0, 0)!=0; |
| 609 | + g.fSshClient = 0; | |
| 610 | + g.zSshCmd = 0; | |
| 608 | 611 | if( g.fSqlTrace ) g.fSqlStats = 1; |
| 609 | 612 | g.fSqlPrint = find_option("sqlprint", 0, 0)!=0; |
| 610 | 613 | g.fHttpTrace = find_option("httptrace", 0, 0)!=0; |
| 611 | 614 | g.zLogin = find_option("user", "U", 1); |
| 612 | 615 | g.zSSLIdentity = find_option("ssl-identity", 0, 1); |
| @@ -1314,11 +1317,12 @@ | ||
| 1314 | 1317 | } |
| 1315 | 1318 | |
| 1316 | 1319 | /* Find the page that the user has requested, construct and deliver that |
| 1317 | 1320 | ** page. |
| 1318 | 1321 | */ |
| 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 ){ | |
| 1320 | 1324 | zPathInfo = "/xfer"; |
| 1321 | 1325 | } |
| 1322 | 1326 | set_base_url(0); |
| 1323 | 1327 | if( zPathInfo==0 || zPathInfo[0]==0 |
| 1324 | 1328 | || (zPathInfo[0]=='/' && zPathInfo[1]==0) ){ |
| @@ -1731,39 +1735,68 @@ | ||
| 1731 | 1735 | zIpAddr = g.argv[5]; |
| 1732 | 1736 | }else{ |
| 1733 | 1737 | g.httpIn = stdin; |
| 1734 | 1738 | g.httpOut = stdout; |
| 1735 | 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 | + } | |
| 1736 | 1746 | } |
| 1737 | 1747 | find_server_repository(0); |
| 1738 | 1748 | g.zRepositoryName = enter_chroot_jail(g.zRepositoryName); |
| 1739 | 1749 | if( useSCGI ){ |
| 1740 | 1750 | cgi_handle_scgi_request(); |
| 1751 | + }else if( g.fSshClient & CGI_SSH_CLIENT ){ | |
| 1752 | + ssh_request_loop(zIpAddr, glob_create(zFileGlob)); | |
| 1741 | 1753 | }else{ |
| 1742 | 1754 | cgi_handle_http_request(zIpAddr); |
| 1743 | 1755 | } |
| 1744 | 1756 | process_one_web_page(zNotFound, glob_create(zFileGlob)); |
| 1745 | 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 | +} | |
| 1746 | 1770 | |
| 1747 | 1771 | /* |
| 1748 | 1772 | ** Note that the following command is used by ssh:// processing. |
| 1749 | 1773 | ** |
| 1750 | 1774 | ** COMMAND: test-http |
| 1751 | 1775 | ** Works like the http command but gives setup permission to all users. |
| 1776 | +** | |
| 1752 | 1777 | */ |
| 1753 | 1778 | void cmd_test_http(void){ |
| 1779 | + const char *zIpAddr; /* IP address of remote client */ | |
| 1780 | + | |
| 1754 | 1781 | Th_InitTraceLog(); |
| 1755 | 1782 | login_set_capabilities("sx", 0); |
| 1756 | 1783 | g.useLocalauth = 1; |
| 1757 | - cgi_set_parameter("REMOTE_ADDR", "127.0.0.1"); | |
| 1758 | 1784 | g.httpIn = stdin; |
| 1759 | 1785 | g.httpOut = stdout; |
| 1760 | 1786 | find_server_repository(0); |
| 1761 | 1787 | g.cgiOutput = 1; |
| 1762 | 1788 | 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 | + } | |
| 1765 | 1798 | } |
| 1766 | 1799 | |
| 1767 | 1800 | #if !defined(_WIN32) |
| 1768 | 1801 | #if !defined(__DARWIN__) && !defined(__APPLE__) && !defined(__HAIKU__) |
| 1769 | 1802 | /* |
| 1770 | 1803 |
| --- 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 |
+5
| --- src/popen.c | ||
| +++ src/popen.c | ||
| @@ -27,10 +27,13 @@ | ||
| 27 | 27 | ** Print a fatal error and quit. |
| 28 | 28 | */ |
| 29 | 29 | static void win32_fatal_error(const char *zMsg){ |
| 30 | 30 | fossil_fatal("%s", zMsg); |
| 31 | 31 | } |
| 32 | +#else | |
| 33 | +#include <signal.h> | |
| 34 | +#include <sys/wait.h> | |
| 32 | 35 | #endif |
| 33 | 36 | |
| 34 | 37 | /* |
| 35 | 38 | ** The following macros are used to cast pointers to integers and |
| 36 | 39 | ** integers to pointers. The way you do this varies from one compiler |
| @@ -171,10 +174,11 @@ | ||
| 171 | 174 | close(pout[0]); |
| 172 | 175 | close(pout[1]); |
| 173 | 176 | *pChildPid = 0; |
| 174 | 177 | return 1; |
| 175 | 178 | } |
| 179 | + signal(SIGPIPE,SIG_IGN); | |
| 176 | 180 | if( *pChildPid==0 ){ |
| 177 | 181 | int fd; |
| 178 | 182 | int nErr = 0; |
| 179 | 183 | /* This is the child process */ |
| 180 | 184 | close(0); |
| @@ -211,7 +215,8 @@ | ||
| 211 | 215 | fclose(pOut); |
| 212 | 216 | #else |
| 213 | 217 | close(fdIn); |
| 214 | 218 | fclose(pOut); |
| 215 | 219 | kill(childPid, SIGINT); |
| 220 | + while( waitpid(0, 0, WNOHANG)>0 ) {} | |
| 216 | 221 | #endif |
| 217 | 222 | } |
| 218 | 223 |
| --- 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 @@ | ||
| 51 | 51 | } |
| 52 | 52 | url_parse(0, URL_REMEMBER); |
| 53 | 53 | if( g.urlProtocol==0 ) return 0; |
| 54 | 54 | if( g.urlUser!=0 && g.urlPasswd==0 ){ |
| 55 | 55 | g.urlPasswd = unobscure(db_get("last-sync-pw", 0)); |
| 56 | + g.urlFlags |= URL_PROMPT_PW; | |
| 57 | + url_prompt_for_password(); | |
| 56 | 58 | } |
| 57 | 59 | #if 0 /* Disabled for now */ |
| 58 | 60 | if( (flags & AUTOSYNC_PULL)!=0 && db_get_boolean("auto-shun",1) ){ |
| 59 | 61 | /* When doing an automatic pull, also automatically pull shuns from |
| 60 | 62 | ** the server if pull_shuns is enabled. |
| @@ -101,16 +103,20 @@ | ||
| 101 | 103 | */ |
| 102 | 104 | if( find_option("verily",0,0)!=0 ){ |
| 103 | 105 | *pSyncFlags |= SYNC_RESYNC; |
| 104 | 106 | } |
| 105 | 107 | url_proxy_options(); |
| 108 | + clone_ssh_find_options(); | |
| 106 | 109 | db_find_and_open_repository(0, 0); |
| 107 | 110 | db_open_config(0); |
| 108 | 111 | if( g.argc==2 ){ |
| 109 | 112 | if( db_get_boolean("auto-shun",1) ) configSync = CONFIGSET_SHUN; |
| 110 | 113 | }else if( g.argc==3 ){ |
| 111 | 114 | zUrl = g.argv[2]; |
| 115 | + } | |
| 116 | + if( urlFlags & URL_REMEMBER ){ | |
| 117 | + clone_ssh_db_set_options(); | |
| 112 | 118 | } |
| 113 | 119 | url_parse(zUrl, urlFlags); |
| 114 | 120 | if( g.urlProtocol==0 ){ |
| 115 | 121 | if( urlOptional ) fossil_exit(0); |
| 116 | 122 | usage("URL"); |
| 117 | 123 |
| --- 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 @@ | ||
| 100 | 100 | }else if( zUrl[0]=='s' ){ |
| 101 | 101 | g.urlIsSsh = 1; |
| 102 | 102 | g.urlProtocol = "ssh"; |
| 103 | 103 | g.urlDfltPort = 22; |
| 104 | 104 | g.urlFossil = "fossil"; |
| 105 | - g.urlShell = 0; | |
| 106 | 105 | iStart = 6; |
| 107 | 106 | }else{ |
| 108 | 107 | g.urlIsHttps = 0; |
| 109 | 108 | g.urlProtocol = "http"; |
| 110 | 109 | g.urlDfltPort = 80; |
| @@ -173,16 +172,10 @@ | ||
| 173 | 172 | g.urlFossil = zValue; |
| 174 | 173 | dehttpize(g.urlFossil); |
| 175 | 174 | zExe = mprintf("%cfossil=%T", cQuerySep, g.urlFossil); |
| 176 | 175 | cQuerySep = '&'; |
| 177 | 176 | } |
| 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 | 177 | } |
| 185 | 178 | |
| 186 | 179 | dehttpize(g.urlPath); |
| 187 | 180 | if( g.urlDfltPort==g.urlPort ){ |
| 188 | 181 | g.urlCanonical = mprintf( |
| @@ -444,26 +437,16 @@ | ||
| 444 | 437 | if( g.urlIsSsh || g.urlIsFile ) return; |
| 445 | 438 | if( isatty(fileno(stdin)) |
| 446 | 439 | && (g.urlFlags & URL_PROMPT_PW)!=0 |
| 447 | 440 | && (g.urlFlags & URL_PROMPTED)==0 |
| 448 | 441 | ){ |
| 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 | 442 | g.urlFlags |= URL_PROMPTED; |
| 443 | + g.urlPasswd = prompt_for_user_password(g.urlUser); | |
| 457 | 444 | if( g.urlPasswd[0] |
| 458 | 445 | && (g.urlFlags & (URL_REMEMBER|URL_ASK_REMEMBER_PW))!=0 |
| 459 | 446 | ){ |
| 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() ){ | |
| 465 | 448 | g.urlFlags |= URL_REMEMBER_PW; |
| 466 | 449 | if( g.urlFlags & URL_REMEMBER ){ |
| 467 | 450 | db_set("last-sync-pw", obscure(g.urlPasswd), 0); |
| 468 | 451 | } |
| 469 | 452 | } |
| @@ -490,10 +473,9 @@ | ||
| 490 | 473 | */ |
| 491 | 474 | void url_get_password_if_needed(void){ |
| 492 | 475 | if( (g.urlUser && g.urlUser[0]) |
| 493 | 476 | && (g.urlPasswd==0 || g.urlPasswd[0]==0) |
| 494 | 477 | && isatty(fileno(stdin)) |
| 495 | - && g.urlIsSsh==0 | |
| 496 | 478 | ){ |
| 497 | 479 | url_prompt_for_password(); |
| 498 | 480 | } |
| 499 | 481 | } |
| 500 | 482 |
| --- 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 @@ | ||
| 128 | 128 | break; |
| 129 | 129 | } |
| 130 | 130 | } |
| 131 | 131 | blob_reset(&secondTry); |
| 132 | 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 | +} | |
| 133 | 160 | |
| 134 | 161 | /* |
| 135 | 162 | ** Prompt the user to enter a single line of text. |
| 136 | 163 | */ |
| 137 | 164 | void prompt_user(const char *zPrompt, Blob *pIn){ |
| 138 | 165 |
| --- 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 @@ | ||
| 1228 | 1228 | { |
| 1229 | 1229 | cgi_reset_content(); |
| 1230 | 1230 | @ error bad\scommand:\s%F(blob_str(&xfer.line)) |
| 1231 | 1231 | } |
| 1232 | 1232 | blobarray_reset(xfer.aToken, xfer.nToken); |
| 1233 | + blob_reset(&xfer.line); | |
| 1233 | 1234 | } |
| 1234 | 1235 | if( isPush ){ |
| 1235 | 1236 | if( run_push_script()==TH_ERROR ){ |
| 1236 | 1237 | cgi_reset_content(); |
| 1237 | 1238 | @ error push\sscript\sfailed:\s%F(Th_GetResult(g.interp, 0)) |
| @@ -1255,10 +1256,11 @@ | ||
| 1255 | 1256 | if( xfer.syncPrivate ) send_private(&xfer); |
| 1256 | 1257 | } |
| 1257 | 1258 | if( recvConfig ){ |
| 1258 | 1259 | configure_finalize_receive(); |
| 1259 | 1260 | } |
| 1261 | + db_multi_exec("DROP TABLE onremote"); | |
| 1260 | 1262 | manifest_crosslink_end(); |
| 1261 | 1263 | |
| 1262 | 1264 | /* Send the server timestamp last, in case prior processing happened |
| 1263 | 1265 | ** to use up a significant fraction of our time window. |
| 1264 | 1266 | */ |
| @@ -1425,11 +1427,10 @@ | ||
| 1425 | 1427 | nCardSent++; |
| 1426 | 1428 | if( (syncFlags & SYNC_PULL)==0 ) zOpType = "Push"; |
| 1427 | 1429 | if( (syncFlags & SYNC_RESYNC)!=0 ) xfer.resync = 0x7fffffff; |
| 1428 | 1430 | } |
| 1429 | 1431 | manifest_crosslink_begin(); |
| 1430 | - transport_global_startup(); | |
| 1431 | 1432 | if( syncFlags & SYNC_VERBOSE ){ |
| 1432 | 1433 | fossil_print(zLabelFormat, "", "Bytes", "Cards", "Artifacts", "Deltas"); |
| 1433 | 1434 | } |
| 1434 | 1435 | |
| 1435 | 1436 | while( go ){ |
| @@ -1504,11 +1505,22 @@ | ||
| 1504 | 1505 | */ |
| 1505 | 1506 | zRandomness = db_text(0, "SELECT hex(randomblob(20))"); |
| 1506 | 1507 | blob_appendf(&send, "# %s\n", zRandomness); |
| 1507 | 1508 | free(zRandomness); |
| 1508 | 1509 | |
| 1510 | + if( syncFlags & SYNC_VERBOSE ){ | |
| 1511 | + fossil_print("waiting for server..."); | |
| 1512 | + } | |
| 1513 | + fflush(stdout); | |
| 1509 | 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 */ | |
| 1510 | 1522 | if( syncFlags & SYNC_VERBOSE ){ |
| 1511 | 1523 | fossil_print(zValueFormat, "Sent:", |
| 1512 | 1524 | blob_size(&send), nCardSent+xfer.nGimmeSent+xfer.nIGotSent, |
| 1513 | 1525 | xfer.nFileSent, xfer.nDeltaSent); |
| 1514 | 1526 | }else{ |
| @@ -1520,19 +1532,11 @@ | ||
| 1520 | 1532 | nCardRcvd = 0; |
| 1521 | 1533 | xfer.nFileSent = 0; |
| 1522 | 1534 | xfer.nDeltaSent = 0; |
| 1523 | 1535 | xfer.nGimmeSent = 0; |
| 1524 | 1536 | 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 | + | |
| 1534 | 1538 | lastPctDone = -1; |
| 1535 | 1539 | blob_reset(&send); |
| 1536 | 1540 | rArrivalTime = db_double(0.0, "SELECT julianday('now')"); |
| 1537 | 1541 | |
| 1538 | 1542 | /* Send the send-private pragma if we are trying to sync private data */ |
| @@ -1775,11 +1779,14 @@ | ||
| 1775 | 1779 | fossil_print("Error: %s\n", zMsg); |
| 1776 | 1780 | if( fossil_strcmp(zMsg, "login failed")==0 ){ |
| 1777 | 1781 | if( nCycle<2 ){ |
| 1778 | 1782 | g.urlPasswd = 0; |
| 1779 | 1783 | 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 | + } | |
| 1781 | 1788 | } |
| 1782 | 1789 | }else{ |
| 1783 | 1790 | blob_appendf(&xfer.err, "server says: %s\n", zMsg); |
| 1784 | 1791 | nErr++; |
| 1785 | 1792 | } |
| 1786 | 1793 |
| --- 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 |