Fossil SCM
Add the --baseurl open to the "fossil server" and "fossil http" commands, for use with reverse proxies such as nginx.
Commit
ecb85f61a9a9a2f00cf42b89081378125fe32df2
Parent
9c9ad8e5723c1cf…
2 files changed
+8
-5
+57
-19
+8
-5
| --- src/cgi.c | ||
| +++ src/cgi.c | ||
| @@ -369,12 +369,14 @@ | ||
| 369 | 369 | char *zLocation; |
| 370 | 370 | CGIDEBUG(("redirect to %s\n", zURL)); |
| 371 | 371 | if( strncmp(zURL,"http:",5)==0 || strncmp(zURL,"https:",6)==0 ){ |
| 372 | 372 | zLocation = mprintf("Location: %s\r\n", zURL); |
| 373 | 373 | }else if( *zURL=='/' ){ |
| 374 | - zLocation = mprintf("Location: %.*s%s\r\n", | |
| 375 | - strlen(g.zBaseURL)-strlen(g.zTop), g.zBaseURL, zURL); | |
| 374 | + int n1 = (int)strlen(g.zBaseURL); | |
| 375 | + int n2 = (int)strlen(g.zTop); | |
| 376 | + if( g.zBaseURL[n1-1]=='/' ) zURL++; | |
| 377 | + zLocation = mprintf("Location: %.*s%s\r\n", n1-n2, g.zBaseURL, zURL); | |
| 376 | 378 | }else{ |
| 377 | 379 | zLocation = mprintf("Location: %s/%s\r\n", g.zBaseURL, zURL); |
| 378 | 380 | } |
| 379 | 381 | cgi_append_header(zLocation); |
| 380 | 382 | cgi_reset_content(); |
| @@ -1125,14 +1127,15 @@ | ||
| 1125 | 1127 | return zResult; |
| 1126 | 1128 | } |
| 1127 | 1129 | |
| 1128 | 1130 | /* |
| 1129 | 1131 | ** This routine handles a single HTTP request which is coming in on |
| 1130 | -** standard input and which replies on standard output. | |
| 1132 | +** g.httpIn and which replies on g.httpOut | |
| 1131 | 1133 | ** |
| 1132 | -** The HTTP request is read from standard input and is used to initialize | |
| 1133 | -** environment variables as per CGI. The cgi_init() routine to complete | |
| 1134 | +** The HTTP request is read from g.httpIn and is used to initialize | |
| 1135 | +** entries in the cgi_parameter() hash, as if those entries were | |
| 1136 | +** environment variables. A call to cgi_init() completes | |
| 1134 | 1137 | ** the setup. Once all the setup is finished, this procedure returns |
| 1135 | 1138 | ** and subsequent code handles the actual generation of the webpage. |
| 1136 | 1139 | */ |
| 1137 | 1140 | void cgi_handle_http_request(const char *zIpAddr){ |
| 1138 | 1141 | char *z, *zToken; |
| 1139 | 1142 |
| --- src/cgi.c | |
| +++ src/cgi.c | |
| @@ -369,12 +369,14 @@ | |
| 369 | char *zLocation; |
| 370 | CGIDEBUG(("redirect to %s\n", zURL)); |
| 371 | if( strncmp(zURL,"http:",5)==0 || strncmp(zURL,"https:",6)==0 ){ |
| 372 | zLocation = mprintf("Location: %s\r\n", zURL); |
| 373 | }else if( *zURL=='/' ){ |
| 374 | zLocation = mprintf("Location: %.*s%s\r\n", |
| 375 | strlen(g.zBaseURL)-strlen(g.zTop), g.zBaseURL, zURL); |
| 376 | }else{ |
| 377 | zLocation = mprintf("Location: %s/%s\r\n", g.zBaseURL, zURL); |
| 378 | } |
| 379 | cgi_append_header(zLocation); |
| 380 | cgi_reset_content(); |
| @@ -1125,14 +1127,15 @@ | |
| 1125 | return zResult; |
| 1126 | } |
| 1127 | |
| 1128 | /* |
| 1129 | ** This routine handles a single HTTP request which is coming in on |
| 1130 | ** standard input and which replies on standard output. |
| 1131 | ** |
| 1132 | ** The HTTP request is read from standard input and is used to initialize |
| 1133 | ** environment variables as per CGI. The cgi_init() routine to complete |
| 1134 | ** the setup. Once all the setup is finished, this procedure returns |
| 1135 | ** and subsequent code handles the actual generation of the webpage. |
| 1136 | */ |
| 1137 | void cgi_handle_http_request(const char *zIpAddr){ |
| 1138 | char *z, *zToken; |
| 1139 |
| --- src/cgi.c | |
| +++ src/cgi.c | |
| @@ -369,12 +369,14 @@ | |
| 369 | char *zLocation; |
| 370 | CGIDEBUG(("redirect to %s\n", zURL)); |
| 371 | if( strncmp(zURL,"http:",5)==0 || strncmp(zURL,"https:",6)==0 ){ |
| 372 | zLocation = mprintf("Location: %s\r\n", zURL); |
| 373 | }else if( *zURL=='/' ){ |
| 374 | int n1 = (int)strlen(g.zBaseURL); |
| 375 | int n2 = (int)strlen(g.zTop); |
| 376 | if( g.zBaseURL[n1-1]=='/' ) zURL++; |
| 377 | zLocation = mprintf("Location: %.*s%s\r\n", n1-n2, g.zBaseURL, zURL); |
| 378 | }else{ |
| 379 | zLocation = mprintf("Location: %s/%s\r\n", g.zBaseURL, zURL); |
| 380 | } |
| 381 | cgi_append_header(zLocation); |
| 382 | cgi_reset_content(); |
| @@ -1125,14 +1127,15 @@ | |
| 1127 | return zResult; |
| 1128 | } |
| 1129 | |
| 1130 | /* |
| 1131 | ** This routine handles a single HTTP request which is coming in on |
| 1132 | ** g.httpIn and which replies on g.httpOut |
| 1133 | ** |
| 1134 | ** The HTTP request is read from g.httpIn and is used to initialize |
| 1135 | ** entries in the cgi_parameter() hash, as if those entries were |
| 1136 | ** environment variables. A call to cgi_init() completes |
| 1137 | ** the setup. Once all the setup is finished, this procedure returns |
| 1138 | ** and subsequent code handles the actual generation of the webpage. |
| 1139 | */ |
| 1140 | void cgi_handle_http_request(const char *zIpAddr){ |
| 1141 | char *z, *zToken; |
| 1142 |
+57
-19
| --- src/main.c | ||
| +++ src/main.c | ||
| @@ -1207,29 +1207,57 @@ | ||
| 1207 | 1207 | |
| 1208 | 1208 | /* |
| 1209 | 1209 | ** Set the g.zBaseURL value to the full URL for the toplevel of |
| 1210 | 1210 | ** the fossil tree. Set g.zTop to g.zBaseURL without the |
| 1211 | 1211 | ** leading "http://" and the host and port. |
| 1212 | +** | |
| 1213 | +** The g.zBaseURL is normally set based on HTTP_HOST and SCRIPT_NAME | |
| 1214 | +** environment variables. However, if zAltBase is not NULL then it | |
| 1215 | +** is the argument to the --baseurl option command-line option and | |
| 1216 | +** g.zBaseURL and g.zTop is set from that instead. | |
| 1212 | 1217 | */ |
| 1213 | -void set_base_url(void){ | |
| 1218 | +static void set_base_url(const char *zAltBase){ | |
| 1214 | 1219 | int i; |
| 1215 | 1220 | const char *zHost; |
| 1216 | 1221 | const char *zMode; |
| 1217 | 1222 | const char *zCur; |
| 1218 | 1223 | |
| 1219 | 1224 | if( g.zBaseURL!=0 ) return; |
| 1220 | - zHost = PD("HTTP_HOST",""); | |
| 1221 | - zMode = PD("HTTPS","off"); | |
| 1222 | - zCur = PD("SCRIPT_NAME","/"); | |
| 1223 | - i = strlen(zCur); | |
| 1224 | - while( i>0 && zCur[i-1]=='/' ) i--; | |
| 1225 | - if( fossil_stricmp(zMode,"on")==0 ){ | |
| 1226 | - g.zBaseURL = mprintf("https://%s%.*s", zHost, i, zCur); | |
| 1227 | - g.zTop = &g.zBaseURL[8+strlen(zHost)]; | |
| 1225 | + if( zAltBase ){ | |
| 1226 | + int i, n, c; | |
| 1227 | + g.zTop = g.zBaseURL = mprintf("%s", zAltBase); | |
| 1228 | + if( memcmp(g.zTop, "http://", 7)!=0 && memcmp(g.zTop,"https://",8)!=0 ){ | |
| 1229 | + fossil_fatal("argument to --baseurl should be 'http://host/path'" | |
| 1230 | + " or 'https://host/path'"); | |
| 1231 | + } | |
| 1232 | + for(i=n=0; (c = g.zTop[i])!=0; i++){ | |
| 1233 | + if( c=='/' ){ | |
| 1234 | + n++; | |
| 1235 | + if( n==3 ){ | |
| 1236 | + g.zTop += i; | |
| 1237 | + break; | |
| 1238 | + } | |
| 1239 | + } | |
| 1240 | + } | |
| 1241 | + if( g.zTop==g.zBaseURL ){ | |
| 1242 | + fossil_fatal("argument to --baseurl should be 'http://host/path'" | |
| 1243 | + " or 'https://host/path'"); | |
| 1244 | + } | |
| 1245 | + if( g.zTop[1]==0 ) g.zTop++; | |
| 1228 | 1246 | }else{ |
| 1229 | - g.zBaseURL = mprintf("http://%s%.*s", zHost, i, zCur); | |
| 1230 | - g.zTop = &g.zBaseURL[7+strlen(zHost)]; | |
| 1247 | + zHost = PD("HTTP_HOST",""); | |
| 1248 | + zMode = PD("HTTPS","off"); | |
| 1249 | + zCur = PD("SCRIPT_NAME","/"); | |
| 1250 | + i = strlen(zCur); | |
| 1251 | + while( i>0 && zCur[i-1]=='/' ) i--; | |
| 1252 | + if( fossil_stricmp(zMode,"on")==0 ){ | |
| 1253 | + g.zBaseURL = mprintf("https://%s%.*s", zHost, i, zCur); | |
| 1254 | + g.zTop = &g.zBaseURL[8+strlen(zHost)]; | |
| 1255 | + }else{ | |
| 1256 | + g.zBaseURL = mprintf("http://%s%.*s", zHost, i, zCur); | |
| 1257 | + g.zTop = &g.zBaseURL[7+strlen(zHost)]; | |
| 1258 | + } | |
| 1231 | 1259 | } |
| 1232 | 1260 | if( db_is_writeable("repository") ){ |
| 1233 | 1261 | if( !db_exists("SELECT 1 FROM config WHERE name='baseurl:%q'", g.zBaseURL)){ |
| 1234 | 1262 | db_multi_exec("INSERT INTO config(name,value,mtime)" |
| 1235 | 1263 | "VALUES('baseurl:%q',1,now())", g.zBaseURL); |
| @@ -1357,11 +1385,11 @@ | ||
| 1357 | 1385 | } |
| 1358 | 1386 | zRepo[j] = '.'; |
| 1359 | 1387 | } |
| 1360 | 1388 | |
| 1361 | 1389 | if( szFile<1024 ){ |
| 1362 | - set_base_url(); | |
| 1390 | + set_base_url(0); | |
| 1363 | 1391 | if( zNotFound ){ |
| 1364 | 1392 | cgi_redirect(zNotFound); |
| 1365 | 1393 | }else{ |
| 1366 | 1394 | #ifdef FOSSIL_ENABLE_JSON |
| 1367 | 1395 | if(g.json.isJsonMode){ |
| @@ -1395,11 +1423,11 @@ | ||
| 1395 | 1423 | ** page. |
| 1396 | 1424 | */ |
| 1397 | 1425 | if( g.zContentType && memcmp(g.zContentType, "application/x-fossil", 20)==0 ){ |
| 1398 | 1426 | zPathInfo = "/xfer"; |
| 1399 | 1427 | } |
| 1400 | - set_base_url(); | |
| 1428 | + set_base_url(0); | |
| 1401 | 1429 | if( zPathInfo==0 || zPathInfo[0]==0 |
| 1402 | 1430 | || (zPathInfo[0]=='/' && zPathInfo[1]==0) ){ |
| 1403 | 1431 | #ifdef FOSSIL_ENABLE_JSON |
| 1404 | 1432 | if(g.json.isJsonMode){ |
| 1405 | 1433 | json_err(FSL_JSON_E_RESOURCE_NOT_FOUND,NULL,1); |
| @@ -1644,11 +1672,11 @@ | ||
| 1644 | 1672 | */ |
| 1645 | 1673 | void redirect_web_page(int nRedirect, char **azRedirect){ |
| 1646 | 1674 | int i; /* Loop counter */ |
| 1647 | 1675 | const char *zNotFound = 0; /* Not found URL */ |
| 1648 | 1676 | const char *zName = P("name"); |
| 1649 | - set_base_url(); | |
| 1677 | + set_base_url(0); | |
| 1650 | 1678 | if( zName==0 ){ |
| 1651 | 1679 | zName = P("SCRIPT_NAME"); |
| 1652 | 1680 | if( zName && zName[0]=='/' ) zName++; |
| 1653 | 1681 | } |
| 1654 | 1682 | if( zName && validate16(zName, strlen(zName)) ){ |
| @@ -1736,25 +1764,29 @@ | ||
| 1736 | 1764 | ** If the --localauth option is given, then automatic login is performed |
| 1737 | 1765 | ** for requests coming from localhost, if the "localauth" setting is not |
| 1738 | 1766 | ** enabled. |
| 1739 | 1767 | ** |
| 1740 | 1768 | ** Options: |
| 1741 | -** --localauth enable automatic login for local connections | |
| 1742 | -** --host NAME specify hostname of the server | |
| 1743 | -** --https signal a request coming in via https | |
| 1744 | -** --nossl signal that no SSL connections are available | |
| 1745 | -** --notfound URL use URL as "HTTP 404, object not found" page. | |
| 1769 | +** --localauth enable automatic login for local connections | |
| 1770 | +** --host NAME specify hostname of the server | |
| 1771 | +** --https signal a request coming in via https | |
| 1772 | +** --nossl signal that no SSL connections are available | |
| 1773 | +** --notfound URL use URL as "HTTP 404, object not found" page. | |
| 1774 | +** --baseurl URL base URL (useful with reverse proxies) | |
| 1746 | 1775 | ** |
| 1747 | 1776 | ** See also: cgi, server, winsrv |
| 1748 | 1777 | */ |
| 1749 | 1778 | void cmd_http(void){ |
| 1750 | 1779 | const char *zIpAddr; |
| 1751 | 1780 | const char *zNotFound; |
| 1752 | 1781 | const char *zHost; |
| 1782 | + const char *zAltBase; | |
| 1753 | 1783 | zNotFound = find_option("notfound", 0, 1); |
| 1754 | 1784 | g.useLocalauth = find_option("localauth", 0, 0)!=0; |
| 1755 | 1785 | g.sslNotAvailable = find_option("nossl", 0, 0)!=0; |
| 1786 | + zAltBase = find_option("baseurl", 0, 1); | |
| 1787 | + if( zAltBase ) set_base_url(zAltBase); | |
| 1756 | 1788 | if( find_option("https",0,0)!=0 ) cgi_replace_parameter("HTTPS","on"); |
| 1757 | 1789 | zHost = find_option("host", 0, 1); |
| 1758 | 1790 | if( zHost ) cgi_replace_parameter("HTTP_HOST",zHost); |
| 1759 | 1791 | g.cgiOutput = 1; |
| 1760 | 1792 | if( g.argc!=2 && g.argc!=3 && g.argc!=6 ){ |
| @@ -1850,10 +1882,11 @@ | ||
| 1850 | 1882 | ** |
| 1851 | 1883 | ** Options: |
| 1852 | 1884 | ** --localauth enable automatic login for requests from localhost |
| 1853 | 1885 | ** -P|--port TCPPORT listen to request on port TCPPORT |
| 1854 | 1886 | ** --th-trace trace TH1 execution (for debugging purposes) |
| 1887 | +** --baseurl URL Use URL as the base (useful for reverse proxies) | |
| 1855 | 1888 | ** |
| 1856 | 1889 | ** See also: cgi, http, winsrv |
| 1857 | 1890 | */ |
| 1858 | 1891 | void cmd_webserver(void){ |
| 1859 | 1892 | int iPort, mxPort; /* Range of TCP ports allowed */ |
| @@ -1861,10 +1894,11 @@ | ||
| 1861 | 1894 | const char *zBrowser; /* Name of web browser program */ |
| 1862 | 1895 | char *zBrowserCmd = 0; /* Command to launch the web browser */ |
| 1863 | 1896 | int isUiCmd; /* True if command is "ui", not "server' */ |
| 1864 | 1897 | const char *zNotFound; /* The --notfound option or NULL */ |
| 1865 | 1898 | int flags = 0; /* Server flags */ |
| 1899 | + const char *zAltBase; /* Argument to the --baseurl option */ | |
| 1866 | 1900 | |
| 1867 | 1901 | #if defined(_WIN32) |
| 1868 | 1902 | const char *zStopperFile; /* Name of file used to terminate server */ |
| 1869 | 1903 | zStopperFile = find_option("stopper", 0, 1); |
| 1870 | 1904 | #endif |
| @@ -1874,10 +1908,14 @@ | ||
| 1874 | 1908 | if( g.thTrace ){ |
| 1875 | 1909 | blob_zero(&g.thLog); |
| 1876 | 1910 | } |
| 1877 | 1911 | zPort = find_option("port", "P", 1); |
| 1878 | 1912 | zNotFound = find_option("notfound", 0, 1); |
| 1913 | + zAltBase = find_option("baseurl", 0, 1); | |
| 1914 | + if( zAltBase ){ | |
| 1915 | + set_base_url(zAltBase); | |
| 1916 | + } | |
| 1879 | 1917 | if( g.argc!=2 && g.argc!=3 ) usage("?REPOSITORY?"); |
| 1880 | 1918 | isUiCmd = g.argv[1][0]=='u'; |
| 1881 | 1919 | if( isUiCmd ){ |
| 1882 | 1920 | flags |= HTTP_SERVER_LOCALHOST; |
| 1883 | 1921 | g.useLocalauth = 1; |
| 1884 | 1922 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -1207,29 +1207,57 @@ | |
| 1207 | |
| 1208 | /* |
| 1209 | ** Set the g.zBaseURL value to the full URL for the toplevel of |
| 1210 | ** the fossil tree. Set g.zTop to g.zBaseURL without the |
| 1211 | ** leading "http://" and the host and port. |
| 1212 | */ |
| 1213 | void set_base_url(void){ |
| 1214 | int i; |
| 1215 | const char *zHost; |
| 1216 | const char *zMode; |
| 1217 | const char *zCur; |
| 1218 | |
| 1219 | if( g.zBaseURL!=0 ) return; |
| 1220 | zHost = PD("HTTP_HOST",""); |
| 1221 | zMode = PD("HTTPS","off"); |
| 1222 | zCur = PD("SCRIPT_NAME","/"); |
| 1223 | i = strlen(zCur); |
| 1224 | while( i>0 && zCur[i-1]=='/' ) i--; |
| 1225 | if( fossil_stricmp(zMode,"on")==0 ){ |
| 1226 | g.zBaseURL = mprintf("https://%s%.*s", zHost, i, zCur); |
| 1227 | g.zTop = &g.zBaseURL[8+strlen(zHost)]; |
| 1228 | }else{ |
| 1229 | g.zBaseURL = mprintf("http://%s%.*s", zHost, i, zCur); |
| 1230 | g.zTop = &g.zBaseURL[7+strlen(zHost)]; |
| 1231 | } |
| 1232 | if( db_is_writeable("repository") ){ |
| 1233 | if( !db_exists("SELECT 1 FROM config WHERE name='baseurl:%q'", g.zBaseURL)){ |
| 1234 | db_multi_exec("INSERT INTO config(name,value,mtime)" |
| 1235 | "VALUES('baseurl:%q',1,now())", g.zBaseURL); |
| @@ -1357,11 +1385,11 @@ | |
| 1357 | } |
| 1358 | zRepo[j] = '.'; |
| 1359 | } |
| 1360 | |
| 1361 | if( szFile<1024 ){ |
| 1362 | set_base_url(); |
| 1363 | if( zNotFound ){ |
| 1364 | cgi_redirect(zNotFound); |
| 1365 | }else{ |
| 1366 | #ifdef FOSSIL_ENABLE_JSON |
| 1367 | if(g.json.isJsonMode){ |
| @@ -1395,11 +1423,11 @@ | |
| 1395 | ** page. |
| 1396 | */ |
| 1397 | if( g.zContentType && memcmp(g.zContentType, "application/x-fossil", 20)==0 ){ |
| 1398 | zPathInfo = "/xfer"; |
| 1399 | } |
| 1400 | set_base_url(); |
| 1401 | if( zPathInfo==0 || zPathInfo[0]==0 |
| 1402 | || (zPathInfo[0]=='/' && zPathInfo[1]==0) ){ |
| 1403 | #ifdef FOSSIL_ENABLE_JSON |
| 1404 | if(g.json.isJsonMode){ |
| 1405 | json_err(FSL_JSON_E_RESOURCE_NOT_FOUND,NULL,1); |
| @@ -1644,11 +1672,11 @@ | |
| 1644 | */ |
| 1645 | void redirect_web_page(int nRedirect, char **azRedirect){ |
| 1646 | int i; /* Loop counter */ |
| 1647 | const char *zNotFound = 0; /* Not found URL */ |
| 1648 | const char *zName = P("name"); |
| 1649 | set_base_url(); |
| 1650 | if( zName==0 ){ |
| 1651 | zName = P("SCRIPT_NAME"); |
| 1652 | if( zName && zName[0]=='/' ) zName++; |
| 1653 | } |
| 1654 | if( zName && validate16(zName, strlen(zName)) ){ |
| @@ -1736,25 +1764,29 @@ | |
| 1736 | ** If the --localauth option is given, then automatic login is performed |
| 1737 | ** for requests coming from localhost, if the "localauth" setting is not |
| 1738 | ** enabled. |
| 1739 | ** |
| 1740 | ** Options: |
| 1741 | ** --localauth enable automatic login for local connections |
| 1742 | ** --host NAME specify hostname of the server |
| 1743 | ** --https signal a request coming in via https |
| 1744 | ** --nossl signal that no SSL connections are available |
| 1745 | ** --notfound URL use URL as "HTTP 404, object not found" page. |
| 1746 | ** |
| 1747 | ** See also: cgi, server, winsrv |
| 1748 | */ |
| 1749 | void cmd_http(void){ |
| 1750 | const char *zIpAddr; |
| 1751 | const char *zNotFound; |
| 1752 | const char *zHost; |
| 1753 | zNotFound = find_option("notfound", 0, 1); |
| 1754 | g.useLocalauth = find_option("localauth", 0, 0)!=0; |
| 1755 | g.sslNotAvailable = find_option("nossl", 0, 0)!=0; |
| 1756 | if( find_option("https",0,0)!=0 ) cgi_replace_parameter("HTTPS","on"); |
| 1757 | zHost = find_option("host", 0, 1); |
| 1758 | if( zHost ) cgi_replace_parameter("HTTP_HOST",zHost); |
| 1759 | g.cgiOutput = 1; |
| 1760 | if( g.argc!=2 && g.argc!=3 && g.argc!=6 ){ |
| @@ -1850,10 +1882,11 @@ | |
| 1850 | ** |
| 1851 | ** Options: |
| 1852 | ** --localauth enable automatic login for requests from localhost |
| 1853 | ** -P|--port TCPPORT listen to request on port TCPPORT |
| 1854 | ** --th-trace trace TH1 execution (for debugging purposes) |
| 1855 | ** |
| 1856 | ** See also: cgi, http, winsrv |
| 1857 | */ |
| 1858 | void cmd_webserver(void){ |
| 1859 | int iPort, mxPort; /* Range of TCP ports allowed */ |
| @@ -1861,10 +1894,11 @@ | |
| 1861 | const char *zBrowser; /* Name of web browser program */ |
| 1862 | char *zBrowserCmd = 0; /* Command to launch the web browser */ |
| 1863 | int isUiCmd; /* True if command is "ui", not "server' */ |
| 1864 | const char *zNotFound; /* The --notfound option or NULL */ |
| 1865 | int flags = 0; /* Server flags */ |
| 1866 | |
| 1867 | #if defined(_WIN32) |
| 1868 | const char *zStopperFile; /* Name of file used to terminate server */ |
| 1869 | zStopperFile = find_option("stopper", 0, 1); |
| 1870 | #endif |
| @@ -1874,10 +1908,14 @@ | |
| 1874 | if( g.thTrace ){ |
| 1875 | blob_zero(&g.thLog); |
| 1876 | } |
| 1877 | zPort = find_option("port", "P", 1); |
| 1878 | zNotFound = find_option("notfound", 0, 1); |
| 1879 | if( g.argc!=2 && g.argc!=3 ) usage("?REPOSITORY?"); |
| 1880 | isUiCmd = g.argv[1][0]=='u'; |
| 1881 | if( isUiCmd ){ |
| 1882 | flags |= HTTP_SERVER_LOCALHOST; |
| 1883 | g.useLocalauth = 1; |
| 1884 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -1207,29 +1207,57 @@ | |
| 1207 | |
| 1208 | /* |
| 1209 | ** Set the g.zBaseURL value to the full URL for the toplevel of |
| 1210 | ** the fossil tree. Set g.zTop to g.zBaseURL without the |
| 1211 | ** leading "http://" and the host and port. |
| 1212 | ** |
| 1213 | ** The g.zBaseURL is normally set based on HTTP_HOST and SCRIPT_NAME |
| 1214 | ** environment variables. However, if zAltBase is not NULL then it |
| 1215 | ** is the argument to the --baseurl option command-line option and |
| 1216 | ** g.zBaseURL and g.zTop is set from that instead. |
| 1217 | */ |
| 1218 | static void set_base_url(const char *zAltBase){ |
| 1219 | int i; |
| 1220 | const char *zHost; |
| 1221 | const char *zMode; |
| 1222 | const char *zCur; |
| 1223 | |
| 1224 | if( g.zBaseURL!=0 ) return; |
| 1225 | if( zAltBase ){ |
| 1226 | int i, n, c; |
| 1227 | g.zTop = g.zBaseURL = mprintf("%s", zAltBase); |
| 1228 | if( memcmp(g.zTop, "http://", 7)!=0 && memcmp(g.zTop,"https://",8)!=0 ){ |
| 1229 | fossil_fatal("argument to --baseurl should be 'http://host/path'" |
| 1230 | " or 'https://host/path'"); |
| 1231 | } |
| 1232 | for(i=n=0; (c = g.zTop[i])!=0; i++){ |
| 1233 | if( c=='/' ){ |
| 1234 | n++; |
| 1235 | if( n==3 ){ |
| 1236 | g.zTop += i; |
| 1237 | break; |
| 1238 | } |
| 1239 | } |
| 1240 | } |
| 1241 | if( g.zTop==g.zBaseURL ){ |
| 1242 | fossil_fatal("argument to --baseurl should be 'http://host/path'" |
| 1243 | " or 'https://host/path'"); |
| 1244 | } |
| 1245 | if( g.zTop[1]==0 ) g.zTop++; |
| 1246 | }else{ |
| 1247 | zHost = PD("HTTP_HOST",""); |
| 1248 | zMode = PD("HTTPS","off"); |
| 1249 | zCur = PD("SCRIPT_NAME","/"); |
| 1250 | i = strlen(zCur); |
| 1251 | while( i>0 && zCur[i-1]=='/' ) i--; |
| 1252 | if( fossil_stricmp(zMode,"on")==0 ){ |
| 1253 | g.zBaseURL = mprintf("https://%s%.*s", zHost, i, zCur); |
| 1254 | g.zTop = &g.zBaseURL[8+strlen(zHost)]; |
| 1255 | }else{ |
| 1256 | g.zBaseURL = mprintf("http://%s%.*s", zHost, i, zCur); |
| 1257 | g.zTop = &g.zBaseURL[7+strlen(zHost)]; |
| 1258 | } |
| 1259 | } |
| 1260 | if( db_is_writeable("repository") ){ |
| 1261 | if( !db_exists("SELECT 1 FROM config WHERE name='baseurl:%q'", g.zBaseURL)){ |
| 1262 | db_multi_exec("INSERT INTO config(name,value,mtime)" |
| 1263 | "VALUES('baseurl:%q',1,now())", g.zBaseURL); |
| @@ -1357,11 +1385,11 @@ | |
| 1385 | } |
| 1386 | zRepo[j] = '.'; |
| 1387 | } |
| 1388 | |
| 1389 | if( szFile<1024 ){ |
| 1390 | set_base_url(0); |
| 1391 | if( zNotFound ){ |
| 1392 | cgi_redirect(zNotFound); |
| 1393 | }else{ |
| 1394 | #ifdef FOSSIL_ENABLE_JSON |
| 1395 | if(g.json.isJsonMode){ |
| @@ -1395,11 +1423,11 @@ | |
| 1423 | ** page. |
| 1424 | */ |
| 1425 | if( g.zContentType && memcmp(g.zContentType, "application/x-fossil", 20)==0 ){ |
| 1426 | zPathInfo = "/xfer"; |
| 1427 | } |
| 1428 | set_base_url(0); |
| 1429 | if( zPathInfo==0 || zPathInfo[0]==0 |
| 1430 | || (zPathInfo[0]=='/' && zPathInfo[1]==0) ){ |
| 1431 | #ifdef FOSSIL_ENABLE_JSON |
| 1432 | if(g.json.isJsonMode){ |
| 1433 | json_err(FSL_JSON_E_RESOURCE_NOT_FOUND,NULL,1); |
| @@ -1644,11 +1672,11 @@ | |
| 1672 | */ |
| 1673 | void redirect_web_page(int nRedirect, char **azRedirect){ |
| 1674 | int i; /* Loop counter */ |
| 1675 | const char *zNotFound = 0; /* Not found URL */ |
| 1676 | const char *zName = P("name"); |
| 1677 | set_base_url(0); |
| 1678 | if( zName==0 ){ |
| 1679 | zName = P("SCRIPT_NAME"); |
| 1680 | if( zName && zName[0]=='/' ) zName++; |
| 1681 | } |
| 1682 | if( zName && validate16(zName, strlen(zName)) ){ |
| @@ -1736,25 +1764,29 @@ | |
| 1764 | ** If the --localauth option is given, then automatic login is performed |
| 1765 | ** for requests coming from localhost, if the "localauth" setting is not |
| 1766 | ** enabled. |
| 1767 | ** |
| 1768 | ** Options: |
| 1769 | ** --localauth enable automatic login for local connections |
| 1770 | ** --host NAME specify hostname of the server |
| 1771 | ** --https signal a request coming in via https |
| 1772 | ** --nossl signal that no SSL connections are available |
| 1773 | ** --notfound URL use URL as "HTTP 404, object not found" page. |
| 1774 | ** --baseurl URL base URL (useful with reverse proxies) |
| 1775 | ** |
| 1776 | ** See also: cgi, server, winsrv |
| 1777 | */ |
| 1778 | void cmd_http(void){ |
| 1779 | const char *zIpAddr; |
| 1780 | const char *zNotFound; |
| 1781 | const char *zHost; |
| 1782 | const char *zAltBase; |
| 1783 | zNotFound = find_option("notfound", 0, 1); |
| 1784 | g.useLocalauth = find_option("localauth", 0, 0)!=0; |
| 1785 | g.sslNotAvailable = find_option("nossl", 0, 0)!=0; |
| 1786 | zAltBase = find_option("baseurl", 0, 1); |
| 1787 | if( zAltBase ) set_base_url(zAltBase); |
| 1788 | if( find_option("https",0,0)!=0 ) cgi_replace_parameter("HTTPS","on"); |
| 1789 | zHost = find_option("host", 0, 1); |
| 1790 | if( zHost ) cgi_replace_parameter("HTTP_HOST",zHost); |
| 1791 | g.cgiOutput = 1; |
| 1792 | if( g.argc!=2 && g.argc!=3 && g.argc!=6 ){ |
| @@ -1850,10 +1882,11 @@ | |
| 1882 | ** |
| 1883 | ** Options: |
| 1884 | ** --localauth enable automatic login for requests from localhost |
| 1885 | ** -P|--port TCPPORT listen to request on port TCPPORT |
| 1886 | ** --th-trace trace TH1 execution (for debugging purposes) |
| 1887 | ** --baseurl URL Use URL as the base (useful for reverse proxies) |
| 1888 | ** |
| 1889 | ** See also: cgi, http, winsrv |
| 1890 | */ |
| 1891 | void cmd_webserver(void){ |
| 1892 | int iPort, mxPort; /* Range of TCP ports allowed */ |
| @@ -1861,10 +1894,11 @@ | |
| 1894 | const char *zBrowser; /* Name of web browser program */ |
| 1895 | char *zBrowserCmd = 0; /* Command to launch the web browser */ |
| 1896 | int isUiCmd; /* True if command is "ui", not "server' */ |
| 1897 | const char *zNotFound; /* The --notfound option or NULL */ |
| 1898 | int flags = 0; /* Server flags */ |
| 1899 | const char *zAltBase; /* Argument to the --baseurl option */ |
| 1900 | |
| 1901 | #if defined(_WIN32) |
| 1902 | const char *zStopperFile; /* Name of file used to terminate server */ |
| 1903 | zStopperFile = find_option("stopper", 0, 1); |
| 1904 | #endif |
| @@ -1874,10 +1908,14 @@ | |
| 1908 | if( g.thTrace ){ |
| 1909 | blob_zero(&g.thLog); |
| 1910 | } |
| 1911 | zPort = find_option("port", "P", 1); |
| 1912 | zNotFound = find_option("notfound", 0, 1); |
| 1913 | zAltBase = find_option("baseurl", 0, 1); |
| 1914 | if( zAltBase ){ |
| 1915 | set_base_url(zAltBase); |
| 1916 | } |
| 1917 | if( g.argc!=2 && g.argc!=3 ) usage("?REPOSITORY?"); |
| 1918 | isUiCmd = g.argv[1][0]=='u'; |
| 1919 | if( isUiCmd ){ |
| 1920 | flags |= HTTP_SERVER_LOCALHOST; |
| 1921 | g.useLocalauth = 1; |
| 1922 |