Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Changes In Branch ashish-ipv6 Excluding Merge-Ins
This is equivalent to a diff from fe5cf37e66 to 7124f09f07
2012-03-10
| ||
16:47 | minor internal refactoring of /json/wiki/get in prep for /json/wiki/preview. check-in: 73816973fb user: stephan tags: trunk | |
13:17 | Merge latest changes from trunk. Leaf check-in: 7124f09f07 user: ashish tags: ashish-ipv6 | |
03:59 | Removed some dead code in /json/timeline/wiki. check-in: fe5cf37e66 user: stephan tags: trunk | |
2012-03-09
| ||
20:16 | Fixed a logic bug which caused /json/wiki/create to not be able to create a new page. check-in: 64c2ec012c user: stephan tags: trunk | |
2012-01-15
| ||
18:06 | Merge latest changes from trunk check-in: b3130baa06 user: ashish tags: ashish-ipv6 | |
Changes to auto.def.
7 7 => {Look for openssl in the given path, or auto or none} 8 8 with-zlib:path => {Look for zlib in the given path} 9 9 with-tcl:path => {Enable Tcl integration, with Tcl in the specified path} 10 10 internal-sqlite=1 => {Don't use the internal sqlite, use the system one} 11 11 static=0 => {Link a static executable} 12 12 lineedit=1 => {Disable line editing} 13 13 fossil-debug=0 => {Build with fossil debugging enabled} 14 + ipv6=1 => {Disable IPv6 support} 14 15 json=0 => {Build with fossil JSON API enabled} 15 16 } 16 17 17 18 # sqlite wants these types if possible 18 19 cc-with {-includes {stdint.h inttypes.h}} { 19 20 cc-check-types uint32_t uint16_t int16_t uint8_t 20 21 } ................................................................................ 70 71 define-append EXTRA_CFLAGS -DFOSSIL_ENABLE_JSON 71 72 } 72 73 73 74 if {[opt-bool static]} { 74 75 # XXX: This will not work on all systems. 75 76 define-append EXTRA_LDFLAGS -static 76 77 } 78 + 79 +if {[opt-bool ipv6]} { 80 + define-append EXTRA_CFLAGS -DWITH_IPV6 81 + msg-result "IPv6 support enabled" 82 + if {[cc-check-functions getaddrinfo]} { 83 + define-append EXTRA_CFLAGS -DHAVE_GETADDRINFO 84 + msg-result "getaddrinfo() enabled" 85 + } 86 +} 77 87 78 88 79 89 # Check for zlib, using the given location if specified 80 90 set zlibpath [opt-val with-zlib] 81 91 if {$zlibpath ne ""} { 82 92 cc-with [list -cflags "-I$zlibpath -L$zlibpath"] 83 93 define-append EXTRA_CFLAGS -I$zlibpath
Changes to src/cgi.c.
32 32 # include <sys/socket.h> 33 33 # include <netinet/in.h> 34 34 # include <arpa/inet.h> 35 35 # include <sys/times.h> 36 36 # include <sys/time.h> 37 37 # include <sys/wait.h> 38 38 # include <sys/select.h> 39 +# include <netdb.h> /* for NI_NUMERICHOST */ 39 40 #endif 40 41 #ifdef __EMX__ 41 42 typedef int socklen_t; 42 43 #endif 43 44 #include <time.h> 44 45 #include <stdio.h> 45 46 #include <stdlib.h> ................................................................................ 1114 1115 ** environment variables as per CGI. The cgi_init() routine to complete 1115 1116 ** the setup. Once all the setup is finished, this procedure returns 1116 1117 ** and subsequent code handles the actual generation of the webpage. 1117 1118 */ 1118 1119 void cgi_handle_http_request(const char *zIpAddr){ 1119 1120 char *z, *zToken; 1120 1121 int i; 1121 - struct sockaddr_in remoteName; 1122 - socklen_t size = sizeof(struct sockaddr_in); 1122 + struct sockaddr_storage remoteName; 1123 + socklen_t size = sizeof(remoteName); 1123 1124 char zLine[2000]; /* A single line of input. */ 1124 1125 g.fullHttpReply = 1; 1125 1126 if( fgets(zLine, sizeof(zLine),g.httpIn)==0 ){ 1126 1127 malformed_request(); 1127 1128 } 1128 1129 zToken = extract_token(zLine, &z); 1129 1130 if( zToken==0 ){ ................................................................................ 1140 1141 malformed_request(); 1141 1142 } 1142 1143 cgi_setenv("REQUEST_URI", zToken); 1143 1144 for(i=0; zToken[i] && zToken[i]!='?'; i++){} 1144 1145 if( zToken[i] ) zToken[i++] = 0; 1145 1146 cgi_setenv("PATH_INFO", zToken); 1146 1147 cgi_setenv("QUERY_STRING", &zToken[i]); 1147 - if( zIpAddr==0 && 1148 - getpeername(fileno(g.httpIn), (struct sockaddr*)&remoteName, 1149 - &size)>=0 1150 - ){ 1151 - zIpAddr = inet_ntoa(remoteName.sin_addr); 1152 - } 1153 - if( zIpAddr ){ 1154 - cgi_setenv("REMOTE_ADDR", zIpAddr); 1155 - g.zIpAddr = mprintf("%s", zIpAddr); 1156 - } 1157 1148 1158 1149 /* Get all the optional fields that follow the first line. 1159 1150 */ 1160 1151 while( fgets(zLine,sizeof(zLine),g.httpIn) ){ 1161 1152 char *zFieldName; 1162 1153 char *zVal; 1163 1154 ................................................................................ 1180 1171 cgi_setenv("HTTPS", zVal); 1181 1172 }else if( fossil_strcmp(zFieldName,"host:")==0 ){ 1182 1173 cgi_setenv("HTTP_HOST", zVal); 1183 1174 }else if( fossil_strcmp(zFieldName,"if-none-match:")==0 ){ 1184 1175 cgi_setenv("HTTP_IF_NONE_MATCH", zVal); 1185 1176 }else if( fossil_strcmp(zFieldName,"if-modified-since:")==0 ){ 1186 1177 cgi_setenv("HTTP_IF_MODIFIED_SINCE", zVal); 1178 + }else if( fossil_strcmp(zFieldName,"x-forwarded-for:")==0 ){ 1179 + char* p = zVal; 1180 + /* 1181 + ** x-forwarded-for header is a list of comma-separated addresses, 1182 + ** with leftmost address corresponding to the client 1183 + */ 1184 + while(*p && *p != ',') p++; 1185 + *p = '\0'; 1186 + zIpAddr = mprintf( "%s", zVal ); 1187 1187 #if 0 1188 1188 }else if( fossil_strcmp(zFieldName,"referer:")==0 ){ 1189 1189 cgi_setenv("HTTP_REFERER", zVal); 1190 1190 #endif 1191 1191 }else if( fossil_strcmp(zFieldName,"user-agent:")==0 ){ 1192 1192 cgi_setenv("HTTP_USER_AGENT", zVal); 1193 1193 } 1194 1194 } 1195 + 1196 + if( zIpAddr==0 && 1197 + getsockname(fileno(g.httpIn), (struct sockaddr*)&remoteName, 1198 + &size)>=0 1199 + ){ 1200 + sa_family_t family; 1201 + int v4mapped=0; 1202 + if( remoteName.ss_family == AF_INET6 && 1203 + IN6_IS_ADDR_V4MAPPED(&(((struct sockaddr_in6*)&remoteName)->sin6_addr)) ){ 1204 + v4mapped = 1; 1205 + } 1206 + if(!getnameinfo((struct sockaddr*)&remoteName, size, zLine, sizeof(zLine), 1207 + NULL, 0, NI_NUMERICHOST)){ 1208 + zIpAddr = zLine; 1209 + } else { 1210 + zIpAddr = NULL; 1211 + } 1212 + if(zIpAddr && v4mapped) { 1213 + /* ::ffff:172.16.0.2 */ 1214 + zIpAddr += 7; 1215 + } 1216 + } 1217 + if( zIpAddr ){ 1218 + cgi_setenv("REMOTE_ADDR", zIpAddr); 1219 + g.zIpAddr = mprintf("%s", zIpAddr); 1220 + } 1195 1221 1196 1222 cgi_init(); 1197 1223 } 1198 1224 1199 1225 #if INTERFACE 1200 1226 /* 1201 1227 ** Bitmap values for the flags parameter to cgi_http_server(). ................................................................................ 1228 1254 int listener = -1; /* The server socket */ 1229 1255 int connection; /* A socket for each individual connection */ 1230 1256 fd_set readfds; /* Set of file descriptors for select() */ 1231 1257 socklen_t lenaddr; /* Length of the inaddr structure */ 1232 1258 int child; /* PID of the child process */ 1233 1259 int nchildren = 0; /* Number of child processes */ 1234 1260 struct timeval delay; /* How long to wait inside select() */ 1261 +#ifdef HAVE_GETADDRINFO 1262 + struct addrinfo hints; 1263 + struct addrinfo* res; 1264 + struct addrinfo* i; 1265 + struct sockaddr_storage inaddr; /* The socket address */ 1266 + char* sPort; 1267 + int iRet; 1268 +#else 1269 +#ifdef WITH_IPV6 1270 + struct sockaddr_in6 inaddr; /* The socket address */ 1271 +#else 1235 1272 struct sockaddr_in inaddr; /* The socket address */ 1273 +#endif 1274 +#endif 1236 1275 int opt = 1; /* setsockopt flag */ 1237 1276 int iPort = mnPort; 1238 1277 1239 1278 while( iPort<=mxPort ){ 1240 - memset(&inaddr, 0, sizeof(inaddr)); 1241 - inaddr.sin_family = AF_INET; 1242 - if( flags & HTTP_SERVER_LOCALHOST ){ 1243 - inaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 1244 - }else{ 1245 - inaddr.sin_addr.s_addr = htonl(INADDR_ANY); 1279 +#ifdef HAVE_GETADDRINFO 1280 + memset(&hints, 0, sizeof(struct addrinfo)); 1281 +#ifdef WITH_IPV6 1282 + hints.ai_family = PF_UNSPEC; 1283 +#else 1284 + hints.ai_family = PF_INET; 1285 +#endif 1286 + hints.ai_socktype = SOCK_STREAM; 1287 + hints.ai_protocol = IPPROTO_TCP; 1288 + if(!(flags & HTTP_SERVER_LOCALHOST)) hints.ai_flags |= AI_PASSIVE; 1289 + 1290 + sPort = mprintf("%d", iPort); 1291 + 1292 + if(iRet = getaddrinfo(NULL, sPort, &hints, &res)) { 1293 + fossil_fatal("Unable to obtain address: %s", gai_strerror(iRet)); 1294 + } 1295 + 1296 + for(i = res; i; i = i->ai_next) { 1297 + listener = socket(i->ai_family, i->ai_socktype, i->ai_protocol); 1298 + if(listener < 0) { 1299 + fossil_fatal("Unable to create socket"); 1300 + } 1301 + opt=1; 1302 + setsockopt(listener,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt)); 1303 + if(i->ai_family == AF_INET6) { 1304 + opt=0; 1305 + setsockopt(listener, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt)); 1306 + } 1307 + if( bind(listener, i->ai_addr, i->ai_addrlen)<0 ){ 1308 + close(listener); 1309 + listener = -1; 1310 + } 1311 + break; 1246 1312 } 1247 - inaddr.sin_port = htons(iPort); 1248 - listener = socket(AF_INET, SOCK_STREAM, 0); 1249 - if( listener<0 ){ 1313 + 1314 + free(sPort); 1315 + freeaddrinfo(res); 1316 + 1317 + if(listener == -1) { 1250 1318 iPort++; 1251 1319 continue; 1252 1320 } 1321 +#else 1322 + memset(&inaddr, 0, sizeof(inaddr)); 1323 + 1324 +#ifdef WITH_IPV6 1325 + inaddr.sin6_family = AF_INET6; 1326 +#else 1327 + inaddr.sin_family = AF_INET; 1328 +#endif 1329 + if( flags & HTTP_SERVER_LOCALHOST ){ 1330 +#ifdef WITH_IPV6 1331 + memcpy(&inaddr.sin6_addr, &in6addr_loopback, sizeof(inaddr.sin6_addr)); 1332 +#else 1333 + inaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 1334 +#endif 1335 + }else{ 1336 +#ifdef WITH_IPV6 1337 + memcpy(&inaddr.sin6_addr, &in6addr_any, sizeof(inaddr.sin6_addr)); 1338 +#else 1339 + inaddr.sin_addr.s_addr = htonl(INADDR_ANY); 1340 +#endif 1341 + } 1342 +#ifdef WITH_IPV6 1343 + inaddr.sin6_port = htons(iPort); 1344 + listener = socket(AF_INET6, SOCK_STREAM, 0); 1345 +#else 1346 + inaddr.sin_port = htons(iPort); 1347 + listener = socket(AF_INET, SOCK_STREAM, 0); 1348 +#endif 1349 + if( listener<0 ){ 1350 + fossil_fatal("Unable to create socket"); 1351 + } 1253 1352 1254 1353 /* if we can't terminate nicely, at least allow the socket to be reused */ 1255 1354 setsockopt(listener,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt)); 1355 + 1356 +#ifdef WITH_IPV6 1357 + opt=0; 1358 + setsockopt(listener, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt)); 1359 +#endif 1256 1360 1257 1361 if( bind(listener, (struct sockaddr*)&inaddr, sizeof(inaddr))<0 ){ 1258 1362 close(listener); 1259 1363 iPort++; 1260 1364 continue; 1261 1365 } 1366 +#endif 1262 1367 break; 1263 1368 } 1264 1369 if( iPort>mxPort ){ 1265 1370 if( mnPort==mxPort ){ 1266 1371 fossil_fatal("unable to open listening socket on ports %d", mnPort); 1267 1372 }else{ 1268 1373 fossil_fatal("unable to open listening socket on any"
Changes to src/http_socket.c.
133 133 ** 134 134 ** g.urlName Name of the server. Ex: www.fossil-scm.org 135 135 ** g.urlPort TCP/IP port to use. Ex: 80 136 136 ** 137 137 ** Return the number of errors. 138 138 */ 139 139 int socket_open(void){ 140 + int error = 0; 141 +#ifdef HAVE_GETADDRINFO 142 + struct addrinfo hints; 143 + struct addrinfo* res; 144 + struct addrinfo* i; 145 + char ip[INET6_ADDRSTRLEN]; 146 + void* addr; 147 + char* sPort; 148 + 149 + memset(&hints, 0, sizeof(struct addrinfo)); 150 + hints.ai_flags = AI_ADDRCONFIG; 151 +#ifdef WITH_IPV6 152 + hints.ai_family = PF_UNSPEC; 153 +#else 154 + hints.ai_family = PF_INET; 155 +#endif 156 + hints.ai_socktype = SOCK_STREAM; 157 + hints.ai_protocol = IPPROTO_TCP; 158 + 159 + sPort = mprintf("%d", g.urlPort); 160 + 161 + if(getaddrinfo(g.urlName, sPort, &hints, &res)) { 162 + socket_set_errmsg("can't resolve host name: %s", g.urlName); 163 + free(sPort); 164 + return 1; 165 + } 166 + for(i = res; i; i = i->ai_next) { 167 + iSocket = socket(i->ai_family, i->ai_socktype, i->ai_protocol); 168 + if(iSocket < 0) { 169 + continue; 170 + } 171 + if(connect(iSocket, i->ai_addr, i->ai_addrlen) < 0) { 172 + close(iSocket); 173 + iSocket = -1; 174 + continue; 175 + } 176 + if(!getnameinfo(i->ai_addr, i->ai_addrlen, ip, sizeof(ip), 177 + NULL, 0, NI_NUMERICHOST)) 178 + g.zIpAddr = mprintf("%s", ip); 179 + break; 180 + } 181 + if(iSocket == -1) { 182 + socket_set_errmsg("cannot connect to host %s:%s", g.urlName, sPort); 183 + error = 1; 184 + } 185 + free(sPort); 186 + freeaddrinfo(res); 187 +#else 140 188 static struct sockaddr_in addr; /* The server address */ 141 189 static int addrIsInit = 0; /* True once addr is initialized */ 142 190 143 191 socket_global_init(); 144 192 if( !addrIsInit ){ 145 193 addr.sin_family = AF_INET; 146 194 addr.sin_port = htons(g.urlPort); ................................................................................ 170 218 if( iSocket<0 ){ 171 219 socket_set_errmsg("cannot create a socket"); 172 220 return 1; 173 221 } 174 222 if( connect(iSocket,(struct sockaddr*)&addr,sizeof(addr))<0 ){ 175 223 socket_set_errmsg("cannot connect to host %s:%d", g.urlName, g.urlPort); 176 224 socket_close(); 177 - return 1; 225 + error = 1; 178 226 } 227 +#endif 179 228 #if !defined(_WIN32) 180 - signal(SIGPIPE, SIG_IGN); 229 + if(!error) 230 + signal(SIGPIPE, SIG_IGN); 181 231 #endif 182 - return 0; 232 + return error; 183 233 } 184 234 185 235 /* 186 236 ** Send content out over the open socket connection. 187 237 */ 188 238 size_t socket_send(void *NotUsed, void *pContent, size_t N){ 189 239 size_t sent;
Changes to src/login.c.
773 773 ** local login is disabled and if we are using HTTP and not HTTPS, 774 774 ** then there is no need to check user credentials. 775 775 ** 776 776 ** This feature allows the "fossil ui" command to give the user 777 777 ** full access rights without having to log in. 778 778 */ 779 779 zRemoteAddr = ipPrefix(zIpAddr = PD("REMOTE_ADDR","nil")); 780 - if( fossil_strcmp(zIpAddr, "127.0.0.1")==0 780 + if( ( fossil_strcmp(zIpAddr, "127.0.0.1")==0 || 781 + fossil_strcmp(zIpAddr, "::1")==0 ) 781 782 && g.useLocalauth 782 783 && db_get_int("localauth",0)==0 783 784 && P("HTTPS")==0 784 785 ){ 785 786 uid = db_int(0, "SELECT uid FROM user WHERE cap LIKE '%%s%%'"); 786 787 g.zLogin = db_text("?", "SELECT login FROM user WHERE uid=%d", uid); 787 788 zCap = "sx"; ................................................................................ 1112 1113 void login_anonymous_available(void){ 1113 1114 if( !g.perm.History && 1114 1115 db_exists("SELECT 1 FROM user" 1115 1116 " WHERE login='anonymous'" 1116 1117 " AND cap LIKE '%%h%%'") ){ 1117 1118 const char *zUrl = PD("REQUEST_URI", "index"); 1118 1119 @ <p>Many <span class="disabled">hyperlinks are disabled.</span><br /> 1119 - @ Use <a href="%s(g.zTop)/login?anon=1&g=%T(zUrl)">anonymous login</a> 1120 + @ Use <a href="%s(g.zTop)/login?anon=1&g=%T(g.zRoot)%T(zUrl)">anonymous login</a> 1120 1121 @ to enable hyperlinks.</p> 1121 1122 } 1122 1123 } 1123 1124 1124 1125 /* 1125 1126 ** While rendering a form, call this routine to add the Anti-CSRF token 1126 1127 ** as a hidden element of the form.
Changes to src/main.c.
116 116 int fQuiet; /* True if -quiet flag is present */ 117 117 int fHttpTrace; /* Trace outbound HTTP requests */ 118 118 int fSystemTrace; /* Trace calls to fossil_system(), --systemtrace */ 119 119 int fNoSync; /* Do not do an autosync even. --nosync */ 120 120 char *zPath; /* Name of webpage being served */ 121 121 char *zExtra; /* Extra path information past the webpage name */ 122 122 char *zBaseURL; /* Full text of the URL being served */ 123 + char *zRedirectBaseURL; /* Full text of the URL being served to be used in redirect */ 124 + char *zRoot; /* Repository web root */ 123 125 char *zTop; /* Parent directory of zPath */ 124 126 const char *zContentType; /* The content type of the input HTTP request */ 125 127 int iErrPriority; /* Priority of current error message */ 126 128 char *zErrMsg; /* Text of an error message */ 127 129 int sslNotAvailable; /* SSL is not available. Do not redirect to https: */ 128 130 Blob cgiIn; /* Input to an xfer www method */ 129 131 int cgiOutput; /* Write error and status messages to CGI */ ................................................................................ 1071 1073 void set_base_url(void){ 1072 1074 int i; 1073 1075 const char *zHost; 1074 1076 const char *zMode; 1075 1077 const char *zCur; 1076 1078 1077 1079 if( g.zBaseURL!=0 ) return; 1080 + if( g.zRoot==0 ) g.zRoot=""; 1078 1081 zHost = PD("HTTP_HOST",""); 1079 1082 zMode = PD("HTTPS","off"); 1080 1083 zCur = PD("SCRIPT_NAME","/"); 1081 1084 i = strlen(zCur); 1082 1085 while( i>0 && zCur[i-1]=='/' ) i--; 1083 1086 if( fossil_stricmp(zMode,"on")==0 ){ 1084 - g.zBaseURL = mprintf("https://%s%.*s", zHost, i, zCur); 1087 + g.zBaseURL = mprintf("https://%s%s%.*s", zHost, g.zRoot, i, zCur); 1085 1088 g.zTop = &g.zBaseURL[8+strlen(zHost)]; 1086 1089 }else{ 1087 - g.zBaseURL = mprintf("http://%s%.*s", zHost, i, zCur); 1090 + g.zBaseURL = mprintf("http://%s%s%.*s", zHost, g.zRoot, i, zCur); 1088 1091 g.zTop = &g.zBaseURL[7+strlen(zHost)]; 1089 1092 } 1090 1093 } 1091 1094 1092 1095 /* 1093 1096 ** Send an HTTP redirect back to the designated Index Page. 1094 1097 */ ................................................................................ 1659 1662 ** 1660 1663 ** Open a socket and begin listening and responding to HTTP requests on 1661 1664 ** TCP port 8080, or on any other TCP port defined by the -P or 1662 1665 ** --port option. The optional argument is the name of the repository. 1663 1666 ** The repository argument may be omitted if the working directory is 1664 1667 ** within an open checkout. 1665 1668 ** 1669 +** If HTTP requests are being reverse proxied to the fossil server, 1670 +** and in proxy server fossil is mapped at a virtual directory, then 1671 +** virtual directory can be specified using optional -R or --root option. 1672 +** 1666 1673 ** The "ui" command automatically starts a web browser after initializing 1667 1674 ** the web server. The "ui" command also binds to 127.0.0.1 and so will 1668 1675 ** only process HTTP traffic from the local machine. 1669 1676 ** 1670 1677 ** In the "server" command, the REPOSITORY can be a directory (aka folder) 1671 1678 ** that contains one or more respositories with names ending in ".fossil". 1672 1679 ** In that case, the first element of the URL is used to select among the ................................................................................ 1673 1680 ** various repositories. 1674 1681 ** 1675 1682 ** By default, the "ui" command provides full administrative access without 1676 1683 ** having to log in. This can be disabled by setting turning off the 1677 1684 ** "localauth" setting. Automatic login for the "server" command is available 1678 1685 ** if the --localauth option is present and the "localauth" setting is off 1679 1686 ** and the connection is from localhost. 1680 -** 1681 1687 ** Options: 1682 1688 ** --localauth enable automatic login for requests from localhost 1683 1689 ** -P|--port TCPPORT listen to request on port TCPPORT 1690 +** -R|--root ROOT map fossil at virtual directory ROOT in reverse 1691 +** proxying 1684 1692 ** --th-trace trace TH1 execution (for debugging purposes) 1685 1693 ** 1686 1694 ** See also: cgi, http, winsrv 1687 1695 */ 1688 1696 void cmd_webserver(void){ 1689 1697 int iPort, mxPort; /* Range of TCP ports allowed */ 1690 1698 const char *zPort; /* Value of the --port option */ ................................................................................ 1697 1705 #if defined(_WIN32) 1698 1706 const char *zStopperFile; /* Name of file used to terminate server */ 1699 1707 zStopperFile = find_option("stopper", 0, 1); 1700 1708 #endif 1701 1709 1702 1710 g.thTrace = find_option("th-trace", 0, 0)!=0; 1703 1711 g.useLocalauth = find_option("localauth", 0, 0)!=0; 1712 + g.zRoot = find_option("root", "R", 1); 1704 1713 if( g.thTrace ){ 1705 1714 blob_zero(&g.thLog); 1706 1715 } 1707 1716 zPort = find_option("port", "P", 1); 1708 1717 zNotFound = find_option("notfound", 0, 1); 1709 1718 if( g.argc!=2 && g.argc!=3 ) usage("?REPOSITORY?"); 1710 1719 isUiCmd = g.argv[1][0]=='u'; ................................................................................ 1734 1743 break; 1735 1744 } 1736 1745 } 1737 1746 } 1738 1747 #else 1739 1748 zBrowser = db_get("web-browser", "open"); 1740 1749 #endif 1741 - zBrowserCmd = mprintf("%s http://localhost:%%d/ &", zBrowser); 1750 + zBrowserCmd = mprintf("%s http://localhost:%%d/%s &", zBrowser, (g.zRoot==0?"":g.zRoot)); 1742 1751 } 1743 1752 db_close(1); 1744 1753 if( cgi_http_server(iPort, mxPort, zBrowserCmd, flags) ){ 1745 1754 fossil_fatal("unable to listen on TCP socket %d", iPort); 1746 1755 } 1747 1756 g.sslNotAvailable = 1; 1748 1757 g.httpIn = stdin;