Fossil SCM
Merge in all new development from trunk.
Commit
535cba9158f23dfd6a0c803c3be08ca8b4ddf1e2
Parent
ed20da74998324a…
34 files changed
+63
-7
+63
-7
+6
-1
+14
+14
-2
+1
-1
+1
-1
+1
-1
+1
-1
+1
-1
+18
-12
+16
-2
+16
-2
+12
-4
+71
-56
+2
-2
+14
-2
+9
-5
+9
-5
+21
-16
+76
-3
+1
-1
+2
-2
+127
-84
+13
+6
-6
+1
+2
+18
-47
+22
+4
-3
+214
-89
+9
-3
~
src/cgi.c
~
src/cgi.c
~
src/checkin.c
~
src/clone.c
~
src/clone.c
~
src/content.c
~
src/diff.c
~
src/glob.c
~
src/graph.c
~
src/json.c
~
src/json.c
~
src/login.c
~
src/main.c
~
src/main.c
~
src/manifest.c
~
src/sqlite3.c
~
src/sqlite3.h
~
src/style.c
~
src/sync.c
~
src/sync.c
~
src/timeline.c
~
src/winhttp.c
~
www/adding_code.wiki
~
www/embeddeddoc.wiki
~
www/fileformat.wiki
~
www/hacker-howto.wiki
~
www/index.wiki
~
www/mkindex.tcl
~
www/permutedindex.wiki
~
www/quickstart.wiki
~
www/scgi.wiki
~
www/selfhost.wiki
~
www/server.wiki
~
www/webui.wiki
+63
-7
| --- src/cgi.c | ||
| +++ src/cgi.c | ||
| @@ -812,10 +812,12 @@ | ||
| 812 | 812 | } |
| 813 | 813 | } |
| 814 | 814 | fputs(z, pLog); |
| 815 | 815 | } |
| 816 | 816 | |
| 817 | +/* Forward declaration */ | |
| 818 | +static NORETURN void malformed_request(const char *zMsg); | |
| 817 | 819 | |
| 818 | 820 | /* |
| 819 | 821 | ** Initialize the query parameter database. Information is pulled from |
| 820 | 822 | ** the QUERY_STRING environment variable (if it exists), from standard |
| 821 | 823 | ** input if there is POST data, and from HTTP_COOKIE. |
| @@ -822,15 +824,26 @@ | ||
| 822 | 824 | */ |
| 823 | 825 | void cgi_init(void){ |
| 824 | 826 | char *z; |
| 825 | 827 | const char *zType; |
| 826 | 828 | int len; |
| 829 | + const char *zRequestUri = cgi_parameter("REQUEST_URI",0); | |
| 830 | + const char *zScriptName = cgi_parameter("SCRIPT_NAME",0); | |
| 831 | + | |
| 827 | 832 | #ifdef FOSSIL_ENABLE_JSON |
| 828 | 833 | json_main_bootstrap(); |
| 829 | 834 | #endif |
| 830 | 835 | g.isHTTP = 1; |
| 831 | 836 | cgi_destination(CGI_BODY); |
| 837 | + if( zRequestUri==0 ) malformed_request("missing REQUEST_URI"); | |
| 838 | + if( zScriptName==0 ) malformed_request("missing SCRIPT_NAME"); | |
| 839 | + if( cgi_parameter("PATH_INFO",0)==0 ){ | |
| 840 | + int i, j; | |
| 841 | + for(i=0; zRequestUri[i]==zScriptName[i] && zRequestUri[i]; i++){} | |
| 842 | + for(j=i; zRequestUri[j] && zRequestUri[j]!='?'; j++){} | |
| 843 | + cgi_set_parameter("PATH_INFO", mprintf("%.*s", j-i, zRequestUri+i)); | |
| 844 | + } | |
| 832 | 845 | |
| 833 | 846 | z = (char*)P("HTTP_COOKIE"); |
| 834 | 847 | if( z ){ |
| 835 | 848 | z = mprintf("%s",z); |
| 836 | 849 | add_param_list(z, ';'); |
| @@ -1092,14 +1105,14 @@ | ||
| 1092 | 1105 | |
| 1093 | 1106 | |
| 1094 | 1107 | /* |
| 1095 | 1108 | ** Send a reply indicating that the HTTP request was malformed |
| 1096 | 1109 | */ |
| 1097 | -static NORETURN void malformed_request(void){ | |
| 1110 | +static NORETURN void malformed_request(const char *zMsg){ | |
| 1098 | 1111 | cgi_set_status(501, "Not Implemented"); |
| 1099 | 1112 | cgi_printf( |
| 1100 | - "<html><body>Unrecognized HTTP Request</body></html>\n" | |
| 1113 | + "<html><body><p>Bad Request: %s</p></body></html>\n", zMsg | |
| 1101 | 1114 | ); |
| 1102 | 1115 | cgi_reply(); |
| 1103 | 1116 | fossil_exit(0); |
| 1104 | 1117 | } |
| 1105 | 1118 | |
| @@ -1187,29 +1200,30 @@ | ||
| 1187 | 1200 | struct sockaddr_in remoteName; |
| 1188 | 1201 | socklen_t size = sizeof(struct sockaddr_in); |
| 1189 | 1202 | char zLine[2000]; /* A single line of input. */ |
| 1190 | 1203 | g.fullHttpReply = 1; |
| 1191 | 1204 | if( fgets(zLine, sizeof(zLine),g.httpIn)==0 ){ |
| 1192 | - malformed_request(); | |
| 1205 | + malformed_request("missing HTTP header"); | |
| 1193 | 1206 | } |
| 1194 | 1207 | blob_append(&g.httpHeader, zLine, -1); |
| 1195 | 1208 | cgi_trace(zLine); |
| 1196 | 1209 | zToken = extract_token(zLine, &z); |
| 1197 | 1210 | if( zToken==0 ){ |
| 1198 | - malformed_request(); | |
| 1211 | + malformed_request("malformed HTTP header"); | |
| 1199 | 1212 | } |
| 1200 | 1213 | if( fossil_strcmp(zToken,"GET")!=0 && fossil_strcmp(zToken,"POST")!=0 |
| 1201 | 1214 | && fossil_strcmp(zToken,"HEAD")!=0 ){ |
| 1202 | - malformed_request(); | |
| 1215 | + malformed_request("unsupported HTTP method"); | |
| 1203 | 1216 | } |
| 1204 | 1217 | cgi_setenv("GATEWAY_INTERFACE","CGI/1.0"); |
| 1205 | 1218 | cgi_setenv("REQUEST_METHOD",zToken); |
| 1206 | 1219 | zToken = extract_token(z, &z); |
| 1207 | 1220 | if( zToken==0 ){ |
| 1208 | - malformed_request(); | |
| 1221 | + malformed_request("malformed URL in HTTP header"); | |
| 1209 | 1222 | } |
| 1210 | 1223 | cgi_setenv("REQUEST_URI", zToken); |
| 1224 | + cgi_setenv("SCRIPT_NAME", ""); | |
| 1211 | 1225 | for(i=0; zToken[i] && zToken[i]!='?'; i++){} |
| 1212 | 1226 | if( zToken[i] ) zToken[i++] = 0; |
| 1213 | 1227 | cgi_setenv("PATH_INFO", zToken); |
| 1214 | 1228 | cgi_setenv("QUERY_STRING", &zToken[i]); |
| 1215 | 1229 | if( zIpAddr==0 && |
| @@ -1269,16 +1283,57 @@ | ||
| 1269 | 1283 | } |
| 1270 | 1284 | } |
| 1271 | 1285 | cgi_init(); |
| 1272 | 1286 | cgi_trace(0); |
| 1273 | 1287 | } |
| 1288 | + | |
| 1289 | +/* | |
| 1290 | +** This routine handles a single SCGI request which is coming in on | |
| 1291 | +** g.httpIn and which replies on g.httpOut | |
| 1292 | +** | |
| 1293 | +** The SCGI request is read from g.httpIn and is used to initialize | |
| 1294 | +** entries in the cgi_parameter() hash, as if those entries were | |
| 1295 | +** environment variables. A call to cgi_init() completes | |
| 1296 | +** the setup. Once all the setup is finished, this procedure returns | |
| 1297 | +** and subsequent code handles the actual generation of the webpage. | |
| 1298 | +*/ | |
| 1299 | +void cgi_handle_scgi_request(void){ | |
| 1300 | + char *zHdr; | |
| 1301 | + char *zToFree; | |
| 1302 | + int nHdr = 0; | |
| 1303 | + int nRead; | |
| 1304 | + int n, m; | |
| 1305 | + char c; | |
| 1306 | + | |
| 1307 | + while( (c = fgetc(g.httpIn))!=EOF && fossil_isdigit(c) ){ | |
| 1308 | + nHdr = nHdr*10 + c - '0'; | |
| 1309 | + } | |
| 1310 | + if( nHdr<16 ) malformed_request("SCGI header too short"); | |
| 1311 | + zToFree = zHdr = fossil_malloc(nHdr); | |
| 1312 | + nRead = (int)fread(zHdr, 1, nHdr, g.httpIn); | |
| 1313 | + if( nRead<nHdr ) malformed_request("cannot read entire SCGI header"); | |
| 1314 | + nHdr = nRead; | |
| 1315 | + while( nHdr ){ | |
| 1316 | + for(n=0; n<nHdr && zHdr[n]; n++){} | |
| 1317 | + for(m=n+1; m<nHdr && zHdr[m]; m++){} | |
| 1318 | + if( m>=nHdr ) malformed_request("SCGI header formatting error"); | |
| 1319 | + cgi_set_parameter(zHdr, zHdr+n+1); | |
| 1320 | + zHdr += m+1; | |
| 1321 | + nHdr -= m+1; | |
| 1322 | + } | |
| 1323 | + fossil_free(zToFree); | |
| 1324 | + fgetc(g.httpIn); /* Read past the "," separating header from content */ | |
| 1325 | + cgi_init(); | |
| 1326 | +} | |
| 1327 | + | |
| 1274 | 1328 | |
| 1275 | 1329 | #if INTERFACE |
| 1276 | 1330 | /* |
| 1277 | 1331 | ** Bitmap values for the flags parameter to cgi_http_server(). |
| 1278 | 1332 | */ |
| 1279 | 1333 | #define HTTP_SERVER_LOCALHOST 0x0001 /* Bind to 127.0.0.1 only */ |
| 1334 | +#define HTTP_SERVER_SCGI 0x0002 /* SCGI instead of HTTP */ | |
| 1280 | 1335 | |
| 1281 | 1336 | #endif /* INTERFACE */ |
| 1282 | 1337 | |
| 1283 | 1338 | /* |
| 1284 | 1339 | ** Maximum number of child processes that we can have running |
| @@ -1356,11 +1411,12 @@ | ||
| 1356 | 1411 | } |
| 1357 | 1412 | } |
| 1358 | 1413 | if( iPort>mxPort ) return 1; |
| 1359 | 1414 | listen(listener,10); |
| 1360 | 1415 | if( iPort>mnPort ){ |
| 1361 | - fossil_print("Listening for HTTP requests on TCP port %d\n", iPort); | |
| 1416 | + fossil_print("Listening for %s requests on TCP port %d\n", | |
| 1417 | + (flags & HTTP_SERVER_SCGI)!=0?"SCGI":"HTTP", iPort); | |
| 1362 | 1418 | fflush(stdout); |
| 1363 | 1419 | } |
| 1364 | 1420 | if( zBrowser ){ |
| 1365 | 1421 | zBrowser = mprintf(zBrowser, iPort); |
| 1366 | 1422 | if( system(zBrowser)<0 ){ |
| 1367 | 1423 |
| --- src/cgi.c | |
| +++ src/cgi.c | |
| @@ -812,10 +812,12 @@ | |
| 812 | } |
| 813 | } |
| 814 | fputs(z, pLog); |
| 815 | } |
| 816 | |
| 817 | |
| 818 | /* |
| 819 | ** Initialize the query parameter database. Information is pulled from |
| 820 | ** the QUERY_STRING environment variable (if it exists), from standard |
| 821 | ** input if there is POST data, and from HTTP_COOKIE. |
| @@ -822,15 +824,26 @@ | |
| 822 | */ |
| 823 | void cgi_init(void){ |
| 824 | char *z; |
| 825 | const char *zType; |
| 826 | int len; |
| 827 | #ifdef FOSSIL_ENABLE_JSON |
| 828 | json_main_bootstrap(); |
| 829 | #endif |
| 830 | g.isHTTP = 1; |
| 831 | cgi_destination(CGI_BODY); |
| 832 | |
| 833 | z = (char*)P("HTTP_COOKIE"); |
| 834 | if( z ){ |
| 835 | z = mprintf("%s",z); |
| 836 | add_param_list(z, ';'); |
| @@ -1092,14 +1105,14 @@ | |
| 1092 | |
| 1093 | |
| 1094 | /* |
| 1095 | ** Send a reply indicating that the HTTP request was malformed |
| 1096 | */ |
| 1097 | static NORETURN void malformed_request(void){ |
| 1098 | cgi_set_status(501, "Not Implemented"); |
| 1099 | cgi_printf( |
| 1100 | "<html><body>Unrecognized HTTP Request</body></html>\n" |
| 1101 | ); |
| 1102 | cgi_reply(); |
| 1103 | fossil_exit(0); |
| 1104 | } |
| 1105 | |
| @@ -1187,29 +1200,30 @@ | |
| 1187 | struct sockaddr_in remoteName; |
| 1188 | socklen_t size = sizeof(struct sockaddr_in); |
| 1189 | char zLine[2000]; /* A single line of input. */ |
| 1190 | g.fullHttpReply = 1; |
| 1191 | if( fgets(zLine, sizeof(zLine),g.httpIn)==0 ){ |
| 1192 | malformed_request(); |
| 1193 | } |
| 1194 | blob_append(&g.httpHeader, zLine, -1); |
| 1195 | cgi_trace(zLine); |
| 1196 | zToken = extract_token(zLine, &z); |
| 1197 | if( zToken==0 ){ |
| 1198 | malformed_request(); |
| 1199 | } |
| 1200 | if( fossil_strcmp(zToken,"GET")!=0 && fossil_strcmp(zToken,"POST")!=0 |
| 1201 | && fossil_strcmp(zToken,"HEAD")!=0 ){ |
| 1202 | malformed_request(); |
| 1203 | } |
| 1204 | cgi_setenv("GATEWAY_INTERFACE","CGI/1.0"); |
| 1205 | cgi_setenv("REQUEST_METHOD",zToken); |
| 1206 | zToken = extract_token(z, &z); |
| 1207 | if( zToken==0 ){ |
| 1208 | malformed_request(); |
| 1209 | } |
| 1210 | cgi_setenv("REQUEST_URI", zToken); |
| 1211 | for(i=0; zToken[i] && zToken[i]!='?'; i++){} |
| 1212 | if( zToken[i] ) zToken[i++] = 0; |
| 1213 | cgi_setenv("PATH_INFO", zToken); |
| 1214 | cgi_setenv("QUERY_STRING", &zToken[i]); |
| 1215 | if( zIpAddr==0 && |
| @@ -1269,16 +1283,57 @@ | |
| 1269 | } |
| 1270 | } |
| 1271 | cgi_init(); |
| 1272 | cgi_trace(0); |
| 1273 | } |
| 1274 | |
| 1275 | #if INTERFACE |
| 1276 | /* |
| 1277 | ** Bitmap values for the flags parameter to cgi_http_server(). |
| 1278 | */ |
| 1279 | #define HTTP_SERVER_LOCALHOST 0x0001 /* Bind to 127.0.0.1 only */ |
| 1280 | |
| 1281 | #endif /* INTERFACE */ |
| 1282 | |
| 1283 | /* |
| 1284 | ** Maximum number of child processes that we can have running |
| @@ -1356,11 +1411,12 @@ | |
| 1356 | } |
| 1357 | } |
| 1358 | if( iPort>mxPort ) return 1; |
| 1359 | listen(listener,10); |
| 1360 | if( iPort>mnPort ){ |
| 1361 | fossil_print("Listening for HTTP requests on TCP port %d\n", iPort); |
| 1362 | fflush(stdout); |
| 1363 | } |
| 1364 | if( zBrowser ){ |
| 1365 | zBrowser = mprintf(zBrowser, iPort); |
| 1366 | if( system(zBrowser)<0 ){ |
| 1367 |
| --- src/cgi.c | |
| +++ src/cgi.c | |
| @@ -812,10 +812,12 @@ | |
| 812 | } |
| 813 | } |
| 814 | fputs(z, pLog); |
| 815 | } |
| 816 | |
| 817 | /* Forward declaration */ |
| 818 | static NORETURN void malformed_request(const char *zMsg); |
| 819 | |
| 820 | /* |
| 821 | ** Initialize the query parameter database. Information is pulled from |
| 822 | ** the QUERY_STRING environment variable (if it exists), from standard |
| 823 | ** input if there is POST data, and from HTTP_COOKIE. |
| @@ -822,15 +824,26 @@ | |
| 824 | */ |
| 825 | void cgi_init(void){ |
| 826 | char *z; |
| 827 | const char *zType; |
| 828 | int len; |
| 829 | const char *zRequestUri = cgi_parameter("REQUEST_URI",0); |
| 830 | const char *zScriptName = cgi_parameter("SCRIPT_NAME",0); |
| 831 | |
| 832 | #ifdef FOSSIL_ENABLE_JSON |
| 833 | json_main_bootstrap(); |
| 834 | #endif |
| 835 | g.isHTTP = 1; |
| 836 | cgi_destination(CGI_BODY); |
| 837 | if( zRequestUri==0 ) malformed_request("missing REQUEST_URI"); |
| 838 | if( zScriptName==0 ) malformed_request("missing SCRIPT_NAME"); |
| 839 | if( cgi_parameter("PATH_INFO",0)==0 ){ |
| 840 | int i, j; |
| 841 | for(i=0; zRequestUri[i]==zScriptName[i] && zRequestUri[i]; i++){} |
| 842 | for(j=i; zRequestUri[j] && zRequestUri[j]!='?'; j++){} |
| 843 | cgi_set_parameter("PATH_INFO", mprintf("%.*s", j-i, zRequestUri+i)); |
| 844 | } |
| 845 | |
| 846 | z = (char*)P("HTTP_COOKIE"); |
| 847 | if( z ){ |
| 848 | z = mprintf("%s",z); |
| 849 | add_param_list(z, ';'); |
| @@ -1092,14 +1105,14 @@ | |
| 1105 | |
| 1106 | |
| 1107 | /* |
| 1108 | ** Send a reply indicating that the HTTP request was malformed |
| 1109 | */ |
| 1110 | static NORETURN void malformed_request(const char *zMsg){ |
| 1111 | cgi_set_status(501, "Not Implemented"); |
| 1112 | cgi_printf( |
| 1113 | "<html><body><p>Bad Request: %s</p></body></html>\n", zMsg |
| 1114 | ); |
| 1115 | cgi_reply(); |
| 1116 | fossil_exit(0); |
| 1117 | } |
| 1118 | |
| @@ -1187,29 +1200,30 @@ | |
| 1200 | struct sockaddr_in remoteName; |
| 1201 | socklen_t size = sizeof(struct sockaddr_in); |
| 1202 | char zLine[2000]; /* A single line of input. */ |
| 1203 | g.fullHttpReply = 1; |
| 1204 | if( fgets(zLine, sizeof(zLine),g.httpIn)==0 ){ |
| 1205 | malformed_request("missing HTTP header"); |
| 1206 | } |
| 1207 | blob_append(&g.httpHeader, zLine, -1); |
| 1208 | cgi_trace(zLine); |
| 1209 | zToken = extract_token(zLine, &z); |
| 1210 | if( zToken==0 ){ |
| 1211 | malformed_request("malformed HTTP header"); |
| 1212 | } |
| 1213 | if( fossil_strcmp(zToken,"GET")!=0 && fossil_strcmp(zToken,"POST")!=0 |
| 1214 | && fossil_strcmp(zToken,"HEAD")!=0 ){ |
| 1215 | malformed_request("unsupported HTTP method"); |
| 1216 | } |
| 1217 | cgi_setenv("GATEWAY_INTERFACE","CGI/1.0"); |
| 1218 | cgi_setenv("REQUEST_METHOD",zToken); |
| 1219 | zToken = extract_token(z, &z); |
| 1220 | if( zToken==0 ){ |
| 1221 | malformed_request("malformed URL in HTTP header"); |
| 1222 | } |
| 1223 | cgi_setenv("REQUEST_URI", zToken); |
| 1224 | cgi_setenv("SCRIPT_NAME", ""); |
| 1225 | for(i=0; zToken[i] && zToken[i]!='?'; i++){} |
| 1226 | if( zToken[i] ) zToken[i++] = 0; |
| 1227 | cgi_setenv("PATH_INFO", zToken); |
| 1228 | cgi_setenv("QUERY_STRING", &zToken[i]); |
| 1229 | if( zIpAddr==0 && |
| @@ -1269,16 +1283,57 @@ | |
| 1283 | } |
| 1284 | } |
| 1285 | cgi_init(); |
| 1286 | cgi_trace(0); |
| 1287 | } |
| 1288 | |
| 1289 | /* |
| 1290 | ** This routine handles a single SCGI request which is coming in on |
| 1291 | ** g.httpIn and which replies on g.httpOut |
| 1292 | ** |
| 1293 | ** The SCGI request is read from g.httpIn and is used to initialize |
| 1294 | ** entries in the cgi_parameter() hash, as if those entries were |
| 1295 | ** environment variables. A call to cgi_init() completes |
| 1296 | ** the setup. Once all the setup is finished, this procedure returns |
| 1297 | ** and subsequent code handles the actual generation of the webpage. |
| 1298 | */ |
| 1299 | void cgi_handle_scgi_request(void){ |
| 1300 | char *zHdr; |
| 1301 | char *zToFree; |
| 1302 | int nHdr = 0; |
| 1303 | int nRead; |
| 1304 | int n, m; |
| 1305 | char c; |
| 1306 | |
| 1307 | while( (c = fgetc(g.httpIn))!=EOF && fossil_isdigit(c) ){ |
| 1308 | nHdr = nHdr*10 + c - '0'; |
| 1309 | } |
| 1310 | if( nHdr<16 ) malformed_request("SCGI header too short"); |
| 1311 | zToFree = zHdr = fossil_malloc(nHdr); |
| 1312 | nRead = (int)fread(zHdr, 1, nHdr, g.httpIn); |
| 1313 | if( nRead<nHdr ) malformed_request("cannot read entire SCGI header"); |
| 1314 | nHdr = nRead; |
| 1315 | while( nHdr ){ |
| 1316 | for(n=0; n<nHdr && zHdr[n]; n++){} |
| 1317 | for(m=n+1; m<nHdr && zHdr[m]; m++){} |
| 1318 | if( m>=nHdr ) malformed_request("SCGI header formatting error"); |
| 1319 | cgi_set_parameter(zHdr, zHdr+n+1); |
| 1320 | zHdr += m+1; |
| 1321 | nHdr -= m+1; |
| 1322 | } |
| 1323 | fossil_free(zToFree); |
| 1324 | fgetc(g.httpIn); /* Read past the "," separating header from content */ |
| 1325 | cgi_init(); |
| 1326 | } |
| 1327 | |
| 1328 | |
| 1329 | #if INTERFACE |
| 1330 | /* |
| 1331 | ** Bitmap values for the flags parameter to cgi_http_server(). |
| 1332 | */ |
| 1333 | #define HTTP_SERVER_LOCALHOST 0x0001 /* Bind to 127.0.0.1 only */ |
| 1334 | #define HTTP_SERVER_SCGI 0x0002 /* SCGI instead of HTTP */ |
| 1335 | |
| 1336 | #endif /* INTERFACE */ |
| 1337 | |
| 1338 | /* |
| 1339 | ** Maximum number of child processes that we can have running |
| @@ -1356,11 +1411,12 @@ | |
| 1411 | } |
| 1412 | } |
| 1413 | if( iPort>mxPort ) return 1; |
| 1414 | listen(listener,10); |
| 1415 | if( iPort>mnPort ){ |
| 1416 | fossil_print("Listening for %s requests on TCP port %d\n", |
| 1417 | (flags & HTTP_SERVER_SCGI)!=0?"SCGI":"HTTP", iPort); |
| 1418 | fflush(stdout); |
| 1419 | } |
| 1420 | if( zBrowser ){ |
| 1421 | zBrowser = mprintf(zBrowser, iPort); |
| 1422 | if( system(zBrowser)<0 ){ |
| 1423 |
+63
-7
| --- src/cgi.c | ||
| +++ src/cgi.c | ||
| @@ -812,10 +812,12 @@ | ||
| 812 | 812 | } |
| 813 | 813 | } |
| 814 | 814 | fputs(z, pLog); |
| 815 | 815 | } |
| 816 | 816 | |
| 817 | +/* Forward declaration */ | |
| 818 | +static NORETURN void malformed_request(const char *zMsg); | |
| 817 | 819 | |
| 818 | 820 | /* |
| 819 | 821 | ** Initialize the query parameter database. Information is pulled from |
| 820 | 822 | ** the QUERY_STRING environment variable (if it exists), from standard |
| 821 | 823 | ** input if there is POST data, and from HTTP_COOKIE. |
| @@ -822,15 +824,26 @@ | ||
| 822 | 824 | */ |
| 823 | 825 | void cgi_init(void){ |
| 824 | 826 | char *z; |
| 825 | 827 | const char *zType; |
| 826 | 828 | int len; |
| 829 | + const char *zRequestUri = cgi_parameter("REQUEST_URI",0); | |
| 830 | + const char *zScriptName = cgi_parameter("SCRIPT_NAME",0); | |
| 831 | + | |
| 827 | 832 | #ifdef FOSSIL_ENABLE_JSON |
| 828 | 833 | json_main_bootstrap(); |
| 829 | 834 | #endif |
| 830 | 835 | g.isHTTP = 1; |
| 831 | 836 | cgi_destination(CGI_BODY); |
| 837 | + if( zRequestUri==0 ) malformed_request("missing REQUEST_URI"); | |
| 838 | + if( zScriptName==0 ) malformed_request("missing SCRIPT_NAME"); | |
| 839 | + if( cgi_parameter("PATH_INFO",0)==0 ){ | |
| 840 | + int i, j; | |
| 841 | + for(i=0; zRequestUri[i]==zScriptName[i] && zRequestUri[i]; i++){} | |
| 842 | + for(j=i; zRequestUri[j] && zRequestUri[j]!='?'; j++){} | |
| 843 | + cgi_set_parameter("PATH_INFO", mprintf("%.*s", j-i, zRequestUri+i)); | |
| 844 | + } | |
| 832 | 845 | |
| 833 | 846 | z = (char*)P("HTTP_COOKIE"); |
| 834 | 847 | if( z ){ |
| 835 | 848 | z = mprintf("%s",z); |
| 836 | 849 | add_param_list(z, ';'); |
| @@ -1092,14 +1105,14 @@ | ||
| 1092 | 1105 | |
| 1093 | 1106 | |
| 1094 | 1107 | /* |
| 1095 | 1108 | ** Send a reply indicating that the HTTP request was malformed |
| 1096 | 1109 | */ |
| 1097 | -static NORETURN void malformed_request(void){ | |
| 1110 | +static NORETURN void malformed_request(const char *zMsg){ | |
| 1098 | 1111 | cgi_set_status(501, "Not Implemented"); |
| 1099 | 1112 | cgi_printf( |
| 1100 | - "<html><body>Unrecognized HTTP Request</body></html>\n" | |
| 1113 | + "<html><body><p>Bad Request: %s</p></body></html>\n", zMsg | |
| 1101 | 1114 | ); |
| 1102 | 1115 | cgi_reply(); |
| 1103 | 1116 | fossil_exit(0); |
| 1104 | 1117 | } |
| 1105 | 1118 | |
| @@ -1187,29 +1200,30 @@ | ||
| 1187 | 1200 | struct sockaddr_in remoteName; |
| 1188 | 1201 | socklen_t size = sizeof(struct sockaddr_in); |
| 1189 | 1202 | char zLine[2000]; /* A single line of input. */ |
| 1190 | 1203 | g.fullHttpReply = 1; |
| 1191 | 1204 | if( fgets(zLine, sizeof(zLine),g.httpIn)==0 ){ |
| 1192 | - malformed_request(); | |
| 1205 | + malformed_request("missing HTTP header"); | |
| 1193 | 1206 | } |
| 1194 | 1207 | blob_append(&g.httpHeader, zLine, -1); |
| 1195 | 1208 | cgi_trace(zLine); |
| 1196 | 1209 | zToken = extract_token(zLine, &z); |
| 1197 | 1210 | if( zToken==0 ){ |
| 1198 | - malformed_request(); | |
| 1211 | + malformed_request("malformed HTTP header"); | |
| 1199 | 1212 | } |
| 1200 | 1213 | if( fossil_strcmp(zToken,"GET")!=0 && fossil_strcmp(zToken,"POST")!=0 |
| 1201 | 1214 | && fossil_strcmp(zToken,"HEAD")!=0 ){ |
| 1202 | - malformed_request(); | |
| 1215 | + malformed_request("unsupported HTTP method"); | |
| 1203 | 1216 | } |
| 1204 | 1217 | cgi_setenv("GATEWAY_INTERFACE","CGI/1.0"); |
| 1205 | 1218 | cgi_setenv("REQUEST_METHOD",zToken); |
| 1206 | 1219 | zToken = extract_token(z, &z); |
| 1207 | 1220 | if( zToken==0 ){ |
| 1208 | - malformed_request(); | |
| 1221 | + malformed_request("malformed URL in HTTP header"); | |
| 1209 | 1222 | } |
| 1210 | 1223 | cgi_setenv("REQUEST_URI", zToken); |
| 1224 | + cgi_setenv("SCRIPT_NAME", ""); | |
| 1211 | 1225 | for(i=0; zToken[i] && zToken[i]!='?'; i++){} |
| 1212 | 1226 | if( zToken[i] ) zToken[i++] = 0; |
| 1213 | 1227 | cgi_setenv("PATH_INFO", zToken); |
| 1214 | 1228 | cgi_setenv("QUERY_STRING", &zToken[i]); |
| 1215 | 1229 | if( zIpAddr==0 && |
| @@ -1269,16 +1283,57 @@ | ||
| 1269 | 1283 | } |
| 1270 | 1284 | } |
| 1271 | 1285 | cgi_init(); |
| 1272 | 1286 | cgi_trace(0); |
| 1273 | 1287 | } |
| 1288 | + | |
| 1289 | +/* | |
| 1290 | +** This routine handles a single SCGI request which is coming in on | |
| 1291 | +** g.httpIn and which replies on g.httpOut | |
| 1292 | +** | |
| 1293 | +** The SCGI request is read from g.httpIn and is used to initialize | |
| 1294 | +** entries in the cgi_parameter() hash, as if those entries were | |
| 1295 | +** environment variables. A call to cgi_init() completes | |
| 1296 | +** the setup. Once all the setup is finished, this procedure returns | |
| 1297 | +** and subsequent code handles the actual generation of the webpage. | |
| 1298 | +*/ | |
| 1299 | +void cgi_handle_scgi_request(void){ | |
| 1300 | + char *zHdr; | |
| 1301 | + char *zToFree; | |
| 1302 | + int nHdr = 0; | |
| 1303 | + int nRead; | |
| 1304 | + int n, m; | |
| 1305 | + char c; | |
| 1306 | + | |
| 1307 | + while( (c = fgetc(g.httpIn))!=EOF && fossil_isdigit(c) ){ | |
| 1308 | + nHdr = nHdr*10 + c - '0'; | |
| 1309 | + } | |
| 1310 | + if( nHdr<16 ) malformed_request("SCGI header too short"); | |
| 1311 | + zToFree = zHdr = fossil_malloc(nHdr); | |
| 1312 | + nRead = (int)fread(zHdr, 1, nHdr, g.httpIn); | |
| 1313 | + if( nRead<nHdr ) malformed_request("cannot read entire SCGI header"); | |
| 1314 | + nHdr = nRead; | |
| 1315 | + while( nHdr ){ | |
| 1316 | + for(n=0; n<nHdr && zHdr[n]; n++){} | |
| 1317 | + for(m=n+1; m<nHdr && zHdr[m]; m++){} | |
| 1318 | + if( m>=nHdr ) malformed_request("SCGI header formatting error"); | |
| 1319 | + cgi_set_parameter(zHdr, zHdr+n+1); | |
| 1320 | + zHdr += m+1; | |
| 1321 | + nHdr -= m+1; | |
| 1322 | + } | |
| 1323 | + fossil_free(zToFree); | |
| 1324 | + fgetc(g.httpIn); /* Read past the "," separating header from content */ | |
| 1325 | + cgi_init(); | |
| 1326 | +} | |
| 1327 | + | |
| 1274 | 1328 | |
| 1275 | 1329 | #if INTERFACE |
| 1276 | 1330 | /* |
| 1277 | 1331 | ** Bitmap values for the flags parameter to cgi_http_server(). |
| 1278 | 1332 | */ |
| 1279 | 1333 | #define HTTP_SERVER_LOCALHOST 0x0001 /* Bind to 127.0.0.1 only */ |
| 1334 | +#define HTTP_SERVER_SCGI 0x0002 /* SCGI instead of HTTP */ | |
| 1280 | 1335 | |
| 1281 | 1336 | #endif /* INTERFACE */ |
| 1282 | 1337 | |
| 1283 | 1338 | /* |
| 1284 | 1339 | ** Maximum number of child processes that we can have running |
| @@ -1356,11 +1411,12 @@ | ||
| 1356 | 1411 | } |
| 1357 | 1412 | } |
| 1358 | 1413 | if( iPort>mxPort ) return 1; |
| 1359 | 1414 | listen(listener,10); |
| 1360 | 1415 | if( iPort>mnPort ){ |
| 1361 | - fossil_print("Listening for HTTP requests on TCP port %d\n", iPort); | |
| 1416 | + fossil_print("Listening for %s requests on TCP port %d\n", | |
| 1417 | + (flags & HTTP_SERVER_SCGI)!=0?"SCGI":"HTTP", iPort); | |
| 1362 | 1418 | fflush(stdout); |
| 1363 | 1419 | } |
| 1364 | 1420 | if( zBrowser ){ |
| 1365 | 1421 | zBrowser = mprintf(zBrowser, iPort); |
| 1366 | 1422 | if( system(zBrowser)<0 ){ |
| 1367 | 1423 |
| --- src/cgi.c | |
| +++ src/cgi.c | |
| @@ -812,10 +812,12 @@ | |
| 812 | } |
| 813 | } |
| 814 | fputs(z, pLog); |
| 815 | } |
| 816 | |
| 817 | |
| 818 | /* |
| 819 | ** Initialize the query parameter database. Information is pulled from |
| 820 | ** the QUERY_STRING environment variable (if it exists), from standard |
| 821 | ** input if there is POST data, and from HTTP_COOKIE. |
| @@ -822,15 +824,26 @@ | |
| 822 | */ |
| 823 | void cgi_init(void){ |
| 824 | char *z; |
| 825 | const char *zType; |
| 826 | int len; |
| 827 | #ifdef FOSSIL_ENABLE_JSON |
| 828 | json_main_bootstrap(); |
| 829 | #endif |
| 830 | g.isHTTP = 1; |
| 831 | cgi_destination(CGI_BODY); |
| 832 | |
| 833 | z = (char*)P("HTTP_COOKIE"); |
| 834 | if( z ){ |
| 835 | z = mprintf("%s",z); |
| 836 | add_param_list(z, ';'); |
| @@ -1092,14 +1105,14 @@ | |
| 1092 | |
| 1093 | |
| 1094 | /* |
| 1095 | ** Send a reply indicating that the HTTP request was malformed |
| 1096 | */ |
| 1097 | static NORETURN void malformed_request(void){ |
| 1098 | cgi_set_status(501, "Not Implemented"); |
| 1099 | cgi_printf( |
| 1100 | "<html><body>Unrecognized HTTP Request</body></html>\n" |
| 1101 | ); |
| 1102 | cgi_reply(); |
| 1103 | fossil_exit(0); |
| 1104 | } |
| 1105 | |
| @@ -1187,29 +1200,30 @@ | |
| 1187 | struct sockaddr_in remoteName; |
| 1188 | socklen_t size = sizeof(struct sockaddr_in); |
| 1189 | char zLine[2000]; /* A single line of input. */ |
| 1190 | g.fullHttpReply = 1; |
| 1191 | if( fgets(zLine, sizeof(zLine),g.httpIn)==0 ){ |
| 1192 | malformed_request(); |
| 1193 | } |
| 1194 | blob_append(&g.httpHeader, zLine, -1); |
| 1195 | cgi_trace(zLine); |
| 1196 | zToken = extract_token(zLine, &z); |
| 1197 | if( zToken==0 ){ |
| 1198 | malformed_request(); |
| 1199 | } |
| 1200 | if( fossil_strcmp(zToken,"GET")!=0 && fossil_strcmp(zToken,"POST")!=0 |
| 1201 | && fossil_strcmp(zToken,"HEAD")!=0 ){ |
| 1202 | malformed_request(); |
| 1203 | } |
| 1204 | cgi_setenv("GATEWAY_INTERFACE","CGI/1.0"); |
| 1205 | cgi_setenv("REQUEST_METHOD",zToken); |
| 1206 | zToken = extract_token(z, &z); |
| 1207 | if( zToken==0 ){ |
| 1208 | malformed_request(); |
| 1209 | } |
| 1210 | cgi_setenv("REQUEST_URI", zToken); |
| 1211 | for(i=0; zToken[i] && zToken[i]!='?'; i++){} |
| 1212 | if( zToken[i] ) zToken[i++] = 0; |
| 1213 | cgi_setenv("PATH_INFO", zToken); |
| 1214 | cgi_setenv("QUERY_STRING", &zToken[i]); |
| 1215 | if( zIpAddr==0 && |
| @@ -1269,16 +1283,57 @@ | |
| 1269 | } |
| 1270 | } |
| 1271 | cgi_init(); |
| 1272 | cgi_trace(0); |
| 1273 | } |
| 1274 | |
| 1275 | #if INTERFACE |
| 1276 | /* |
| 1277 | ** Bitmap values for the flags parameter to cgi_http_server(). |
| 1278 | */ |
| 1279 | #define HTTP_SERVER_LOCALHOST 0x0001 /* Bind to 127.0.0.1 only */ |
| 1280 | |
| 1281 | #endif /* INTERFACE */ |
| 1282 | |
| 1283 | /* |
| 1284 | ** Maximum number of child processes that we can have running |
| @@ -1356,11 +1411,12 @@ | |
| 1356 | } |
| 1357 | } |
| 1358 | if( iPort>mxPort ) return 1; |
| 1359 | listen(listener,10); |
| 1360 | if( iPort>mnPort ){ |
| 1361 | fossil_print("Listening for HTTP requests on TCP port %d\n", iPort); |
| 1362 | fflush(stdout); |
| 1363 | } |
| 1364 | if( zBrowser ){ |
| 1365 | zBrowser = mprintf(zBrowser, iPort); |
| 1366 | if( system(zBrowser)<0 ){ |
| 1367 |
| --- src/cgi.c | |
| +++ src/cgi.c | |
| @@ -812,10 +812,12 @@ | |
| 812 | } |
| 813 | } |
| 814 | fputs(z, pLog); |
| 815 | } |
| 816 | |
| 817 | /* Forward declaration */ |
| 818 | static NORETURN void malformed_request(const char *zMsg); |
| 819 | |
| 820 | /* |
| 821 | ** Initialize the query parameter database. Information is pulled from |
| 822 | ** the QUERY_STRING environment variable (if it exists), from standard |
| 823 | ** input if there is POST data, and from HTTP_COOKIE. |
| @@ -822,15 +824,26 @@ | |
| 824 | */ |
| 825 | void cgi_init(void){ |
| 826 | char *z; |
| 827 | const char *zType; |
| 828 | int len; |
| 829 | const char *zRequestUri = cgi_parameter("REQUEST_URI",0); |
| 830 | const char *zScriptName = cgi_parameter("SCRIPT_NAME",0); |
| 831 | |
| 832 | #ifdef FOSSIL_ENABLE_JSON |
| 833 | json_main_bootstrap(); |
| 834 | #endif |
| 835 | g.isHTTP = 1; |
| 836 | cgi_destination(CGI_BODY); |
| 837 | if( zRequestUri==0 ) malformed_request("missing REQUEST_URI"); |
| 838 | if( zScriptName==0 ) malformed_request("missing SCRIPT_NAME"); |
| 839 | if( cgi_parameter("PATH_INFO",0)==0 ){ |
| 840 | int i, j; |
| 841 | for(i=0; zRequestUri[i]==zScriptName[i] && zRequestUri[i]; i++){} |
| 842 | for(j=i; zRequestUri[j] && zRequestUri[j]!='?'; j++){} |
| 843 | cgi_set_parameter("PATH_INFO", mprintf("%.*s", j-i, zRequestUri+i)); |
| 844 | } |
| 845 | |
| 846 | z = (char*)P("HTTP_COOKIE"); |
| 847 | if( z ){ |
| 848 | z = mprintf("%s",z); |
| 849 | add_param_list(z, ';'); |
| @@ -1092,14 +1105,14 @@ | |
| 1105 | |
| 1106 | |
| 1107 | /* |
| 1108 | ** Send a reply indicating that the HTTP request was malformed |
| 1109 | */ |
| 1110 | static NORETURN void malformed_request(const char *zMsg){ |
| 1111 | cgi_set_status(501, "Not Implemented"); |
| 1112 | cgi_printf( |
| 1113 | "<html><body><p>Bad Request: %s</p></body></html>\n", zMsg |
| 1114 | ); |
| 1115 | cgi_reply(); |
| 1116 | fossil_exit(0); |
| 1117 | } |
| 1118 | |
| @@ -1187,29 +1200,30 @@ | |
| 1200 | struct sockaddr_in remoteName; |
| 1201 | socklen_t size = sizeof(struct sockaddr_in); |
| 1202 | char zLine[2000]; /* A single line of input. */ |
| 1203 | g.fullHttpReply = 1; |
| 1204 | if( fgets(zLine, sizeof(zLine),g.httpIn)==0 ){ |
| 1205 | malformed_request("missing HTTP header"); |
| 1206 | } |
| 1207 | blob_append(&g.httpHeader, zLine, -1); |
| 1208 | cgi_trace(zLine); |
| 1209 | zToken = extract_token(zLine, &z); |
| 1210 | if( zToken==0 ){ |
| 1211 | malformed_request("malformed HTTP header"); |
| 1212 | } |
| 1213 | if( fossil_strcmp(zToken,"GET")!=0 && fossil_strcmp(zToken,"POST")!=0 |
| 1214 | && fossil_strcmp(zToken,"HEAD")!=0 ){ |
| 1215 | malformed_request("unsupported HTTP method"); |
| 1216 | } |
| 1217 | cgi_setenv("GATEWAY_INTERFACE","CGI/1.0"); |
| 1218 | cgi_setenv("REQUEST_METHOD",zToken); |
| 1219 | zToken = extract_token(z, &z); |
| 1220 | if( zToken==0 ){ |
| 1221 | malformed_request("malformed URL in HTTP header"); |
| 1222 | } |
| 1223 | cgi_setenv("REQUEST_URI", zToken); |
| 1224 | cgi_setenv("SCRIPT_NAME", ""); |
| 1225 | for(i=0; zToken[i] && zToken[i]!='?'; i++){} |
| 1226 | if( zToken[i] ) zToken[i++] = 0; |
| 1227 | cgi_setenv("PATH_INFO", zToken); |
| 1228 | cgi_setenv("QUERY_STRING", &zToken[i]); |
| 1229 | if( zIpAddr==0 && |
| @@ -1269,16 +1283,57 @@ | |
| 1283 | } |
| 1284 | } |
| 1285 | cgi_init(); |
| 1286 | cgi_trace(0); |
| 1287 | } |
| 1288 | |
| 1289 | /* |
| 1290 | ** This routine handles a single SCGI request which is coming in on |
| 1291 | ** g.httpIn and which replies on g.httpOut |
| 1292 | ** |
| 1293 | ** The SCGI request is read from g.httpIn and is used to initialize |
| 1294 | ** entries in the cgi_parameter() hash, as if those entries were |
| 1295 | ** environment variables. A call to cgi_init() completes |
| 1296 | ** the setup. Once all the setup is finished, this procedure returns |
| 1297 | ** and subsequent code handles the actual generation of the webpage. |
| 1298 | */ |
| 1299 | void cgi_handle_scgi_request(void){ |
| 1300 | char *zHdr; |
| 1301 | char *zToFree; |
| 1302 | int nHdr = 0; |
| 1303 | int nRead; |
| 1304 | int n, m; |
| 1305 | char c; |
| 1306 | |
| 1307 | while( (c = fgetc(g.httpIn))!=EOF && fossil_isdigit(c) ){ |
| 1308 | nHdr = nHdr*10 + c - '0'; |
| 1309 | } |
| 1310 | if( nHdr<16 ) malformed_request("SCGI header too short"); |
| 1311 | zToFree = zHdr = fossil_malloc(nHdr); |
| 1312 | nRead = (int)fread(zHdr, 1, nHdr, g.httpIn); |
| 1313 | if( nRead<nHdr ) malformed_request("cannot read entire SCGI header"); |
| 1314 | nHdr = nRead; |
| 1315 | while( nHdr ){ |
| 1316 | for(n=0; n<nHdr && zHdr[n]; n++){} |
| 1317 | for(m=n+1; m<nHdr && zHdr[m]; m++){} |
| 1318 | if( m>=nHdr ) malformed_request("SCGI header formatting error"); |
| 1319 | cgi_set_parameter(zHdr, zHdr+n+1); |
| 1320 | zHdr += m+1; |
| 1321 | nHdr -= m+1; |
| 1322 | } |
| 1323 | fossil_free(zToFree); |
| 1324 | fgetc(g.httpIn); /* Read past the "," separating header from content */ |
| 1325 | cgi_init(); |
| 1326 | } |
| 1327 | |
| 1328 | |
| 1329 | #if INTERFACE |
| 1330 | /* |
| 1331 | ** Bitmap values for the flags parameter to cgi_http_server(). |
| 1332 | */ |
| 1333 | #define HTTP_SERVER_LOCALHOST 0x0001 /* Bind to 127.0.0.1 only */ |
| 1334 | #define HTTP_SERVER_SCGI 0x0002 /* SCGI instead of HTTP */ |
| 1335 | |
| 1336 | #endif /* INTERFACE */ |
| 1337 | |
| 1338 | /* |
| 1339 | ** Maximum number of child processes that we can have running |
| @@ -1356,11 +1411,12 @@ | |
| 1411 | } |
| 1412 | } |
| 1413 | if( iPort>mxPort ) return 1; |
| 1414 | listen(listener,10); |
| 1415 | if( iPort>mnPort ){ |
| 1416 | fossil_print("Listening for %s requests on TCP port %d\n", |
| 1417 | (flags & HTTP_SERVER_SCGI)!=0?"SCGI":"HTTP", iPort); |
| 1418 | fflush(stdout); |
| 1419 | } |
| 1420 | if( zBrowser ){ |
| 1421 | zBrowser = mprintf(zBrowser, iPort); |
| 1422 | if( system(zBrowser)<0 ){ |
| 1423 |
+6
-1
| --- src/checkin.c | ||
| +++ src/checkin.c | ||
| @@ -1526,10 +1526,15 @@ | ||
| 1526 | 1526 | blob_append(&comment, zComment, -1); |
| 1527 | 1527 | }else if( zComFile ){ |
| 1528 | 1528 | blob_zero(&comment); |
| 1529 | 1529 | blob_read_from_file(&comment, zComFile); |
| 1530 | 1530 | blob_to_utf8_no_bom(&comment, 1); |
| 1531 | + }else if(dryRunFlag){ | |
| 1532 | + blob_zero(&comment); | |
| 1533 | + blob_append(&comment, "Dry-run mode - no comment provided.", -1) | |
| 1534 | + /* Comment needed to avoid downstream assertion. */ | |
| 1535 | + ; | |
| 1531 | 1536 | }else{ |
| 1532 | 1537 | char *zInit = db_text(0, "SELECT value FROM vvar WHERE name='ci-comment'"); |
| 1533 | 1538 | prepare_commit_comment(&comment, zInit, &sCiInfo, vid); |
| 1534 | 1539 | if( zInit && zInit[0] && fossil_strcmp(zInit, blob_str(&comment))==0 ){ |
| 1535 | 1540 | blob_zero(&ans); |
| @@ -1722,11 +1727,11 @@ | ||
| 1722 | 1727 | int nrid; |
| 1723 | 1728 | |
| 1724 | 1729 | blob_zero(&ctrl); |
| 1725 | 1730 | zDate = date_in_standard_format(sCiInfo.zDateOvrd ? sCiInfo.zDateOvrd : "now"); |
| 1726 | 1731 | blob_appendf(&ctrl, "D %s\n", zDate); |
| 1727 | - blob_appendf(&ctrl, "T +closed %s\n", zIntegrateUuid); | |
| 1732 | + blob_appendf(&ctrl, "T +closed %s by\\smerge\\s--integrate\n", zIntegrateUuid); | |
| 1728 | 1733 | blob_appendf(&ctrl, "U %F\n", sCiInfo.zUserOvrd ? sCiInfo.zUserOvrd : g.zLogin); |
| 1729 | 1734 | md5sum_blob(&ctrl, &cksum); |
| 1730 | 1735 | blob_appendf(&ctrl, "Z %b\n", &cksum); |
| 1731 | 1736 | nrid = content_put(&ctrl); |
| 1732 | 1737 | manifest_crosslink(nrid, &ctrl); |
| 1733 | 1738 |
| --- src/checkin.c | |
| +++ src/checkin.c | |
| @@ -1526,10 +1526,15 @@ | |
| 1526 | blob_append(&comment, zComment, -1); |
| 1527 | }else if( zComFile ){ |
| 1528 | blob_zero(&comment); |
| 1529 | blob_read_from_file(&comment, zComFile); |
| 1530 | blob_to_utf8_no_bom(&comment, 1); |
| 1531 | }else{ |
| 1532 | char *zInit = db_text(0, "SELECT value FROM vvar WHERE name='ci-comment'"); |
| 1533 | prepare_commit_comment(&comment, zInit, &sCiInfo, vid); |
| 1534 | if( zInit && zInit[0] && fossil_strcmp(zInit, blob_str(&comment))==0 ){ |
| 1535 | blob_zero(&ans); |
| @@ -1722,11 +1727,11 @@ | |
| 1722 | int nrid; |
| 1723 | |
| 1724 | blob_zero(&ctrl); |
| 1725 | zDate = date_in_standard_format(sCiInfo.zDateOvrd ? sCiInfo.zDateOvrd : "now"); |
| 1726 | blob_appendf(&ctrl, "D %s\n", zDate); |
| 1727 | blob_appendf(&ctrl, "T +closed %s\n", zIntegrateUuid); |
| 1728 | blob_appendf(&ctrl, "U %F\n", sCiInfo.zUserOvrd ? sCiInfo.zUserOvrd : g.zLogin); |
| 1729 | md5sum_blob(&ctrl, &cksum); |
| 1730 | blob_appendf(&ctrl, "Z %b\n", &cksum); |
| 1731 | nrid = content_put(&ctrl); |
| 1732 | manifest_crosslink(nrid, &ctrl); |
| 1733 |
| --- src/checkin.c | |
| +++ src/checkin.c | |
| @@ -1526,10 +1526,15 @@ | |
| 1526 | blob_append(&comment, zComment, -1); |
| 1527 | }else if( zComFile ){ |
| 1528 | blob_zero(&comment); |
| 1529 | blob_read_from_file(&comment, zComFile); |
| 1530 | blob_to_utf8_no_bom(&comment, 1); |
| 1531 | }else if(dryRunFlag){ |
| 1532 | blob_zero(&comment); |
| 1533 | blob_append(&comment, "Dry-run mode - no comment provided.", -1) |
| 1534 | /* Comment needed to avoid downstream assertion. */ |
| 1535 | ; |
| 1536 | }else{ |
| 1537 | char *zInit = db_text(0, "SELECT value FROM vvar WHERE name='ci-comment'"); |
| 1538 | prepare_commit_comment(&comment, zInit, &sCiInfo, vid); |
| 1539 | if( zInit && zInit[0] && fossil_strcmp(zInit, blob_str(&comment))==0 ){ |
| 1540 | blob_zero(&ans); |
| @@ -1722,11 +1727,11 @@ | |
| 1727 | int nrid; |
| 1728 | |
| 1729 | blob_zero(&ctrl); |
| 1730 | zDate = date_in_standard_format(sCiInfo.zDateOvrd ? sCiInfo.zDateOvrd : "now"); |
| 1731 | blob_appendf(&ctrl, "D %s\n", zDate); |
| 1732 | blob_appendf(&ctrl, "T +closed %s by\\smerge\\s--integrate\n", zIntegrateUuid); |
| 1733 | blob_appendf(&ctrl, "U %F\n", sCiInfo.zUserOvrd ? sCiInfo.zUserOvrd : g.zLogin); |
| 1734 | md5sum_blob(&ctrl, &cksum); |
| 1735 | blob_appendf(&ctrl, "Z %b\n", &cksum); |
| 1736 | nrid = content_put(&ctrl); |
| 1737 | manifest_crosslink(nrid, &ctrl); |
| 1738 |
+14
| --- src/clone.c | ||
| +++ src/clone.c | ||
| @@ -84,10 +84,24 @@ | ||
| 84 | 84 | ** |
| 85 | 85 | ** Usage: %fossil clone ?OPTIONS? URL FILENAME |
| 86 | 86 | ** |
| 87 | 87 | ** Make a clone of a repository specified by URL in the local |
| 88 | 88 | ** file named FILENAME. |
| 89 | +** | |
| 90 | +** URL must be in one of the following form: ([...] mean optional) | |
| 91 | +** HTTP/HTTPS protocol: | |
| 92 | +** http[s]://[userid[:password]@]host[:port][/path] | |
| 93 | +** | |
| 94 | +** SSH protocol: | |
| 95 | +** ssh://[userid[:password]@]host[:port]/path/to/repo.fossil\\ | |
| 96 | +** [?fossil=path/to/fossil.exe] | |
| 97 | +** | |
| 98 | +** Filesystem: | |
| 99 | +** [file://]path/to/repo.fossil | |
| 100 | +** | |
| 101 | +** Note: For ssh and filesystem, path must have an extra leading | |
| 102 | +** '/' to use an absolute path. | |
| 89 | 103 | ** |
| 90 | 104 | ** By default, your current login name is used to create the default |
| 91 | 105 | ** admin user. This can be overridden using the -A|--admin-user |
| 92 | 106 | ** parameter. |
| 93 | 107 | ** |
| 94 | 108 |
| --- src/clone.c | |
| +++ src/clone.c | |
| @@ -84,10 +84,24 @@ | |
| 84 | ** |
| 85 | ** Usage: %fossil clone ?OPTIONS? URL FILENAME |
| 86 | ** |
| 87 | ** Make a clone of a repository specified by URL in the local |
| 88 | ** file named FILENAME. |
| 89 | ** |
| 90 | ** By default, your current login name is used to create the default |
| 91 | ** admin user. This can be overridden using the -A|--admin-user |
| 92 | ** parameter. |
| 93 | ** |
| 94 |
| --- src/clone.c | |
| +++ src/clone.c | |
| @@ -84,10 +84,24 @@ | |
| 84 | ** |
| 85 | ** Usage: %fossil clone ?OPTIONS? URL FILENAME |
| 86 | ** |
| 87 | ** Make a clone of a repository specified by URL in the local |
| 88 | ** file named FILENAME. |
| 89 | ** |
| 90 | ** URL must be in one of the following form: ([...] mean optional) |
| 91 | ** HTTP/HTTPS protocol: |
| 92 | ** http[s]://[userid[:password]@]host[:port][/path] |
| 93 | ** |
| 94 | ** SSH protocol: |
| 95 | ** ssh://[userid[:password]@]host[:port]/path/to/repo.fossil\\ |
| 96 | ** [?fossil=path/to/fossil.exe] |
| 97 | ** |
| 98 | ** Filesystem: |
| 99 | ** [file://]path/to/repo.fossil |
| 100 | ** |
| 101 | ** Note: For ssh and filesystem, path must have an extra leading |
| 102 | ** '/' to use an absolute path. |
| 103 | ** |
| 104 | ** By default, your current login name is used to create the default |
| 105 | ** admin user. This can be overridden using the -A|--admin-user |
| 106 | ** parameter. |
| 107 | ** |
| 108 |
+14
| --- src/clone.c | ||
| +++ src/clone.c | ||
| @@ -84,10 +84,24 @@ | ||
| 84 | 84 | ** |
| 85 | 85 | ** Usage: %fossil clone ?OPTIONS? URL FILENAME |
| 86 | 86 | ** |
| 87 | 87 | ** Make a clone of a repository specified by URL in the local |
| 88 | 88 | ** file named FILENAME. |
| 89 | +** | |
| 90 | +** URL must be in one of the following form: ([...] mean optional) | |
| 91 | +** HTTP/HTTPS protocol: | |
| 92 | +** http[s]://[userid[:password]@]host[:port][/path] | |
| 93 | +** | |
| 94 | +** SSH protocol: | |
| 95 | +** ssh://[userid[:password]@]host[:port]/path/to/repo.fossil\\ | |
| 96 | +** [?fossil=path/to/fossil.exe] | |
| 97 | +** | |
| 98 | +** Filesystem: | |
| 99 | +** [file://]path/to/repo.fossil | |
| 100 | +** | |
| 101 | +** Note: For ssh and filesystem, path must have an extra leading | |
| 102 | +** '/' to use an absolute path. | |
| 89 | 103 | ** |
| 90 | 104 | ** By default, your current login name is used to create the default |
| 91 | 105 | ** admin user. This can be overridden using the -A|--admin-user |
| 92 | 106 | ** parameter. |
| 93 | 107 | ** |
| 94 | 108 |
| --- src/clone.c | |
| +++ src/clone.c | |
| @@ -84,10 +84,24 @@ | |
| 84 | ** |
| 85 | ** Usage: %fossil clone ?OPTIONS? URL FILENAME |
| 86 | ** |
| 87 | ** Make a clone of a repository specified by URL in the local |
| 88 | ** file named FILENAME. |
| 89 | ** |
| 90 | ** By default, your current login name is used to create the default |
| 91 | ** admin user. This can be overridden using the -A|--admin-user |
| 92 | ** parameter. |
| 93 | ** |
| 94 |
| --- src/clone.c | |
| +++ src/clone.c | |
| @@ -84,10 +84,24 @@ | |
| 84 | ** |
| 85 | ** Usage: %fossil clone ?OPTIONS? URL FILENAME |
| 86 | ** |
| 87 | ** Make a clone of a repository specified by URL in the local |
| 88 | ** file named FILENAME. |
| 89 | ** |
| 90 | ** URL must be in one of the following form: ([...] mean optional) |
| 91 | ** HTTP/HTTPS protocol: |
| 92 | ** http[s]://[userid[:password]@]host[:port][/path] |
| 93 | ** |
| 94 | ** SSH protocol: |
| 95 | ** ssh://[userid[:password]@]host[:port]/path/to/repo.fossil\\ |
| 96 | ** [?fossil=path/to/fossil.exe] |
| 97 | ** |
| 98 | ** Filesystem: |
| 99 | ** [file://]path/to/repo.fossil |
| 100 | ** |
| 101 | ** Note: For ssh and filesystem, path must have an extra leading |
| 102 | ** '/' to use an absolute path. |
| 103 | ** |
| 104 | ** By default, your current login name is used to create the default |
| 105 | ** admin user. This can be overridden using the -A|--admin-user |
| 106 | ** parameter. |
| 107 | ** |
| 108 |
-2
| --- src/content.c | ||
| +++ src/content.c | ||
| @@ -27,11 +27,10 @@ | ||
| 27 | 27 | static struct { |
| 28 | 28 | i64 szTotal; /* Total size of all entries in the cache */ |
| 29 | 29 | int n; /* Current number of cache entries */ |
| 30 | 30 | int nAlloc; /* Number of slots allocated in a[] */ |
| 31 | 31 | int nextAge; /* Age counter for implementing LRU */ |
| 32 | - int skipCnt; /* Used to limit entries expelled from cache */ | |
| 33 | 32 | struct cacheLine { /* One instance of this for each cache entry */ |
| 34 | 33 | int rid; /* Artifact id */ |
| 35 | 34 | int age; /* Age. Newer is larger */ |
| 36 | 35 | Blob content; /* Content of the artifact */ |
| 37 | 36 | } *a; /* The positive cache */ |
| @@ -492,11 +491,10 @@ | ||
| 492 | 491 | |
| 493 | 492 | assert( g.repositoryOpen ); |
| 494 | 493 | assert( pBlob!=0 ); |
| 495 | 494 | assert( srcId==0 || zUuid!=0 ); |
| 496 | 495 | if( zUuid==0 ){ |
| 497 | - assert( pBlob!=0 ); | |
| 498 | 496 | assert( nBlob==0 ); |
| 499 | 497 | sha1sum_blob(pBlob, &hash); |
| 500 | 498 | }else{ |
| 501 | 499 | blob_init(&hash, zUuid, -1); |
| 502 | 500 | } |
| 503 | 501 |
| --- src/content.c | |
| +++ src/content.c | |
| @@ -27,11 +27,10 @@ | |
| 27 | static struct { |
| 28 | i64 szTotal; /* Total size of all entries in the cache */ |
| 29 | int n; /* Current number of cache entries */ |
| 30 | int nAlloc; /* Number of slots allocated in a[] */ |
| 31 | int nextAge; /* Age counter for implementing LRU */ |
| 32 | int skipCnt; /* Used to limit entries expelled from cache */ |
| 33 | struct cacheLine { /* One instance of this for each cache entry */ |
| 34 | int rid; /* Artifact id */ |
| 35 | int age; /* Age. Newer is larger */ |
| 36 | Blob content; /* Content of the artifact */ |
| 37 | } *a; /* The positive cache */ |
| @@ -492,11 +491,10 @@ | |
| 492 | |
| 493 | assert( g.repositoryOpen ); |
| 494 | assert( pBlob!=0 ); |
| 495 | assert( srcId==0 || zUuid!=0 ); |
| 496 | if( zUuid==0 ){ |
| 497 | assert( pBlob!=0 ); |
| 498 | assert( nBlob==0 ); |
| 499 | sha1sum_blob(pBlob, &hash); |
| 500 | }else{ |
| 501 | blob_init(&hash, zUuid, -1); |
| 502 | } |
| 503 |
| --- src/content.c | |
| +++ src/content.c | |
| @@ -27,11 +27,10 @@ | |
| 27 | static struct { |
| 28 | i64 szTotal; /* Total size of all entries in the cache */ |
| 29 | int n; /* Current number of cache entries */ |
| 30 | int nAlloc; /* Number of slots allocated in a[] */ |
| 31 | int nextAge; /* Age counter for implementing LRU */ |
| 32 | struct cacheLine { /* One instance of this for each cache entry */ |
| 33 | int rid; /* Artifact id */ |
| 34 | int age; /* Age. Newer is larger */ |
| 35 | Blob content; /* Content of the artifact */ |
| 36 | } *a; /* The positive cache */ |
| @@ -492,11 +491,10 @@ | |
| 491 | |
| 492 | assert( g.repositoryOpen ); |
| 493 | assert( pBlob!=0 ); |
| 494 | assert( srcId==0 || zUuid!=0 ); |
| 495 | if( zUuid==0 ){ |
| 496 | assert( nBlob==0 ); |
| 497 | sha1sum_blob(pBlob, &hash); |
| 498 | }else{ |
| 499 | blob_init(&hash, zUuid, -1); |
| 500 | } |
| 501 |
+1
-1
| --- src/diff.c | ||
| +++ src/diff.c | ||
| @@ -1310,11 +1310,11 @@ | ||
| 1310 | 1310 | sbsWriteText(&s, &B[b+j], SBS_TXTB); |
| 1311 | 1311 | } |
| 1312 | 1312 | } |
| 1313 | 1313 | |
| 1314 | 1314 | if( s.escHtml && blob_size(s.apCols[SBS_LNA])>0 ){ |
| 1315 | - blob_append(pOut, "<table class=\"sbsdiffcols\" width=\"90%\"><tr>\n", -1); | |
| 1315 | + blob_append(pOut, "<table class=\"sbsdiffcols\"><tr>\n", -1); | |
| 1316 | 1316 | for(i=SBS_LNA; i<=SBS_TXTB; i++){ |
| 1317 | 1317 | sbsWriteColumn(pOut, s.apCols[i], i); |
| 1318 | 1318 | blob_reset(s.apCols[i]); |
| 1319 | 1319 | } |
| 1320 | 1320 | blob_append(pOut, "</tr></table>\n", -1); |
| 1321 | 1321 |
| --- src/diff.c | |
| +++ src/diff.c | |
| @@ -1310,11 +1310,11 @@ | |
| 1310 | sbsWriteText(&s, &B[b+j], SBS_TXTB); |
| 1311 | } |
| 1312 | } |
| 1313 | |
| 1314 | if( s.escHtml && blob_size(s.apCols[SBS_LNA])>0 ){ |
| 1315 | blob_append(pOut, "<table class=\"sbsdiffcols\" width=\"90%\"><tr>\n", -1); |
| 1316 | for(i=SBS_LNA; i<=SBS_TXTB; i++){ |
| 1317 | sbsWriteColumn(pOut, s.apCols[i], i); |
| 1318 | blob_reset(s.apCols[i]); |
| 1319 | } |
| 1320 | blob_append(pOut, "</tr></table>\n", -1); |
| 1321 |
| --- src/diff.c | |
| +++ src/diff.c | |
| @@ -1310,11 +1310,11 @@ | |
| 1310 | sbsWriteText(&s, &B[b+j], SBS_TXTB); |
| 1311 | } |
| 1312 | } |
| 1313 | |
| 1314 | if( s.escHtml && blob_size(s.apCols[SBS_LNA])>0 ){ |
| 1315 | blob_append(pOut, "<table class=\"sbsdiffcols\"><tr>\n", -1); |
| 1316 | for(i=SBS_LNA; i<=SBS_TXTB; i++){ |
| 1317 | sbsWriteColumn(pOut, s.apCols[i], i); |
| 1318 | blob_reset(s.apCols[i]); |
| 1319 | } |
| 1320 | blob_append(pOut, "</tr></table>\n", -1); |
| 1321 |
+1
-1
| --- src/glob.c | ||
| +++ src/glob.c | ||
| @@ -252,11 +252,11 @@ | ||
| 252 | 252 | ** |
| 253 | 253 | ** PATTERN is a comma- and whitespace-separated list of optionally |
| 254 | 254 | ** quoted glob patterns. Show which of the STRINGs that follow match |
| 255 | 255 | ** the PATTERN. |
| 256 | 256 | ** |
| 257 | -** If PATTERN begins with "@" the the rest of the pattern is understood | |
| 257 | +** If PATTERN begins with "@" the rest of the pattern is understood | |
| 258 | 258 | ** to be a setting name (such as binary-glob, crln-glob, or encoding-glob) |
| 259 | 259 | ** and the value of that setting is used as the actually glob pattern. |
| 260 | 260 | */ |
| 261 | 261 | void glob_test_cmd(void){ |
| 262 | 262 | Glob *pGlob; |
| 263 | 263 |
| --- src/glob.c | |
| +++ src/glob.c | |
| @@ -252,11 +252,11 @@ | |
| 252 | ** |
| 253 | ** PATTERN is a comma- and whitespace-separated list of optionally |
| 254 | ** quoted glob patterns. Show which of the STRINGs that follow match |
| 255 | ** the PATTERN. |
| 256 | ** |
| 257 | ** If PATTERN begins with "@" the the rest of the pattern is understood |
| 258 | ** to be a setting name (such as binary-glob, crln-glob, or encoding-glob) |
| 259 | ** and the value of that setting is used as the actually glob pattern. |
| 260 | */ |
| 261 | void glob_test_cmd(void){ |
| 262 | Glob *pGlob; |
| 263 |
| --- src/glob.c | |
| +++ src/glob.c | |
| @@ -252,11 +252,11 @@ | |
| 252 | ** |
| 253 | ** PATTERN is a comma- and whitespace-separated list of optionally |
| 254 | ** quoted glob patterns. Show which of the STRINGs that follow match |
| 255 | ** the PATTERN. |
| 256 | ** |
| 257 | ** If PATTERN begins with "@" the rest of the pattern is understood |
| 258 | ** to be a setting name (such as binary-glob, crln-glob, or encoding-glob) |
| 259 | ** and the value of that setting is used as the actually glob pattern. |
| 260 | */ |
| 261 | void glob_test_cmd(void){ |
| 262 | Glob *pGlob; |
| 263 |
+1
-1
| --- src/graph.c | ||
| +++ src/graph.c | ||
| @@ -198,11 +198,11 @@ | ||
| 198 | 198 | pRow->zBranch = persistBranchName(p, zBranch); |
| 199 | 199 | if( zUuid==0 ) zUuid = ""; |
| 200 | 200 | sqlite3_snprintf(sizeof(pRow->zUuid), pRow->zUuid, "%s", zUuid); |
| 201 | 201 | pRow->isLeaf = isLeaf; |
| 202 | 202 | memset(pRow->aiRiser, -1, sizeof(pRow->aiRiser)); |
| 203 | - if( zBgClr==0 || zBgClr[0]==0 ) zBgClr = "white"; | |
| 203 | + if( zBgClr==0 ) zBgClr = ""; | |
| 204 | 204 | pRow->zBgClr = persistBranchName(p, zBgClr); |
| 205 | 205 | memcpy(pRow->aParent, aParent, sizeof(aParent[0])*nParent); |
| 206 | 206 | if( p->pFirst==0 ){ |
| 207 | 207 | p->pFirst = pRow; |
| 208 | 208 | }else{ |
| 209 | 209 |
| --- src/graph.c | |
| +++ src/graph.c | |
| @@ -198,11 +198,11 @@ | |
| 198 | pRow->zBranch = persistBranchName(p, zBranch); |
| 199 | if( zUuid==0 ) zUuid = ""; |
| 200 | sqlite3_snprintf(sizeof(pRow->zUuid), pRow->zUuid, "%s", zUuid); |
| 201 | pRow->isLeaf = isLeaf; |
| 202 | memset(pRow->aiRiser, -1, sizeof(pRow->aiRiser)); |
| 203 | if( zBgClr==0 || zBgClr[0]==0 ) zBgClr = "white"; |
| 204 | pRow->zBgClr = persistBranchName(p, zBgClr); |
| 205 | memcpy(pRow->aParent, aParent, sizeof(aParent[0])*nParent); |
| 206 | if( p->pFirst==0 ){ |
| 207 | p->pFirst = pRow; |
| 208 | }else{ |
| 209 |
| --- src/graph.c | |
| +++ src/graph.c | |
| @@ -198,11 +198,11 @@ | |
| 198 | pRow->zBranch = persistBranchName(p, zBranch); |
| 199 | if( zUuid==0 ) zUuid = ""; |
| 200 | sqlite3_snprintf(sizeof(pRow->zUuid), pRow->zUuid, "%s", zUuid); |
| 201 | pRow->isLeaf = isLeaf; |
| 202 | memset(pRow->aiRiser, -1, sizeof(pRow->aiRiser)); |
| 203 | if( zBgClr==0 ) zBgClr = ""; |
| 204 | pRow->zBgClr = persistBranchName(p, zBgClr); |
| 205 | memcpy(pRow->aParent, aParent, sizeof(aParent[0])*nParent); |
| 206 | if( p->pFirst==0 ){ |
| 207 | p->pFirst = pRow; |
| 208 | }else{ |
| 209 |
+1
-1
| --- src/json.c | ||
| +++ src/json.c | ||
| @@ -2004,11 +2004,11 @@ | ||
| 2004 | 2004 | ** whose final entry MUST have a NULL name value or results |
| 2005 | 2005 | ** are undefined. |
| 2006 | 2006 | ** |
| 2007 | 2007 | ** The list is appended to pOut. The number of items (not bytes) |
| 2008 | 2008 | ** appended are returned. If filterByMode is non-0 then the result |
| 2009 | -** list will contain only commands which are able to run in the the | |
| 2009 | +** list will contain only commands which are able to run in the | |
| 2010 | 2010 | ** current run mode (CLI vs. HTTP). |
| 2011 | 2011 | */ |
| 2012 | 2012 | static int json_pagedefs_to_string(JsonPageDef const * zPages, |
| 2013 | 2013 | Blob * pOut, int filterByMode){ |
| 2014 | 2014 | int i = 0; |
| 2015 | 2015 |
| --- src/json.c | |
| +++ src/json.c | |
| @@ -2004,11 +2004,11 @@ | |
| 2004 | ** whose final entry MUST have a NULL name value or results |
| 2005 | ** are undefined. |
| 2006 | ** |
| 2007 | ** The list is appended to pOut. The number of items (not bytes) |
| 2008 | ** appended are returned. If filterByMode is non-0 then the result |
| 2009 | ** list will contain only commands which are able to run in the the |
| 2010 | ** current run mode (CLI vs. HTTP). |
| 2011 | */ |
| 2012 | static int json_pagedefs_to_string(JsonPageDef const * zPages, |
| 2013 | Blob * pOut, int filterByMode){ |
| 2014 | int i = 0; |
| 2015 |
| --- src/json.c | |
| +++ src/json.c | |
| @@ -2004,11 +2004,11 @@ | |
| 2004 | ** whose final entry MUST have a NULL name value or results |
| 2005 | ** are undefined. |
| 2006 | ** |
| 2007 | ** The list is appended to pOut. The number of items (not bytes) |
| 2008 | ** appended are returned. If filterByMode is non-0 then the result |
| 2009 | ** list will contain only commands which are able to run in the |
| 2010 | ** current run mode (CLI vs. HTTP). |
| 2011 | */ |
| 2012 | static int json_pagedefs_to_string(JsonPageDef const * zPages, |
| 2013 | Blob * pOut, int filterByMode){ |
| 2014 | int i = 0; |
| 2015 |
+1
-1
| --- src/json.c | ||
| +++ src/json.c | ||
| @@ -2004,11 +2004,11 @@ | ||
| 2004 | 2004 | ** whose final entry MUST have a NULL name value or results |
| 2005 | 2005 | ** are undefined. |
| 2006 | 2006 | ** |
| 2007 | 2007 | ** The list is appended to pOut. The number of items (not bytes) |
| 2008 | 2008 | ** appended are returned. If filterByMode is non-0 then the result |
| 2009 | -** list will contain only commands which are able to run in the the | |
| 2009 | +** list will contain only commands which are able to run in the | |
| 2010 | 2010 | ** current run mode (CLI vs. HTTP). |
| 2011 | 2011 | */ |
| 2012 | 2012 | static int json_pagedefs_to_string(JsonPageDef const * zPages, |
| 2013 | 2013 | Blob * pOut, int filterByMode){ |
| 2014 | 2014 | int i = 0; |
| 2015 | 2015 |
| --- src/json.c | |
| +++ src/json.c | |
| @@ -2004,11 +2004,11 @@ | |
| 2004 | ** whose final entry MUST have a NULL name value or results |
| 2005 | ** are undefined. |
| 2006 | ** |
| 2007 | ** The list is appended to pOut. The number of items (not bytes) |
| 2008 | ** appended are returned. If filterByMode is non-0 then the result |
| 2009 | ** list will contain only commands which are able to run in the the |
| 2010 | ** current run mode (CLI vs. HTTP). |
| 2011 | */ |
| 2012 | static int json_pagedefs_to_string(JsonPageDef const * zPages, |
| 2013 | Blob * pOut, int filterByMode){ |
| 2014 | int i = 0; |
| 2015 |
| --- src/json.c | |
| +++ src/json.c | |
| @@ -2004,11 +2004,11 @@ | |
| 2004 | ** whose final entry MUST have a NULL name value or results |
| 2005 | ** are undefined. |
| 2006 | ** |
| 2007 | ** The list is appended to pOut. The number of items (not bytes) |
| 2008 | ** appended are returned. If filterByMode is non-0 then the result |
| 2009 | ** list will contain only commands which are able to run in the |
| 2010 | ** current run mode (CLI vs. HTTP). |
| 2011 | */ |
| 2012 | static int json_pagedefs_to_string(JsonPageDef const * zPages, |
| 2013 | Blob * pOut, int filterByMode){ |
| 2014 | int i = 0; |
| 2015 |
+18
-12
| --- src/login.c | ||
| +++ src/login.c | ||
| @@ -391,24 +391,24 @@ | ||
| 391 | 391 | for(i=0; zAgent[i]; i++){ |
| 392 | 392 | if( prefix_match("bot", zAgent+i) ) return 0; |
| 393 | 393 | if( prefix_match("spider", zAgent+i) ) return 0; |
| 394 | 394 | if( prefix_match("crawl", zAgent+i) ) return 0; |
| 395 | 395 | /* If a URI appears in the User-Agent, it is probably a bot */ |
| 396 | - if( memcmp("http", zAgent+i,4)==0 ) return 0; | |
| 396 | + if( strncmp("http", zAgent+i,4)==0 ) return 0; | |
| 397 | 397 | } |
| 398 | - if( memcmp(zAgent, "Mozilla/", 8)==0 ){ | |
| 398 | + if( strncmp(zAgent, "Mozilla/", 8)==0 ){ | |
| 399 | 399 | if( atoi(&zAgent[8])<4 ) return 0; /* Many bots advertise as Mozilla/3 */ |
| 400 | 400 | if( strglob("*Firefox/[1-9]*", zAgent) ) return 1; |
| 401 | 401 | if( strglob("*Chrome/[1-9]*", zAgent) ) return 1; |
| 402 | 402 | if( strglob("*(compatible;?MSIE?[1789]*", zAgent) ) return 1; |
| 403 | 403 | if( strglob("*AppleWebKit/[1-9]*(KHTML*", zAgent) ) return 1; |
| 404 | 404 | return 0; |
| 405 | 405 | } |
| 406 | - if( memcmp(zAgent, "Opera/", 6)==0 ) return 1; | |
| 407 | - if( memcmp(zAgent, "Safari/", 7)==0 ) return 1; | |
| 408 | - if( memcmp(zAgent, "Lynx/", 5)==0 ) return 1; | |
| 409 | - if( memcmp(zAgent, "NetSurf/", 8)==0 ) return 1; | |
| 406 | + if( strncmp(zAgent, "Opera/", 6)==0 ) return 1; | |
| 407 | + if( strncmp(zAgent, "Safari/", 7)==0 ) return 1; | |
| 408 | + if( strncmp(zAgent, "Lynx/", 5)==0 ) return 1; | |
| 409 | + if( strncmp(zAgent, "NetSurf/", 8)==0 ) return 1; | |
| 410 | 410 | return 0; |
| 411 | 411 | } |
| 412 | 412 | |
| 413 | 413 | /* |
| 414 | 414 | ** COMMAND: test-ishuman |
| @@ -598,11 +598,11 @@ | ||
| 598 | 598 | @ </table> |
| 599 | 599 | @ <script type="text/JavaScript"> |
| 600 | 600 | @ gebi('u').focus() |
| 601 | 601 | @ function chngAction(form){ |
| 602 | 602 | if( g.sslNotAvailable==0 |
| 603 | - && memcmp(g.zBaseURL,"https:",6)!=0 | |
| 603 | + && strncmp(g.zBaseURL,"https:",6)!=0 | |
| 604 | 604 | && db_get_boolean("https-login",0) |
| 605 | 605 | ){ |
| 606 | 606 | char *zSSL = mprintf("https:%s", &g.zBaseURL[5]); |
| 607 | 607 | @ if( form.u.value!="anonymous" ){ |
| 608 | 608 | @ form.action = "%h(zSSL)/login"; |
| @@ -763,14 +763,17 @@ | ||
| 763 | 763 | } |
| 764 | 764 | |
| 765 | 765 | /* |
| 766 | 766 | ** This routine examines the login cookie to see if it exists and |
| 767 | 767 | ** is valid. If the login cookie checks out, it then sets global |
| 768 | -** variables appropriately. Global variables set include g.userUid | |
| 769 | -** and g.zLogin and the g.perm family of permission booleans. | |
| 768 | +** variables appropriately. | |
| 770 | 769 | ** |
| 771 | -** If the | |
| 770 | +** g.userUid Database USER.UID value. Might be -1 for "nobody" | |
| 771 | +** g.zLogin Database USER.LOGIN value. NULL for user "nobody" | |
| 772 | +** g.perm Permissions granted to this user | |
| 773 | +** g.isHuman True if the user is human, not a spider or robot | |
| 774 | +** | |
| 772 | 775 | */ |
| 773 | 776 | void login_check_credentials(void){ |
| 774 | 777 | int uid = 0; /* User id */ |
| 775 | 778 | const char *zCookie; /* Text of the login cookie */ |
| 776 | 779 | const char *zIpAddr; /* Raw IP address of the requestor */ |
| @@ -799,10 +802,11 @@ | ||
| 799 | 802 | ){ |
| 800 | 803 | uid = db_int(0, "SELECT uid FROM user WHERE cap LIKE '%%s%%'"); |
| 801 | 804 | g.zLogin = db_text("?", "SELECT login FROM user WHERE uid=%d", uid); |
| 802 | 805 | zCap = "sx"; |
| 803 | 806 | g.noPswd = 1; |
| 807 | + g.isHuman = 1; | |
| 804 | 808 | sqlite3_snprintf(sizeof(g.zCsrfToken), g.zCsrfToken, "localhost"); |
| 805 | 809 | } |
| 806 | 810 | |
| 807 | 811 | /* Check the login cookie to see if it matches a known valid user. |
| 808 | 812 | */ |
| @@ -907,10 +911,11 @@ | ||
| 907 | 911 | */ |
| 908 | 912 | g.userUid = uid; |
| 909 | 913 | if( fossil_strcmp(g.zLogin,"nobody")==0 ){ |
| 910 | 914 | g.zLogin = 0; |
| 911 | 915 | } |
| 916 | + g.isHuman = g.zLogin==0 ? isHuman(P("HTTP_USER_AGENT")) : 1; | |
| 912 | 917 | |
| 913 | 918 | /* Set the capabilities */ |
| 914 | 919 | login_replace_capabilities(zCap, 0); |
| 915 | 920 | login_set_anon_nobody_capabilities(); |
| 916 | 921 | |
| @@ -918,13 +923,14 @@ | ||
| 918 | 923 | ** who do not have the "h" permission as long as their UserAgent string |
| 919 | 924 | ** makes it appear that they are human. Check to see if auto-hyperlink is |
| 920 | 925 | ** enabled for this repository and make appropriate adjustments to the |
| 921 | 926 | ** permission flags if it is. |
| 922 | 927 | */ |
| 923 | - if( zCap[0] && !g.perm.Hyperlink | |
| 928 | + if( zCap[0] | |
| 929 | + && !g.perm.Hyperlink | |
| 930 | + && g.isHuman | |
| 924 | 931 | && db_get_boolean("auto-hyperlink",1) |
| 925 | - && isHuman(P("HTTP_USER_AGENT")) | |
| 926 | 932 | ){ |
| 927 | 933 | g.perm.Hyperlink = 1; |
| 928 | 934 | g.javascriptHyperlink = 1; |
| 929 | 935 | } |
| 930 | 936 | |
| 931 | 937 |
| --- src/login.c | |
| +++ src/login.c | |
| @@ -391,24 +391,24 @@ | |
| 391 | for(i=0; zAgent[i]; i++){ |
| 392 | if( prefix_match("bot", zAgent+i) ) return 0; |
| 393 | if( prefix_match("spider", zAgent+i) ) return 0; |
| 394 | if( prefix_match("crawl", zAgent+i) ) return 0; |
| 395 | /* If a URI appears in the User-Agent, it is probably a bot */ |
| 396 | if( memcmp("http", zAgent+i,4)==0 ) return 0; |
| 397 | } |
| 398 | if( memcmp(zAgent, "Mozilla/", 8)==0 ){ |
| 399 | if( atoi(&zAgent[8])<4 ) return 0; /* Many bots advertise as Mozilla/3 */ |
| 400 | if( strglob("*Firefox/[1-9]*", zAgent) ) return 1; |
| 401 | if( strglob("*Chrome/[1-9]*", zAgent) ) return 1; |
| 402 | if( strglob("*(compatible;?MSIE?[1789]*", zAgent) ) return 1; |
| 403 | if( strglob("*AppleWebKit/[1-9]*(KHTML*", zAgent) ) return 1; |
| 404 | return 0; |
| 405 | } |
| 406 | if( memcmp(zAgent, "Opera/", 6)==0 ) return 1; |
| 407 | if( memcmp(zAgent, "Safari/", 7)==0 ) return 1; |
| 408 | if( memcmp(zAgent, "Lynx/", 5)==0 ) return 1; |
| 409 | if( memcmp(zAgent, "NetSurf/", 8)==0 ) return 1; |
| 410 | return 0; |
| 411 | } |
| 412 | |
| 413 | /* |
| 414 | ** COMMAND: test-ishuman |
| @@ -598,11 +598,11 @@ | |
| 598 | @ </table> |
| 599 | @ <script type="text/JavaScript"> |
| 600 | @ gebi('u').focus() |
| 601 | @ function chngAction(form){ |
| 602 | if( g.sslNotAvailable==0 |
| 603 | && memcmp(g.zBaseURL,"https:",6)!=0 |
| 604 | && db_get_boolean("https-login",0) |
| 605 | ){ |
| 606 | char *zSSL = mprintf("https:%s", &g.zBaseURL[5]); |
| 607 | @ if( form.u.value!="anonymous" ){ |
| 608 | @ form.action = "%h(zSSL)/login"; |
| @@ -763,14 +763,17 @@ | |
| 763 | } |
| 764 | |
| 765 | /* |
| 766 | ** This routine examines the login cookie to see if it exists and |
| 767 | ** is valid. If the login cookie checks out, it then sets global |
| 768 | ** variables appropriately. Global variables set include g.userUid |
| 769 | ** and g.zLogin and the g.perm family of permission booleans. |
| 770 | ** |
| 771 | ** If the |
| 772 | */ |
| 773 | void login_check_credentials(void){ |
| 774 | int uid = 0; /* User id */ |
| 775 | const char *zCookie; /* Text of the login cookie */ |
| 776 | const char *zIpAddr; /* Raw IP address of the requestor */ |
| @@ -799,10 +802,11 @@ | |
| 799 | ){ |
| 800 | uid = db_int(0, "SELECT uid FROM user WHERE cap LIKE '%%s%%'"); |
| 801 | g.zLogin = db_text("?", "SELECT login FROM user WHERE uid=%d", uid); |
| 802 | zCap = "sx"; |
| 803 | g.noPswd = 1; |
| 804 | sqlite3_snprintf(sizeof(g.zCsrfToken), g.zCsrfToken, "localhost"); |
| 805 | } |
| 806 | |
| 807 | /* Check the login cookie to see if it matches a known valid user. |
| 808 | */ |
| @@ -907,10 +911,11 @@ | |
| 907 | */ |
| 908 | g.userUid = uid; |
| 909 | if( fossil_strcmp(g.zLogin,"nobody")==0 ){ |
| 910 | g.zLogin = 0; |
| 911 | } |
| 912 | |
| 913 | /* Set the capabilities */ |
| 914 | login_replace_capabilities(zCap, 0); |
| 915 | login_set_anon_nobody_capabilities(); |
| 916 | |
| @@ -918,13 +923,14 @@ | |
| 918 | ** who do not have the "h" permission as long as their UserAgent string |
| 919 | ** makes it appear that they are human. Check to see if auto-hyperlink is |
| 920 | ** enabled for this repository and make appropriate adjustments to the |
| 921 | ** permission flags if it is. |
| 922 | */ |
| 923 | if( zCap[0] && !g.perm.Hyperlink |
| 924 | && db_get_boolean("auto-hyperlink",1) |
| 925 | && isHuman(P("HTTP_USER_AGENT")) |
| 926 | ){ |
| 927 | g.perm.Hyperlink = 1; |
| 928 | g.javascriptHyperlink = 1; |
| 929 | } |
| 930 | |
| 931 |
| --- src/login.c | |
| +++ src/login.c | |
| @@ -391,24 +391,24 @@ | |
| 391 | for(i=0; zAgent[i]; i++){ |
| 392 | if( prefix_match("bot", zAgent+i) ) return 0; |
| 393 | if( prefix_match("spider", zAgent+i) ) return 0; |
| 394 | if( prefix_match("crawl", zAgent+i) ) return 0; |
| 395 | /* If a URI appears in the User-Agent, it is probably a bot */ |
| 396 | if( strncmp("http", zAgent+i,4)==0 ) return 0; |
| 397 | } |
| 398 | if( strncmp(zAgent, "Mozilla/", 8)==0 ){ |
| 399 | if( atoi(&zAgent[8])<4 ) return 0; /* Many bots advertise as Mozilla/3 */ |
| 400 | if( strglob("*Firefox/[1-9]*", zAgent) ) return 1; |
| 401 | if( strglob("*Chrome/[1-9]*", zAgent) ) return 1; |
| 402 | if( strglob("*(compatible;?MSIE?[1789]*", zAgent) ) return 1; |
| 403 | if( strglob("*AppleWebKit/[1-9]*(KHTML*", zAgent) ) return 1; |
| 404 | return 0; |
| 405 | } |
| 406 | if( strncmp(zAgent, "Opera/", 6)==0 ) return 1; |
| 407 | if( strncmp(zAgent, "Safari/", 7)==0 ) return 1; |
| 408 | if( strncmp(zAgent, "Lynx/", 5)==0 ) return 1; |
| 409 | if( strncmp(zAgent, "NetSurf/", 8)==0 ) return 1; |
| 410 | return 0; |
| 411 | } |
| 412 | |
| 413 | /* |
| 414 | ** COMMAND: test-ishuman |
| @@ -598,11 +598,11 @@ | |
| 598 | @ </table> |
| 599 | @ <script type="text/JavaScript"> |
| 600 | @ gebi('u').focus() |
| 601 | @ function chngAction(form){ |
| 602 | if( g.sslNotAvailable==0 |
| 603 | && strncmp(g.zBaseURL,"https:",6)!=0 |
| 604 | && db_get_boolean("https-login",0) |
| 605 | ){ |
| 606 | char *zSSL = mprintf("https:%s", &g.zBaseURL[5]); |
| 607 | @ if( form.u.value!="anonymous" ){ |
| 608 | @ form.action = "%h(zSSL)/login"; |
| @@ -763,14 +763,17 @@ | |
| 763 | } |
| 764 | |
| 765 | /* |
| 766 | ** This routine examines the login cookie to see if it exists and |
| 767 | ** is valid. If the login cookie checks out, it then sets global |
| 768 | ** variables appropriately. |
| 769 | ** |
| 770 | ** g.userUid Database USER.UID value. Might be -1 for "nobody" |
| 771 | ** g.zLogin Database USER.LOGIN value. NULL for user "nobody" |
| 772 | ** g.perm Permissions granted to this user |
| 773 | ** g.isHuman True if the user is human, not a spider or robot |
| 774 | ** |
| 775 | */ |
| 776 | void login_check_credentials(void){ |
| 777 | int uid = 0; /* User id */ |
| 778 | const char *zCookie; /* Text of the login cookie */ |
| 779 | const char *zIpAddr; /* Raw IP address of the requestor */ |
| @@ -799,10 +802,11 @@ | |
| 802 | ){ |
| 803 | uid = db_int(0, "SELECT uid FROM user WHERE cap LIKE '%%s%%'"); |
| 804 | g.zLogin = db_text("?", "SELECT login FROM user WHERE uid=%d", uid); |
| 805 | zCap = "sx"; |
| 806 | g.noPswd = 1; |
| 807 | g.isHuman = 1; |
| 808 | sqlite3_snprintf(sizeof(g.zCsrfToken), g.zCsrfToken, "localhost"); |
| 809 | } |
| 810 | |
| 811 | /* Check the login cookie to see if it matches a known valid user. |
| 812 | */ |
| @@ -907,10 +911,11 @@ | |
| 911 | */ |
| 912 | g.userUid = uid; |
| 913 | if( fossil_strcmp(g.zLogin,"nobody")==0 ){ |
| 914 | g.zLogin = 0; |
| 915 | } |
| 916 | g.isHuman = g.zLogin==0 ? isHuman(P("HTTP_USER_AGENT")) : 1; |
| 917 | |
| 918 | /* Set the capabilities */ |
| 919 | login_replace_capabilities(zCap, 0); |
| 920 | login_set_anon_nobody_capabilities(); |
| 921 | |
| @@ -918,13 +923,14 @@ | |
| 923 | ** who do not have the "h" permission as long as their UserAgent string |
| 924 | ** makes it appear that they are human. Check to see if auto-hyperlink is |
| 925 | ** enabled for this repository and make appropriate adjustments to the |
| 926 | ** permission flags if it is. |
| 927 | */ |
| 928 | if( zCap[0] |
| 929 | && !g.perm.Hyperlink |
| 930 | && g.isHuman |
| 931 | && db_get_boolean("auto-hyperlink",1) |
| 932 | ){ |
| 933 | g.perm.Hyperlink = 1; |
| 934 | g.javascriptHyperlink = 1; |
| 935 | } |
| 936 | |
| 937 |
+16
-2
| --- src/main.c | ||
| +++ src/main.c | ||
| @@ -185,10 +185,11 @@ | ||
| 185 | 185 | const char *zSSLIdentity; /* Value of --ssl-identity option, filename of |
| 186 | 186 | ** SSL client identity */ |
| 187 | 187 | int useLocalauth; /* No login required if from 127.0.0.1 */ |
| 188 | 188 | int noPswd; /* Logged in without password (on 127.0.0.1) */ |
| 189 | 189 | int userUid; /* Integer user id */ |
| 190 | + int isHuman; /* True if access by a human, not a spider or bot */ | |
| 190 | 191 | |
| 191 | 192 | /* Information used to populate the RCVFROM table */ |
| 192 | 193 | int rcvid; /* The rcvid. 0 if not yet defined. */ |
| 193 | 194 | char *zIpAddr; /* The remote IP address */ |
| 194 | 195 | char *zNonce; /* The nonce used for login */ |
| @@ -1639,19 +1640,21 @@ | ||
| 1639 | 1640 | ** --https signal a request coming in via https |
| 1640 | 1641 | ** --nossl signal that no SSL connections are available |
| 1641 | 1642 | ** --notfound URL use URL as "HTTP 404, object not found" page. |
| 1642 | 1643 | ** --files GLOB comma-separate glob patterns for static file to serve |
| 1643 | 1644 | ** --baseurl URL base URL (useful with reverse proxies) |
| 1645 | +** --scgi Interpret input as SCGI rather than HTTP | |
| 1644 | 1646 | ** |
| 1645 | 1647 | ** See also: cgi, server, winsrv |
| 1646 | 1648 | */ |
| 1647 | 1649 | void cmd_http(void){ |
| 1648 | 1650 | const char *zIpAddr; |
| 1649 | 1651 | const char *zNotFound; |
| 1650 | 1652 | const char *zHost; |
| 1651 | 1653 | const char *zAltBase; |
| 1652 | 1654 | const char *zFileGlob; |
| 1655 | + int useSCGI; | |
| 1653 | 1656 | |
| 1654 | 1657 | /* The winhttp module passes the --files option as --files-urlenc with |
| 1655 | 1658 | ** the argument being URL encoded, to avoid wildcard expansion in the |
| 1656 | 1659 | ** shell. This option is for internal use and is undocumented. |
| 1657 | 1660 | */ |
| @@ -1664,10 +1667,11 @@ | ||
| 1664 | 1667 | zFileGlob = find_option("files",0,1); |
| 1665 | 1668 | } |
| 1666 | 1669 | zNotFound = find_option("notfound", 0, 1); |
| 1667 | 1670 | g.useLocalauth = find_option("localauth", 0, 0)!=0; |
| 1668 | 1671 | g.sslNotAvailable = find_option("nossl", 0, 0)!=0; |
| 1672 | + useSCGI = find_option("scgi", 0, 0)!=0; | |
| 1669 | 1673 | zAltBase = find_option("baseurl", 0, 1); |
| 1670 | 1674 | if( zAltBase ) set_base_url(zAltBase); |
| 1671 | 1675 | if( find_option("https",0,0)!=0 ) cgi_replace_parameter("HTTPS","on"); |
| 1672 | 1676 | zHost = find_option("host", 0, 1); |
| 1673 | 1677 | if( zHost ) cgi_replace_parameter("HTTP_HOST",zHost); |
| @@ -1688,11 +1692,15 @@ | ||
| 1688 | 1692 | if( zIpAddr==0 ){ |
| 1689 | 1693 | zIpAddr = cgi_ssh_remote_addr(0); |
| 1690 | 1694 | } |
| 1691 | 1695 | find_server_repository(0); |
| 1692 | 1696 | g.zRepositoryName = enter_chroot_jail(g.zRepositoryName); |
| 1693 | - cgi_handle_http_request(zIpAddr); | |
| 1697 | + if( useSCGI ){ | |
| 1698 | + cgi_handle_scgi_request(); | |
| 1699 | + }else{ | |
| 1700 | + cgi_handle_http_request(zIpAddr); | |
| 1701 | + } | |
| 1694 | 1702 | process_one_web_page(zNotFound, glob_create(zFileGlob)); |
| 1695 | 1703 | } |
| 1696 | 1704 | |
| 1697 | 1705 | /* |
| 1698 | 1706 | ** Note that the following command is used by ssh:// processing. |
| @@ -1784,10 +1792,11 @@ | ||
| 1784 | 1792 | ** -P|--port TCPPORT listen to request on port TCPPORT |
| 1785 | 1793 | ** --th-trace trace TH1 execution (for debugging purposes) |
| 1786 | 1794 | ** --baseurl URL Use URL as the base (useful for reverse proxies) |
| 1787 | 1795 | ** --notfound URL Redirect |
| 1788 | 1796 | ** --files GLOBLIST Comma-separated list of glob patterns for static files |
| 1797 | +** --scgi Accept SCGI rather than HTTP | |
| 1789 | 1798 | ** |
| 1790 | 1799 | ** See also: cgi, http, winsrv |
| 1791 | 1800 | */ |
| 1792 | 1801 | void cmd_webserver(void){ |
| 1793 | 1802 | int iPort, mxPort; /* Range of TCP ports allowed */ |
| @@ -1810,10 +1819,11 @@ | ||
| 1810 | 1819 | g.useLocalauth = find_option("localauth", 0, 0)!=0; |
| 1811 | 1820 | Th_InitTraceLog(); |
| 1812 | 1821 | zPort = find_option("port", "P", 1); |
| 1813 | 1822 | zNotFound = find_option("notfound", 0, 1); |
| 1814 | 1823 | zAltBase = find_option("baseurl", 0, 1); |
| 1824 | + if( find_option("scgi", 0, 0)!=0 ) flags |= HTTP_SERVER_SCGI; | |
| 1815 | 1825 | if( zAltBase ){ |
| 1816 | 1826 | set_base_url(zAltBase); |
| 1817 | 1827 | } |
| 1818 | 1828 | if ( find_option("localhost", 0, 0)!=0 ){ |
| 1819 | 1829 | flags |= HTTP_SERVER_LOCALHOST; |
| @@ -1874,11 +1884,15 @@ | ||
| 1874 | 1884 | fprintf(stderr, "====== SERVER pid %d =======\n", getpid()); |
| 1875 | 1885 | } |
| 1876 | 1886 | g.cgiOutput = 1; |
| 1877 | 1887 | find_server_repository(isUiCmd && zNotFound==0); |
| 1878 | 1888 | g.zRepositoryName = enter_chroot_jail(g.zRepositoryName); |
| 1879 | - cgi_handle_http_request(0); | |
| 1889 | + if( flags & HTTP_SERVER_SCGI ){ | |
| 1890 | + cgi_handle_scgi_request(); | |
| 1891 | + }else{ | |
| 1892 | + cgi_handle_http_request(0); | |
| 1893 | + } | |
| 1880 | 1894 | process_one_web_page(zNotFound, glob_create(zFileGlob)); |
| 1881 | 1895 | #else |
| 1882 | 1896 | /* Win32 implementation */ |
| 1883 | 1897 | if( isUiCmd ){ |
| 1884 | 1898 | zBrowser = db_get("web-browser", "start"); |
| 1885 | 1899 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -185,10 +185,11 @@ | |
| 185 | const char *zSSLIdentity; /* Value of --ssl-identity option, filename of |
| 186 | ** SSL client identity */ |
| 187 | int useLocalauth; /* No login required if from 127.0.0.1 */ |
| 188 | int noPswd; /* Logged in without password (on 127.0.0.1) */ |
| 189 | int userUid; /* Integer user id */ |
| 190 | |
| 191 | /* Information used to populate the RCVFROM table */ |
| 192 | int rcvid; /* The rcvid. 0 if not yet defined. */ |
| 193 | char *zIpAddr; /* The remote IP address */ |
| 194 | char *zNonce; /* The nonce used for login */ |
| @@ -1639,19 +1640,21 @@ | |
| 1639 | ** --https signal a request coming in via https |
| 1640 | ** --nossl signal that no SSL connections are available |
| 1641 | ** --notfound URL use URL as "HTTP 404, object not found" page. |
| 1642 | ** --files GLOB comma-separate glob patterns for static file to serve |
| 1643 | ** --baseurl URL base URL (useful with reverse proxies) |
| 1644 | ** |
| 1645 | ** See also: cgi, server, winsrv |
| 1646 | */ |
| 1647 | void cmd_http(void){ |
| 1648 | const char *zIpAddr; |
| 1649 | const char *zNotFound; |
| 1650 | const char *zHost; |
| 1651 | const char *zAltBase; |
| 1652 | const char *zFileGlob; |
| 1653 | |
| 1654 | /* The winhttp module passes the --files option as --files-urlenc with |
| 1655 | ** the argument being URL encoded, to avoid wildcard expansion in the |
| 1656 | ** shell. This option is for internal use and is undocumented. |
| 1657 | */ |
| @@ -1664,10 +1667,11 @@ | |
| 1664 | zFileGlob = find_option("files",0,1); |
| 1665 | } |
| 1666 | zNotFound = find_option("notfound", 0, 1); |
| 1667 | g.useLocalauth = find_option("localauth", 0, 0)!=0; |
| 1668 | g.sslNotAvailable = find_option("nossl", 0, 0)!=0; |
| 1669 | zAltBase = find_option("baseurl", 0, 1); |
| 1670 | if( zAltBase ) set_base_url(zAltBase); |
| 1671 | if( find_option("https",0,0)!=0 ) cgi_replace_parameter("HTTPS","on"); |
| 1672 | zHost = find_option("host", 0, 1); |
| 1673 | if( zHost ) cgi_replace_parameter("HTTP_HOST",zHost); |
| @@ -1688,11 +1692,15 @@ | |
| 1688 | if( zIpAddr==0 ){ |
| 1689 | zIpAddr = cgi_ssh_remote_addr(0); |
| 1690 | } |
| 1691 | find_server_repository(0); |
| 1692 | g.zRepositoryName = enter_chroot_jail(g.zRepositoryName); |
| 1693 | cgi_handle_http_request(zIpAddr); |
| 1694 | process_one_web_page(zNotFound, glob_create(zFileGlob)); |
| 1695 | } |
| 1696 | |
| 1697 | /* |
| 1698 | ** Note that the following command is used by ssh:// processing. |
| @@ -1784,10 +1792,11 @@ | |
| 1784 | ** -P|--port TCPPORT listen to request on port TCPPORT |
| 1785 | ** --th-trace trace TH1 execution (for debugging purposes) |
| 1786 | ** --baseurl URL Use URL as the base (useful for reverse proxies) |
| 1787 | ** --notfound URL Redirect |
| 1788 | ** --files GLOBLIST Comma-separated list of glob patterns for static files |
| 1789 | ** |
| 1790 | ** See also: cgi, http, winsrv |
| 1791 | */ |
| 1792 | void cmd_webserver(void){ |
| 1793 | int iPort, mxPort; /* Range of TCP ports allowed */ |
| @@ -1810,10 +1819,11 @@ | |
| 1810 | g.useLocalauth = find_option("localauth", 0, 0)!=0; |
| 1811 | Th_InitTraceLog(); |
| 1812 | zPort = find_option("port", "P", 1); |
| 1813 | zNotFound = find_option("notfound", 0, 1); |
| 1814 | zAltBase = find_option("baseurl", 0, 1); |
| 1815 | if( zAltBase ){ |
| 1816 | set_base_url(zAltBase); |
| 1817 | } |
| 1818 | if ( find_option("localhost", 0, 0)!=0 ){ |
| 1819 | flags |= HTTP_SERVER_LOCALHOST; |
| @@ -1874,11 +1884,15 @@ | |
| 1874 | fprintf(stderr, "====== SERVER pid %d =======\n", getpid()); |
| 1875 | } |
| 1876 | g.cgiOutput = 1; |
| 1877 | find_server_repository(isUiCmd && zNotFound==0); |
| 1878 | g.zRepositoryName = enter_chroot_jail(g.zRepositoryName); |
| 1879 | cgi_handle_http_request(0); |
| 1880 | process_one_web_page(zNotFound, glob_create(zFileGlob)); |
| 1881 | #else |
| 1882 | /* Win32 implementation */ |
| 1883 | if( isUiCmd ){ |
| 1884 | zBrowser = db_get("web-browser", "start"); |
| 1885 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -185,10 +185,11 @@ | |
| 185 | const char *zSSLIdentity; /* Value of --ssl-identity option, filename of |
| 186 | ** SSL client identity */ |
| 187 | int useLocalauth; /* No login required if from 127.0.0.1 */ |
| 188 | int noPswd; /* Logged in without password (on 127.0.0.1) */ |
| 189 | int userUid; /* Integer user id */ |
| 190 | int isHuman; /* True if access by a human, not a spider or bot */ |
| 191 | |
| 192 | /* Information used to populate the RCVFROM table */ |
| 193 | int rcvid; /* The rcvid. 0 if not yet defined. */ |
| 194 | char *zIpAddr; /* The remote IP address */ |
| 195 | char *zNonce; /* The nonce used for login */ |
| @@ -1639,19 +1640,21 @@ | |
| 1640 | ** --https signal a request coming in via https |
| 1641 | ** --nossl signal that no SSL connections are available |
| 1642 | ** --notfound URL use URL as "HTTP 404, object not found" page. |
| 1643 | ** --files GLOB comma-separate glob patterns for static file to serve |
| 1644 | ** --baseurl URL base URL (useful with reverse proxies) |
| 1645 | ** --scgi Interpret input as SCGI rather than HTTP |
| 1646 | ** |
| 1647 | ** See also: cgi, server, winsrv |
| 1648 | */ |
| 1649 | void cmd_http(void){ |
| 1650 | const char *zIpAddr; |
| 1651 | const char *zNotFound; |
| 1652 | const char *zHost; |
| 1653 | const char *zAltBase; |
| 1654 | const char *zFileGlob; |
| 1655 | int useSCGI; |
| 1656 | |
| 1657 | /* The winhttp module passes the --files option as --files-urlenc with |
| 1658 | ** the argument being URL encoded, to avoid wildcard expansion in the |
| 1659 | ** shell. This option is for internal use and is undocumented. |
| 1660 | */ |
| @@ -1664,10 +1667,11 @@ | |
| 1667 | zFileGlob = find_option("files",0,1); |
| 1668 | } |
| 1669 | zNotFound = find_option("notfound", 0, 1); |
| 1670 | g.useLocalauth = find_option("localauth", 0, 0)!=0; |
| 1671 | g.sslNotAvailable = find_option("nossl", 0, 0)!=0; |
| 1672 | useSCGI = find_option("scgi", 0, 0)!=0; |
| 1673 | zAltBase = find_option("baseurl", 0, 1); |
| 1674 | if( zAltBase ) set_base_url(zAltBase); |
| 1675 | if( find_option("https",0,0)!=0 ) cgi_replace_parameter("HTTPS","on"); |
| 1676 | zHost = find_option("host", 0, 1); |
| 1677 | if( zHost ) cgi_replace_parameter("HTTP_HOST",zHost); |
| @@ -1688,11 +1692,15 @@ | |
| 1692 | if( zIpAddr==0 ){ |
| 1693 | zIpAddr = cgi_ssh_remote_addr(0); |
| 1694 | } |
| 1695 | find_server_repository(0); |
| 1696 | g.zRepositoryName = enter_chroot_jail(g.zRepositoryName); |
| 1697 | if( useSCGI ){ |
| 1698 | cgi_handle_scgi_request(); |
| 1699 | }else{ |
| 1700 | cgi_handle_http_request(zIpAddr); |
| 1701 | } |
| 1702 | process_one_web_page(zNotFound, glob_create(zFileGlob)); |
| 1703 | } |
| 1704 | |
| 1705 | /* |
| 1706 | ** Note that the following command is used by ssh:// processing. |
| @@ -1784,10 +1792,11 @@ | |
| 1792 | ** -P|--port TCPPORT listen to request on port TCPPORT |
| 1793 | ** --th-trace trace TH1 execution (for debugging purposes) |
| 1794 | ** --baseurl URL Use URL as the base (useful for reverse proxies) |
| 1795 | ** --notfound URL Redirect |
| 1796 | ** --files GLOBLIST Comma-separated list of glob patterns for static files |
| 1797 | ** --scgi Accept SCGI rather than HTTP |
| 1798 | ** |
| 1799 | ** See also: cgi, http, winsrv |
| 1800 | */ |
| 1801 | void cmd_webserver(void){ |
| 1802 | int iPort, mxPort; /* Range of TCP ports allowed */ |
| @@ -1810,10 +1819,11 @@ | |
| 1819 | g.useLocalauth = find_option("localauth", 0, 0)!=0; |
| 1820 | Th_InitTraceLog(); |
| 1821 | zPort = find_option("port", "P", 1); |
| 1822 | zNotFound = find_option("notfound", 0, 1); |
| 1823 | zAltBase = find_option("baseurl", 0, 1); |
| 1824 | if( find_option("scgi", 0, 0)!=0 ) flags |= HTTP_SERVER_SCGI; |
| 1825 | if( zAltBase ){ |
| 1826 | set_base_url(zAltBase); |
| 1827 | } |
| 1828 | if ( find_option("localhost", 0, 0)!=0 ){ |
| 1829 | flags |= HTTP_SERVER_LOCALHOST; |
| @@ -1874,11 +1884,15 @@ | |
| 1884 | fprintf(stderr, "====== SERVER pid %d =======\n", getpid()); |
| 1885 | } |
| 1886 | g.cgiOutput = 1; |
| 1887 | find_server_repository(isUiCmd && zNotFound==0); |
| 1888 | g.zRepositoryName = enter_chroot_jail(g.zRepositoryName); |
| 1889 | if( flags & HTTP_SERVER_SCGI ){ |
| 1890 | cgi_handle_scgi_request(); |
| 1891 | }else{ |
| 1892 | cgi_handle_http_request(0); |
| 1893 | } |
| 1894 | process_one_web_page(zNotFound, glob_create(zFileGlob)); |
| 1895 | #else |
| 1896 | /* Win32 implementation */ |
| 1897 | if( isUiCmd ){ |
| 1898 | zBrowser = db_get("web-browser", "start"); |
| 1899 |
+16
-2
| --- src/main.c | ||
| +++ src/main.c | ||
| @@ -185,10 +185,11 @@ | ||
| 185 | 185 | const char *zSSLIdentity; /* Value of --ssl-identity option, filename of |
| 186 | 186 | ** SSL client identity */ |
| 187 | 187 | int useLocalauth; /* No login required if from 127.0.0.1 */ |
| 188 | 188 | int noPswd; /* Logged in without password (on 127.0.0.1) */ |
| 189 | 189 | int userUid; /* Integer user id */ |
| 190 | + int isHuman; /* True if access by a human, not a spider or bot */ | |
| 190 | 191 | |
| 191 | 192 | /* Information used to populate the RCVFROM table */ |
| 192 | 193 | int rcvid; /* The rcvid. 0 if not yet defined. */ |
| 193 | 194 | char *zIpAddr; /* The remote IP address */ |
| 194 | 195 | char *zNonce; /* The nonce used for login */ |
| @@ -1639,19 +1640,21 @@ | ||
| 1639 | 1640 | ** --https signal a request coming in via https |
| 1640 | 1641 | ** --nossl signal that no SSL connections are available |
| 1641 | 1642 | ** --notfound URL use URL as "HTTP 404, object not found" page. |
| 1642 | 1643 | ** --files GLOB comma-separate glob patterns for static file to serve |
| 1643 | 1644 | ** --baseurl URL base URL (useful with reverse proxies) |
| 1645 | +** --scgi Interpret input as SCGI rather than HTTP | |
| 1644 | 1646 | ** |
| 1645 | 1647 | ** See also: cgi, server, winsrv |
| 1646 | 1648 | */ |
| 1647 | 1649 | void cmd_http(void){ |
| 1648 | 1650 | const char *zIpAddr; |
| 1649 | 1651 | const char *zNotFound; |
| 1650 | 1652 | const char *zHost; |
| 1651 | 1653 | const char *zAltBase; |
| 1652 | 1654 | const char *zFileGlob; |
| 1655 | + int useSCGI; | |
| 1653 | 1656 | |
| 1654 | 1657 | /* The winhttp module passes the --files option as --files-urlenc with |
| 1655 | 1658 | ** the argument being URL encoded, to avoid wildcard expansion in the |
| 1656 | 1659 | ** shell. This option is for internal use and is undocumented. |
| 1657 | 1660 | */ |
| @@ -1664,10 +1667,11 @@ | ||
| 1664 | 1667 | zFileGlob = find_option("files",0,1); |
| 1665 | 1668 | } |
| 1666 | 1669 | zNotFound = find_option("notfound", 0, 1); |
| 1667 | 1670 | g.useLocalauth = find_option("localauth", 0, 0)!=0; |
| 1668 | 1671 | g.sslNotAvailable = find_option("nossl", 0, 0)!=0; |
| 1672 | + useSCGI = find_option("scgi", 0, 0)!=0; | |
| 1669 | 1673 | zAltBase = find_option("baseurl", 0, 1); |
| 1670 | 1674 | if( zAltBase ) set_base_url(zAltBase); |
| 1671 | 1675 | if( find_option("https",0,0)!=0 ) cgi_replace_parameter("HTTPS","on"); |
| 1672 | 1676 | zHost = find_option("host", 0, 1); |
| 1673 | 1677 | if( zHost ) cgi_replace_parameter("HTTP_HOST",zHost); |
| @@ -1688,11 +1692,15 @@ | ||
| 1688 | 1692 | if( zIpAddr==0 ){ |
| 1689 | 1693 | zIpAddr = cgi_ssh_remote_addr(0); |
| 1690 | 1694 | } |
| 1691 | 1695 | find_server_repository(0); |
| 1692 | 1696 | g.zRepositoryName = enter_chroot_jail(g.zRepositoryName); |
| 1693 | - cgi_handle_http_request(zIpAddr); | |
| 1697 | + if( useSCGI ){ | |
| 1698 | + cgi_handle_scgi_request(); | |
| 1699 | + }else{ | |
| 1700 | + cgi_handle_http_request(zIpAddr); | |
| 1701 | + } | |
| 1694 | 1702 | process_one_web_page(zNotFound, glob_create(zFileGlob)); |
| 1695 | 1703 | } |
| 1696 | 1704 | |
| 1697 | 1705 | /* |
| 1698 | 1706 | ** Note that the following command is used by ssh:// processing. |
| @@ -1784,10 +1792,11 @@ | ||
| 1784 | 1792 | ** -P|--port TCPPORT listen to request on port TCPPORT |
| 1785 | 1793 | ** --th-trace trace TH1 execution (for debugging purposes) |
| 1786 | 1794 | ** --baseurl URL Use URL as the base (useful for reverse proxies) |
| 1787 | 1795 | ** --notfound URL Redirect |
| 1788 | 1796 | ** --files GLOBLIST Comma-separated list of glob patterns for static files |
| 1797 | +** --scgi Accept SCGI rather than HTTP | |
| 1789 | 1798 | ** |
| 1790 | 1799 | ** See also: cgi, http, winsrv |
| 1791 | 1800 | */ |
| 1792 | 1801 | void cmd_webserver(void){ |
| 1793 | 1802 | int iPort, mxPort; /* Range of TCP ports allowed */ |
| @@ -1810,10 +1819,11 @@ | ||
| 1810 | 1819 | g.useLocalauth = find_option("localauth", 0, 0)!=0; |
| 1811 | 1820 | Th_InitTraceLog(); |
| 1812 | 1821 | zPort = find_option("port", "P", 1); |
| 1813 | 1822 | zNotFound = find_option("notfound", 0, 1); |
| 1814 | 1823 | zAltBase = find_option("baseurl", 0, 1); |
| 1824 | + if( find_option("scgi", 0, 0)!=0 ) flags |= HTTP_SERVER_SCGI; | |
| 1815 | 1825 | if( zAltBase ){ |
| 1816 | 1826 | set_base_url(zAltBase); |
| 1817 | 1827 | } |
| 1818 | 1828 | if ( find_option("localhost", 0, 0)!=0 ){ |
| 1819 | 1829 | flags |= HTTP_SERVER_LOCALHOST; |
| @@ -1874,11 +1884,15 @@ | ||
| 1874 | 1884 | fprintf(stderr, "====== SERVER pid %d =======\n", getpid()); |
| 1875 | 1885 | } |
| 1876 | 1886 | g.cgiOutput = 1; |
| 1877 | 1887 | find_server_repository(isUiCmd && zNotFound==0); |
| 1878 | 1888 | g.zRepositoryName = enter_chroot_jail(g.zRepositoryName); |
| 1879 | - cgi_handle_http_request(0); | |
| 1889 | + if( flags & HTTP_SERVER_SCGI ){ | |
| 1890 | + cgi_handle_scgi_request(); | |
| 1891 | + }else{ | |
| 1892 | + cgi_handle_http_request(0); | |
| 1893 | + } | |
| 1880 | 1894 | process_one_web_page(zNotFound, glob_create(zFileGlob)); |
| 1881 | 1895 | #else |
| 1882 | 1896 | /* Win32 implementation */ |
| 1883 | 1897 | if( isUiCmd ){ |
| 1884 | 1898 | zBrowser = db_get("web-browser", "start"); |
| 1885 | 1899 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -185,10 +185,11 @@ | |
| 185 | const char *zSSLIdentity; /* Value of --ssl-identity option, filename of |
| 186 | ** SSL client identity */ |
| 187 | int useLocalauth; /* No login required if from 127.0.0.1 */ |
| 188 | int noPswd; /* Logged in without password (on 127.0.0.1) */ |
| 189 | int userUid; /* Integer user id */ |
| 190 | |
| 191 | /* Information used to populate the RCVFROM table */ |
| 192 | int rcvid; /* The rcvid. 0 if not yet defined. */ |
| 193 | char *zIpAddr; /* The remote IP address */ |
| 194 | char *zNonce; /* The nonce used for login */ |
| @@ -1639,19 +1640,21 @@ | |
| 1639 | ** --https signal a request coming in via https |
| 1640 | ** --nossl signal that no SSL connections are available |
| 1641 | ** --notfound URL use URL as "HTTP 404, object not found" page. |
| 1642 | ** --files GLOB comma-separate glob patterns for static file to serve |
| 1643 | ** --baseurl URL base URL (useful with reverse proxies) |
| 1644 | ** |
| 1645 | ** See also: cgi, server, winsrv |
| 1646 | */ |
| 1647 | void cmd_http(void){ |
| 1648 | const char *zIpAddr; |
| 1649 | const char *zNotFound; |
| 1650 | const char *zHost; |
| 1651 | const char *zAltBase; |
| 1652 | const char *zFileGlob; |
| 1653 | |
| 1654 | /* The winhttp module passes the --files option as --files-urlenc with |
| 1655 | ** the argument being URL encoded, to avoid wildcard expansion in the |
| 1656 | ** shell. This option is for internal use and is undocumented. |
| 1657 | */ |
| @@ -1664,10 +1667,11 @@ | |
| 1664 | zFileGlob = find_option("files",0,1); |
| 1665 | } |
| 1666 | zNotFound = find_option("notfound", 0, 1); |
| 1667 | g.useLocalauth = find_option("localauth", 0, 0)!=0; |
| 1668 | g.sslNotAvailable = find_option("nossl", 0, 0)!=0; |
| 1669 | zAltBase = find_option("baseurl", 0, 1); |
| 1670 | if( zAltBase ) set_base_url(zAltBase); |
| 1671 | if( find_option("https",0,0)!=0 ) cgi_replace_parameter("HTTPS","on"); |
| 1672 | zHost = find_option("host", 0, 1); |
| 1673 | if( zHost ) cgi_replace_parameter("HTTP_HOST",zHost); |
| @@ -1688,11 +1692,15 @@ | |
| 1688 | if( zIpAddr==0 ){ |
| 1689 | zIpAddr = cgi_ssh_remote_addr(0); |
| 1690 | } |
| 1691 | find_server_repository(0); |
| 1692 | g.zRepositoryName = enter_chroot_jail(g.zRepositoryName); |
| 1693 | cgi_handle_http_request(zIpAddr); |
| 1694 | process_one_web_page(zNotFound, glob_create(zFileGlob)); |
| 1695 | } |
| 1696 | |
| 1697 | /* |
| 1698 | ** Note that the following command is used by ssh:// processing. |
| @@ -1784,10 +1792,11 @@ | |
| 1784 | ** -P|--port TCPPORT listen to request on port TCPPORT |
| 1785 | ** --th-trace trace TH1 execution (for debugging purposes) |
| 1786 | ** --baseurl URL Use URL as the base (useful for reverse proxies) |
| 1787 | ** --notfound URL Redirect |
| 1788 | ** --files GLOBLIST Comma-separated list of glob patterns for static files |
| 1789 | ** |
| 1790 | ** See also: cgi, http, winsrv |
| 1791 | */ |
| 1792 | void cmd_webserver(void){ |
| 1793 | int iPort, mxPort; /* Range of TCP ports allowed */ |
| @@ -1810,10 +1819,11 @@ | |
| 1810 | g.useLocalauth = find_option("localauth", 0, 0)!=0; |
| 1811 | Th_InitTraceLog(); |
| 1812 | zPort = find_option("port", "P", 1); |
| 1813 | zNotFound = find_option("notfound", 0, 1); |
| 1814 | zAltBase = find_option("baseurl", 0, 1); |
| 1815 | if( zAltBase ){ |
| 1816 | set_base_url(zAltBase); |
| 1817 | } |
| 1818 | if ( find_option("localhost", 0, 0)!=0 ){ |
| 1819 | flags |= HTTP_SERVER_LOCALHOST; |
| @@ -1874,11 +1884,15 @@ | |
| 1874 | fprintf(stderr, "====== SERVER pid %d =======\n", getpid()); |
| 1875 | } |
| 1876 | g.cgiOutput = 1; |
| 1877 | find_server_repository(isUiCmd && zNotFound==0); |
| 1878 | g.zRepositoryName = enter_chroot_jail(g.zRepositoryName); |
| 1879 | cgi_handle_http_request(0); |
| 1880 | process_one_web_page(zNotFound, glob_create(zFileGlob)); |
| 1881 | #else |
| 1882 | /* Win32 implementation */ |
| 1883 | if( isUiCmd ){ |
| 1884 | zBrowser = db_get("web-browser", "start"); |
| 1885 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -185,10 +185,11 @@ | |
| 185 | const char *zSSLIdentity; /* Value of --ssl-identity option, filename of |
| 186 | ** SSL client identity */ |
| 187 | int useLocalauth; /* No login required if from 127.0.0.1 */ |
| 188 | int noPswd; /* Logged in without password (on 127.0.0.1) */ |
| 189 | int userUid; /* Integer user id */ |
| 190 | int isHuman; /* True if access by a human, not a spider or bot */ |
| 191 | |
| 192 | /* Information used to populate the RCVFROM table */ |
| 193 | int rcvid; /* The rcvid. 0 if not yet defined. */ |
| 194 | char *zIpAddr; /* The remote IP address */ |
| 195 | char *zNonce; /* The nonce used for login */ |
| @@ -1639,19 +1640,21 @@ | |
| 1640 | ** --https signal a request coming in via https |
| 1641 | ** --nossl signal that no SSL connections are available |
| 1642 | ** --notfound URL use URL as "HTTP 404, object not found" page. |
| 1643 | ** --files GLOB comma-separate glob patterns for static file to serve |
| 1644 | ** --baseurl URL base URL (useful with reverse proxies) |
| 1645 | ** --scgi Interpret input as SCGI rather than HTTP |
| 1646 | ** |
| 1647 | ** See also: cgi, server, winsrv |
| 1648 | */ |
| 1649 | void cmd_http(void){ |
| 1650 | const char *zIpAddr; |
| 1651 | const char *zNotFound; |
| 1652 | const char *zHost; |
| 1653 | const char *zAltBase; |
| 1654 | const char *zFileGlob; |
| 1655 | int useSCGI; |
| 1656 | |
| 1657 | /* The winhttp module passes the --files option as --files-urlenc with |
| 1658 | ** the argument being URL encoded, to avoid wildcard expansion in the |
| 1659 | ** shell. This option is for internal use and is undocumented. |
| 1660 | */ |
| @@ -1664,10 +1667,11 @@ | |
| 1667 | zFileGlob = find_option("files",0,1); |
| 1668 | } |
| 1669 | zNotFound = find_option("notfound", 0, 1); |
| 1670 | g.useLocalauth = find_option("localauth", 0, 0)!=0; |
| 1671 | g.sslNotAvailable = find_option("nossl", 0, 0)!=0; |
| 1672 | useSCGI = find_option("scgi", 0, 0)!=0; |
| 1673 | zAltBase = find_option("baseurl", 0, 1); |
| 1674 | if( zAltBase ) set_base_url(zAltBase); |
| 1675 | if( find_option("https",0,0)!=0 ) cgi_replace_parameter("HTTPS","on"); |
| 1676 | zHost = find_option("host", 0, 1); |
| 1677 | if( zHost ) cgi_replace_parameter("HTTP_HOST",zHost); |
| @@ -1688,11 +1692,15 @@ | |
| 1692 | if( zIpAddr==0 ){ |
| 1693 | zIpAddr = cgi_ssh_remote_addr(0); |
| 1694 | } |
| 1695 | find_server_repository(0); |
| 1696 | g.zRepositoryName = enter_chroot_jail(g.zRepositoryName); |
| 1697 | if( useSCGI ){ |
| 1698 | cgi_handle_scgi_request(); |
| 1699 | }else{ |
| 1700 | cgi_handle_http_request(zIpAddr); |
| 1701 | } |
| 1702 | process_one_web_page(zNotFound, glob_create(zFileGlob)); |
| 1703 | } |
| 1704 | |
| 1705 | /* |
| 1706 | ** Note that the following command is used by ssh:// processing. |
| @@ -1784,10 +1792,11 @@ | |
| 1792 | ** -P|--port TCPPORT listen to request on port TCPPORT |
| 1793 | ** --th-trace trace TH1 execution (for debugging purposes) |
| 1794 | ** --baseurl URL Use URL as the base (useful for reverse proxies) |
| 1795 | ** --notfound URL Redirect |
| 1796 | ** --files GLOBLIST Comma-separated list of glob patterns for static files |
| 1797 | ** --scgi Accept SCGI rather than HTTP |
| 1798 | ** |
| 1799 | ** See also: cgi, http, winsrv |
| 1800 | */ |
| 1801 | void cmd_webserver(void){ |
| 1802 | int iPort, mxPort; /* Range of TCP ports allowed */ |
| @@ -1810,10 +1819,11 @@ | |
| 1819 | g.useLocalauth = find_option("localauth", 0, 0)!=0; |
| 1820 | Th_InitTraceLog(); |
| 1821 | zPort = find_option("port", "P", 1); |
| 1822 | zNotFound = find_option("notfound", 0, 1); |
| 1823 | zAltBase = find_option("baseurl", 0, 1); |
| 1824 | if( find_option("scgi", 0, 0)!=0 ) flags |= HTTP_SERVER_SCGI; |
| 1825 | if( zAltBase ){ |
| 1826 | set_base_url(zAltBase); |
| 1827 | } |
| 1828 | if ( find_option("localhost", 0, 0)!=0 ){ |
| 1829 | flags |= HTTP_SERVER_LOCALHOST; |
| @@ -1874,11 +1884,15 @@ | |
| 1884 | fprintf(stderr, "====== SERVER pid %d =======\n", getpid()); |
| 1885 | } |
| 1886 | g.cgiOutput = 1; |
| 1887 | find_server_repository(isUiCmd && zNotFound==0); |
| 1888 | g.zRepositoryName = enter_chroot_jail(g.zRepositoryName); |
| 1889 | if( flags & HTTP_SERVER_SCGI ){ |
| 1890 | cgi_handle_scgi_request(); |
| 1891 | }else{ |
| 1892 | cgi_handle_http_request(0); |
| 1893 | } |
| 1894 | process_one_web_page(zNotFound, glob_create(zFileGlob)); |
| 1895 | #else |
| 1896 | /* Win32 implementation */ |
| 1897 | if( isUiCmd ){ |
| 1898 | zBrowser = db_get("web-browser", "start"); |
| 1899 |
+12
-4
| --- src/manifest.c | ||
| +++ src/manifest.c | ||
| @@ -660,11 +660,11 @@ | ||
| 660 | 660 | } |
| 661 | 661 | |
| 662 | 662 | /* |
| 663 | 663 | ** P <uuid> ... |
| 664 | 664 | ** |
| 665 | - ** Specify one or more other artifacts where are the parents of | |
| 665 | + ** Specify one or more other artifacts which are the parents of | |
| 666 | 666 | ** this artifact. The first parent is the primary parent. All |
| 667 | 667 | ** others are parents by merge. |
| 668 | 668 | */ |
| 669 | 669 | case 'P': { |
| 670 | 670 | while( (zUuid = next_token(&x, &sz))!=0 ){ |
| @@ -783,11 +783,11 @@ | ||
| 783 | 783 | ** Identify the user who created this control file by their |
| 784 | 784 | ** login. Only one U line is allowed. Prohibited in clusters. |
| 785 | 785 | ** If the user name is omitted, take that to be "anonymous". |
| 786 | 786 | */ |
| 787 | 787 | case 'U': { |
| 788 | - if( p->zUser!=0 ) SYNTAX("more than on U-card"); | |
| 788 | + if( p->zUser!=0 ) SYNTAX("more than one U-card"); | |
| 789 | 789 | p->zUser = next_token(&x, 0); |
| 790 | 790 | if( p->zUser==0 ){ |
| 791 | 791 | p->zUser = "anonymous"; |
| 792 | 792 | }else{ |
| 793 | 793 | defossilize(p->zUser); |
| @@ -1965,13 +1965,21 @@ | ||
| 1965 | 1965 | }else if( memcmp(zName, "+sym-",5)==0 ){ |
| 1966 | 1966 | blob_appendf(&comment, " Add tag \"%h\".", &zName[5]); |
| 1967 | 1967 | }else if( memcmp(zName, "-sym-",5)==0 ){ |
| 1968 | 1968 | blob_appendf(&comment, " Cancel tag \"%h\".", &zName[5]); |
| 1969 | 1969 | }else if( strcmp(zName, "+closed")==0 ){ |
| 1970 | - blob_appendf(&comment, " Marked \"Closed\"."); | |
| 1970 | + blob_append(&comment, " Marked \"Closed\"", -1); | |
| 1971 | + if( zValue && *zValue ){ | |
| 1972 | + blob_appendf(&comment, " with note \"%h\"", zValue); | |
| 1973 | + } | |
| 1974 | + blob_append(&comment, ".", 1); | |
| 1971 | 1975 | }else if( strcmp(zName, "-closed")==0 ){ |
| 1972 | - blob_appendf(&comment, " Removed the \"Closed\" mark."); | |
| 1976 | + blob_append(&comment, " Removed the \"Closed\" mark", -1); | |
| 1977 | + if( zValue && *zValue ){ | |
| 1978 | + blob_appendf(&comment, " with note \"%h\"", zValue); | |
| 1979 | + } | |
| 1980 | + blob_append(&comment, ".", 1); | |
| 1973 | 1981 | }else { |
| 1974 | 1982 | if( zName[0]=='-' ){ |
| 1975 | 1983 | blob_appendf(&comment, " Cancel \"%h\"", &zName[1]); |
| 1976 | 1984 | }else if( zName[0]=='+' ){ |
| 1977 | 1985 | blob_appendf(&comment, " Add \"%h\"", &zName[1]); |
| 1978 | 1986 |
| --- src/manifest.c | |
| +++ src/manifest.c | |
| @@ -660,11 +660,11 @@ | |
| 660 | } |
| 661 | |
| 662 | /* |
| 663 | ** P <uuid> ... |
| 664 | ** |
| 665 | ** Specify one or more other artifacts where are the parents of |
| 666 | ** this artifact. The first parent is the primary parent. All |
| 667 | ** others are parents by merge. |
| 668 | */ |
| 669 | case 'P': { |
| 670 | while( (zUuid = next_token(&x, &sz))!=0 ){ |
| @@ -783,11 +783,11 @@ | |
| 783 | ** Identify the user who created this control file by their |
| 784 | ** login. Only one U line is allowed. Prohibited in clusters. |
| 785 | ** If the user name is omitted, take that to be "anonymous". |
| 786 | */ |
| 787 | case 'U': { |
| 788 | if( p->zUser!=0 ) SYNTAX("more than on U-card"); |
| 789 | p->zUser = next_token(&x, 0); |
| 790 | if( p->zUser==0 ){ |
| 791 | p->zUser = "anonymous"; |
| 792 | }else{ |
| 793 | defossilize(p->zUser); |
| @@ -1965,13 +1965,21 @@ | |
| 1965 | }else if( memcmp(zName, "+sym-",5)==0 ){ |
| 1966 | blob_appendf(&comment, " Add tag \"%h\".", &zName[5]); |
| 1967 | }else if( memcmp(zName, "-sym-",5)==0 ){ |
| 1968 | blob_appendf(&comment, " Cancel tag \"%h\".", &zName[5]); |
| 1969 | }else if( strcmp(zName, "+closed")==0 ){ |
| 1970 | blob_appendf(&comment, " Marked \"Closed\"."); |
| 1971 | }else if( strcmp(zName, "-closed")==0 ){ |
| 1972 | blob_appendf(&comment, " Removed the \"Closed\" mark."); |
| 1973 | }else { |
| 1974 | if( zName[0]=='-' ){ |
| 1975 | blob_appendf(&comment, " Cancel \"%h\"", &zName[1]); |
| 1976 | }else if( zName[0]=='+' ){ |
| 1977 | blob_appendf(&comment, " Add \"%h\"", &zName[1]); |
| 1978 |
| --- src/manifest.c | |
| +++ src/manifest.c | |
| @@ -660,11 +660,11 @@ | |
| 660 | } |
| 661 | |
| 662 | /* |
| 663 | ** P <uuid> ... |
| 664 | ** |
| 665 | ** Specify one or more other artifacts which are the parents of |
| 666 | ** this artifact. The first parent is the primary parent. All |
| 667 | ** others are parents by merge. |
| 668 | */ |
| 669 | case 'P': { |
| 670 | while( (zUuid = next_token(&x, &sz))!=0 ){ |
| @@ -783,11 +783,11 @@ | |
| 783 | ** Identify the user who created this control file by their |
| 784 | ** login. Only one U line is allowed. Prohibited in clusters. |
| 785 | ** If the user name is omitted, take that to be "anonymous". |
| 786 | */ |
| 787 | case 'U': { |
| 788 | if( p->zUser!=0 ) SYNTAX("more than one U-card"); |
| 789 | p->zUser = next_token(&x, 0); |
| 790 | if( p->zUser==0 ){ |
| 791 | p->zUser = "anonymous"; |
| 792 | }else{ |
| 793 | defossilize(p->zUser); |
| @@ -1965,13 +1965,21 @@ | |
| 1965 | }else if( memcmp(zName, "+sym-",5)==0 ){ |
| 1966 | blob_appendf(&comment, " Add tag \"%h\".", &zName[5]); |
| 1967 | }else if( memcmp(zName, "-sym-",5)==0 ){ |
| 1968 | blob_appendf(&comment, " Cancel tag \"%h\".", &zName[5]); |
| 1969 | }else if( strcmp(zName, "+closed")==0 ){ |
| 1970 | blob_append(&comment, " Marked \"Closed\"", -1); |
| 1971 | if( zValue && *zValue ){ |
| 1972 | blob_appendf(&comment, " with note \"%h\"", zValue); |
| 1973 | } |
| 1974 | blob_append(&comment, ".", 1); |
| 1975 | }else if( strcmp(zName, "-closed")==0 ){ |
| 1976 | blob_append(&comment, " Removed the \"Closed\" mark", -1); |
| 1977 | if( zValue && *zValue ){ |
| 1978 | blob_appendf(&comment, " with note \"%h\"", zValue); |
| 1979 | } |
| 1980 | blob_append(&comment, ".", 1); |
| 1981 | }else { |
| 1982 | if( zName[0]=='-' ){ |
| 1983 | blob_appendf(&comment, " Cancel \"%h\"", &zName[1]); |
| 1984 | }else if( zName[0]=='+' ){ |
| 1985 | blob_appendf(&comment, " Add \"%h\"", &zName[1]); |
| 1986 |
+71
-56
| --- src/sqlite3.c | ||
| +++ src/sqlite3.c | ||
| @@ -656,11 +656,11 @@ | ||
| 656 | 656 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 657 | 657 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 658 | 658 | */ |
| 659 | 659 | #define SQLITE_VERSION "3.8.0" |
| 660 | 660 | #define SQLITE_VERSION_NUMBER 3008000 |
| 661 | -#define SQLITE_SOURCE_ID "2013-08-06 07:45:08 924f7e4d7a8fa2fe9100836663f3733b6e1a9084" | |
| 661 | +#define SQLITE_SOURCE_ID "2013-08-15 22:40:21 f2d175f975cd0be63425424ec322a98fb650019e" | |
| 662 | 662 | |
| 663 | 663 | /* |
| 664 | 664 | ** CAPI3REF: Run-Time Library Version Numbers |
| 665 | 665 | ** KEYWORDS: sqlite3_version, sqlite3_sourceid |
| 666 | 666 | ** |
| @@ -7780,11 +7780,11 @@ | ||
| 7780 | 7780 | #endif |
| 7781 | 7781 | |
| 7782 | 7782 | #if 0 |
| 7783 | 7783 | } /* End of the 'extern "C"' block */ |
| 7784 | 7784 | #endif |
| 7785 | -#endif | |
| 7785 | +#endif /* _SQLITE3_H_ */ | |
| 7786 | 7786 | |
| 7787 | 7787 | /* |
| 7788 | 7788 | ** 2010 August 30 |
| 7789 | 7789 | ** |
| 7790 | 7790 | ** The author disclaims copyright to this source code. In place of |
| @@ -11214,11 +11214,11 @@ | ||
| 11214 | 11214 | ** subqueries looking for a match. |
| 11215 | 11215 | */ |
| 11216 | 11216 | struct NameContext { |
| 11217 | 11217 | Parse *pParse; /* The parser */ |
| 11218 | 11218 | SrcList *pSrcList; /* One or more tables used to resolve names */ |
| 11219 | - ExprList *pEList; /* Optional list of named expressions */ | |
| 11219 | + ExprList *pEList; /* Optional list of result-set columns */ | |
| 11220 | 11220 | AggInfo *pAggInfo; /* Information about aggregates at this level */ |
| 11221 | 11221 | NameContext *pNext; /* Next outer name context. NULL for outermost */ |
| 11222 | 11222 | int nRef; /* Number of names resolved by this context */ |
| 11223 | 11223 | int nErr; /* Number of errors encountered while resolving names */ |
| 11224 | 11224 | u8 ncFlags; /* Zero or more NC_* flags defined below */ |
| @@ -11229,13 +11229,11 @@ | ||
| 11229 | 11229 | */ |
| 11230 | 11230 | #define NC_AllowAgg 0x01 /* Aggregate functions are allowed here */ |
| 11231 | 11231 | #define NC_HasAgg 0x02 /* One or more aggregate functions seen */ |
| 11232 | 11232 | #define NC_IsCheck 0x04 /* True if resolving names in a CHECK constraint */ |
| 11233 | 11233 | #define NC_InAggFunc 0x08 /* True if analyzing arguments to an agg func */ |
| 11234 | -#define NC_AsMaybe 0x10 /* Resolve to AS terms of the result set only | |
| 11235 | - ** if no other resolution is available */ | |
| 11236 | -#define NC_PartIdx 0x20 /* True if resolving a partial index WHERE */ | |
| 11234 | +#define NC_PartIdx 0x10 /* True if resolving a partial index WHERE */ | |
| 11237 | 11235 | |
| 11238 | 11236 | /* |
| 11239 | 11237 | ** An instance of the following structure contains all information |
| 11240 | 11238 | ** needed to generate code for a single SELECT statement. |
| 11241 | 11239 | ** |
| @@ -17231,17 +17229,17 @@ | ||
| 17231 | 17229 | u8 *aCtrl; |
| 17232 | 17230 | |
| 17233 | 17231 | } mem5; |
| 17234 | 17232 | |
| 17235 | 17233 | /* |
| 17236 | -** Access the static variable through a macro for SQLITE_OMIT_WSD | |
| 17234 | +** Access the static variable through a macro for SQLITE_OMIT_WSD. | |
| 17237 | 17235 | */ |
| 17238 | 17236 | #define mem5 GLOBAL(struct Mem5Global, mem5) |
| 17239 | 17237 | |
| 17240 | 17238 | /* |
| 17241 | 17239 | ** Assuming mem5.zPool is divided up into an array of Mem5Link |
| 17242 | -** structures, return a pointer to the idx-th such lik. | |
| 17240 | +** structures, return a pointer to the idx-th such link. | |
| 17243 | 17241 | */ |
| 17244 | 17242 | #define MEM5LINK(idx) ((Mem5Link *)(&mem5.zPool[(idx)*mem5.szAtom])) |
| 17245 | 17243 | |
| 17246 | 17244 | /* |
| 17247 | 17245 | ** Unlink the chunk at mem5.aPool[i] from list it is currently |
| @@ -17333,11 +17331,11 @@ | ||
| 17333 | 17331 | |
| 17334 | 17332 | /* |
| 17335 | 17333 | ** Return a block of memory of at least nBytes in size. |
| 17336 | 17334 | ** Return NULL if unable. Return NULL if nBytes==0. |
| 17337 | 17335 | ** |
| 17338 | -** The caller guarantees that nByte positive. | |
| 17336 | +** The caller guarantees that nByte is positive. | |
| 17339 | 17337 | ** |
| 17340 | 17338 | ** The caller has obtained a mutex prior to invoking this |
| 17341 | 17339 | ** routine so there is never any chance that two or more |
| 17342 | 17340 | ** threads can be in this routine at the same time. |
| 17343 | 17341 | */ |
| @@ -17455,11 +17453,11 @@ | ||
| 17455 | 17453 | } |
| 17456 | 17454 | memsys5Link(iBlock, iLogsize); |
| 17457 | 17455 | } |
| 17458 | 17456 | |
| 17459 | 17457 | /* |
| 17460 | -** Allocate nBytes of memory | |
| 17458 | +** Allocate nBytes of memory. | |
| 17461 | 17459 | */ |
| 17462 | 17460 | static void *memsys5Malloc(int nBytes){ |
| 17463 | 17461 | sqlite3_int64 *p = 0; |
| 17464 | 17462 | if( nBytes>0 ){ |
| 17465 | 17463 | memsys5Enter(); |
| @@ -74041,22 +74039,23 @@ | ||
| 74041 | 74039 | ** |
| 74042 | 74040 | ** The reason for suppressing the TK_AS term when the expression is a simple |
| 74043 | 74041 | ** column reference is so that the column reference will be recognized as |
| 74044 | 74042 | ** usable by indices within the WHERE clause processing logic. |
| 74045 | 74043 | ** |
| 74046 | -** Hack: The TK_AS operator is inhibited if zType[0]=='G'. This means | |
| 74044 | +** The TK_AS operator is inhibited if zType[0]=='G'. This means | |
| 74047 | 74045 | ** that in a GROUP BY clause, the expression is evaluated twice. Hence: |
| 74048 | 74046 | ** |
| 74049 | 74047 | ** SELECT random()%5 AS x, count(*) FROM tab GROUP BY x |
| 74050 | 74048 | ** |
| 74051 | 74049 | ** Is equivalent to: |
| 74052 | 74050 | ** |
| 74053 | 74051 | ** SELECT random()%5 AS x, count(*) FROM tab GROUP BY random()%5 |
| 74054 | 74052 | ** |
| 74055 | 74053 | ** The result of random()%5 in the GROUP BY clause is probably different |
| 74056 | -** from the result in the result-set. We might fix this someday. Or | |
| 74057 | -** then again, we might not... | |
| 74054 | +** from the result in the result-set. On the other hand Standard SQL does | |
| 74055 | +** not allow the GROUP BY clause to contain references to result-set columns. | |
| 74056 | +** So this should never come up in well-formed queries. | |
| 74058 | 74057 | ** |
| 74059 | 74058 | ** If the reference is followed by a COLLATE operator, then make sure |
| 74060 | 74059 | ** the COLLATE operator is preserved. For example: |
| 74061 | 74060 | ** |
| 74062 | 74061 | ** SELECT a+b, c+d FROM t1 ORDER BY 1 COLLATE nocase; |
| @@ -74382,14 +74381,20 @@ | ||
| 74382 | 74381 | ** |
| 74383 | 74382 | ** In cases like this, replace pExpr with a copy of the expression that |
| 74384 | 74383 | ** forms the result set entry ("a+b" in the example) and return immediately. |
| 74385 | 74384 | ** Note that the expression in the result set should have already been |
| 74386 | 74385 | ** resolved by the time the WHERE clause is resolved. |
| 74386 | + ** | |
| 74387 | + ** The ability to use an output result-set column in the WHERE, GROUP BY, | |
| 74388 | + ** or HAVING clauses, or as part of a larger expression in the ORDRE BY | |
| 74389 | + ** clause is not standard SQL. This is a (goofy) SQLite extension, that | |
| 74390 | + ** is supported for backwards compatibility only. TO DO: Issue a warning | |
| 74391 | + ** on sqlite3_log() whenever the capability is used. | |
| 74387 | 74392 | */ |
| 74388 | 74393 | if( (pEList = pNC->pEList)!=0 |
| 74389 | 74394 | && zTab==0 |
| 74390 | - && ((pNC->ncFlags & NC_AsMaybe)==0 || cnt==0) | |
| 74395 | + && cnt==0 | |
| 74391 | 74396 | ){ |
| 74392 | 74397 | for(j=0; j<pEList->nExpr; j++){ |
| 74393 | 74398 | char *zAs = pEList->a[j].zName; |
| 74394 | 74399 | if( zAs!=0 && sqlite3StrICmp(zAs, zCol)==0 ){ |
| 74395 | 74400 | Expr *pOrig; |
| @@ -74947,11 +74952,11 @@ | ||
| 74947 | 74952 | } |
| 74948 | 74953 | |
| 74949 | 74954 | /* |
| 74950 | 74955 | ** Check every term in the ORDER BY or GROUP BY clause pOrderBy of |
| 74951 | 74956 | ** the SELECT statement pSelect. If any term is reference to a |
| 74952 | -** result set expression (as determined by the ExprList.a.iCol field) | |
| 74957 | +** result set expression (as determined by the ExprList.a.iOrderByCol field) | |
| 74953 | 74958 | ** then convert that term into a copy of the corresponding result set |
| 74954 | 74959 | ** column. |
| 74955 | 74960 | ** |
| 74956 | 74961 | ** If any errors are detected, add an error message to pParse and |
| 74957 | 74962 | ** return non-zero. Return zero if no errors are seen. |
| @@ -74995,11 +75000,11 @@ | ||
| 74995 | 75000 | ** |
| 74996 | 75001 | ** This routine resolves each term of the clause into an expression. |
| 74997 | 75002 | ** If the order-by term is an integer I between 1 and N (where N is the |
| 74998 | 75003 | ** number of columns in the result set of the SELECT) then the expression |
| 74999 | 75004 | ** in the resolution is a copy of the I-th result-set expression. If |
| 75000 | -** the order-by term is an identify that corresponds to the AS-name of | |
| 75005 | +** the order-by term is an identifier that corresponds to the AS-name of | |
| 75001 | 75006 | ** a result-set expression, then the term resolves to a copy of the |
| 75002 | 75007 | ** result-set expression. Otherwise, the expression is resolved in |
| 75003 | 75008 | ** the usual way - using sqlite3ResolveExprNames(). |
| 75004 | 75009 | ** |
| 75005 | 75010 | ** This routine returns the number of errors. If errors occur, then |
| @@ -75021,20 +75026,23 @@ | ||
| 75021 | 75026 | if( pOrderBy==0 ) return 0; |
| 75022 | 75027 | nResult = pSelect->pEList->nExpr; |
| 75023 | 75028 | pParse = pNC->pParse; |
| 75024 | 75029 | for(i=0, pItem=pOrderBy->a; i<pOrderBy->nExpr; i++, pItem++){ |
| 75025 | 75030 | Expr *pE = pItem->pExpr; |
| 75026 | - iCol = resolveAsName(pParse, pSelect->pEList, pE); | |
| 75027 | - if( iCol>0 ){ | |
| 75028 | - /* If an AS-name match is found, mark this ORDER BY column as being | |
| 75029 | - ** a copy of the iCol-th result-set column. The subsequent call to | |
| 75030 | - ** sqlite3ResolveOrderGroupBy() will convert the expression to a | |
| 75031 | - ** copy of the iCol-th result-set expression. */ | |
| 75032 | - pItem->iOrderByCol = (u16)iCol; | |
| 75033 | - continue; | |
| 75034 | - } | |
| 75035 | - if( sqlite3ExprIsInteger(sqlite3ExprSkipCollate(pE), &iCol) ){ | |
| 75031 | + Expr *pE2 = sqlite3ExprSkipCollate(pE); | |
| 75032 | + if( zType[0]!='G' ){ | |
| 75033 | + iCol = resolveAsName(pParse, pSelect->pEList, pE2); | |
| 75034 | + if( iCol>0 ){ | |
| 75035 | + /* If an AS-name match is found, mark this ORDER BY column as being | |
| 75036 | + ** a copy of the iCol-th result-set column. The subsequent call to | |
| 75037 | + ** sqlite3ResolveOrderGroupBy() will convert the expression to a | |
| 75038 | + ** copy of the iCol-th result-set expression. */ | |
| 75039 | + pItem->iOrderByCol = (u16)iCol; | |
| 75040 | + continue; | |
| 75041 | + } | |
| 75042 | + } | |
| 75043 | + if( sqlite3ExprIsInteger(pE2, &iCol) ){ | |
| 75036 | 75044 | /* The ORDER BY term is an integer constant. Again, set the column |
| 75037 | 75045 | ** number so that sqlite3ResolveOrderGroupBy() will convert the |
| 75038 | 75046 | ** order-by term to a copy of the result-set expression */ |
| 75039 | 75047 | if( iCol<1 || iCol>0xffff ){ |
| 75040 | 75048 | resolveOutOfRangeError(pParse, zType, i+1, nResult); |
| @@ -75173,23 +75181,21 @@ | ||
| 75173 | 75181 | if( p->pHaving && !pGroupBy ){ |
| 75174 | 75182 | sqlite3ErrorMsg(pParse, "a GROUP BY clause is required before HAVING"); |
| 75175 | 75183 | return WRC_Abort; |
| 75176 | 75184 | } |
| 75177 | 75185 | |
| 75178 | - /* Add the expression list to the name-context before parsing the | |
| 75186 | + /* Add the output column list to the name-context before parsing the | |
| 75179 | 75187 | ** other expressions in the SELECT statement. This is so that |
| 75180 | 75188 | ** expressions in the WHERE clause (etc.) can refer to expressions by |
| 75181 | 75189 | ** aliases in the result set. |
| 75182 | 75190 | ** |
| 75183 | 75191 | ** Minor point: If this is the case, then the expression will be |
| 75184 | 75192 | ** re-evaluated for each reference to it. |
| 75185 | 75193 | */ |
| 75186 | 75194 | sNC.pEList = p->pEList; |
| 75187 | - sNC.ncFlags |= NC_AsMaybe; | |
| 75188 | 75195 | if( sqlite3ResolveExprNames(&sNC, p->pHaving) ) return WRC_Abort; |
| 75189 | 75196 | if( sqlite3ResolveExprNames(&sNC, p->pWhere) ) return WRC_Abort; |
| 75190 | - sNC.ncFlags &= ~NC_AsMaybe; | |
| 75191 | 75197 | |
| 75192 | 75198 | /* The ORDER BY and GROUP BY clauses may not refer to terms in |
| 75193 | 75199 | ** outer queries |
| 75194 | 75200 | */ |
| 75195 | 75201 | sNC.pNext = 0; |
| @@ -77134,19 +77140,21 @@ | ||
| 77134 | 77140 | assert( !isRowid ); |
| 77135 | 77141 | sqlite3SelectDestInit(&dest, SRT_Set, pExpr->iTable); |
| 77136 | 77142 | dest.affSdst = (u8)affinity; |
| 77137 | 77143 | assert( (pExpr->iTable&0x0000FFFF)==pExpr->iTable ); |
| 77138 | 77144 | pExpr->x.pSelect->iLimit = 0; |
| 77145 | + testcase( pKeyInfo==0 ); /* Caused by OOM in sqlite3KeyInfoAlloc() */ | |
| 77139 | 77146 | if( sqlite3Select(pParse, pExpr->x.pSelect, &dest) ){ |
| 77140 | 77147 | sqlite3DbFree(pParse->db, pKeyInfo); |
| 77141 | 77148 | return 0; |
| 77142 | 77149 | } |
| 77143 | 77150 | pEList = pExpr->x.pSelect->pEList; |
| 77144 | - if( pKeyInfo && ALWAYS(pEList!=0 && pEList->nExpr>0) ){ | |
| 77145 | - pKeyInfo->aColl[0] = sqlite3BinaryCompareCollSeq(pParse, pExpr->pLeft, | |
| 77146 | - pEList->a[0].pExpr); | |
| 77147 | - } | |
| 77151 | + assert( pKeyInfo!=0 ); /* OOM will cause exit after sqlite3Select() */ | |
| 77152 | + assert( pEList!=0 ); | |
| 77153 | + assert( pEList->nExpr>0 ); | |
| 77154 | + pKeyInfo->aColl[0] = sqlite3BinaryCompareCollSeq(pParse, pExpr->pLeft, | |
| 77155 | + pEList->a[0].pExpr); | |
| 77148 | 77156 | }else if( ALWAYS(pExpr->x.pList!=0) ){ |
| 77149 | 77157 | /* Case 2: expr IN (exprlist) |
| 77150 | 77158 | ** |
| 77151 | 77159 | ** For each expression, build an index key from the evaluation and |
| 77152 | 77160 | ** store it in the temporary table. If <expr> is a column, then use |
| @@ -104711,11 +104719,11 @@ | ||
| 104711 | 104719 | # define WHERETRACE_ENABLED 1 |
| 104712 | 104720 | #else |
| 104713 | 104721 | # define WHERETRACE(K,X) |
| 104714 | 104722 | #endif |
| 104715 | 104723 | |
| 104716 | -/* Forward reference | |
| 104724 | +/* Forward references | |
| 104717 | 104725 | */ |
| 104718 | 104726 | typedef struct WhereClause WhereClause; |
| 104719 | 104727 | typedef struct WhereMaskSet WhereMaskSet; |
| 104720 | 104728 | typedef struct WhereOrInfo WhereOrInfo; |
| 104721 | 104729 | typedef struct WhereAndInfo WhereAndInfo; |
| @@ -104733,18 +104741,19 @@ | ||
| 104733 | 104741 | ** maximum cost for ordinary tables is 64*(2**63) which becomes 6900. |
| 104734 | 104742 | ** (Virtual tables can return a larger cost, but let's assume they do not.) |
| 104735 | 104743 | ** So all costs can be stored in a 16-bit unsigned integer without risk |
| 104736 | 104744 | ** of overflow. |
| 104737 | 104745 | ** |
| 104738 | -** Costs are estimates, so don't go to the computational trouble to compute | |
| 104739 | -** 10*log2(X) exactly. Instead, a close estimate is used. Any value of | |
| 104740 | -** X<=1 is stored as 0. X=2 is 10. X=3 is 16. X=1000 is 99. etc. | |
| 104746 | +** Costs are estimates, so no effort is made to compute 10*log2(X) exactly. | |
| 104747 | +** Instead, a close estimate is used. Any value of X<=1 is stored as 0. | |
| 104748 | +** X=2 is 10. X=3 is 16. X=1000 is 99. etc. | |
| 104741 | 104749 | ** |
| 104742 | 104750 | ** The tool/wherecosttest.c source file implements a command-line program |
| 104743 | -** that will convert between WhereCost to integers and do addition and | |
| 104744 | -** multiplication on WhereCost values. That command-line program is a | |
| 104745 | -** useful utility to have around when working with this module. | |
| 104751 | +** that will convert WhereCosts to integers, convert integers to WhereCosts | |
| 104752 | +** and do addition and multiplication on WhereCost values. The wherecosttest | |
| 104753 | +** command-line program is a useful utility to have around when working with | |
| 104754 | +** this module. | |
| 104746 | 104755 | */ |
| 104747 | 104756 | typedef unsigned short int WhereCost; |
| 104748 | 104757 | |
| 104749 | 104758 | /* |
| 104750 | 104759 | ** This object contains information needed to implement a single nested |
| @@ -104843,12 +104852,12 @@ | ||
| 104843 | 104852 | WhereCost rRun; /* Cost of running this subquery */ |
| 104844 | 104853 | WhereCost nOut; /* Number of outputs for this subquery */ |
| 104845 | 104854 | }; |
| 104846 | 104855 | |
| 104847 | 104856 | /* The WhereOrSet object holds a set of possible WhereOrCosts that |
| 104848 | -** correspond to the subquery(s) of OR-clause processing. At most | |
| 104849 | -** favorable N_OR_COST elements are retained. | |
| 104857 | +** correspond to the subquery(s) of OR-clause processing. Only the | |
| 104858 | +** best N_OR_COST elements are retained. | |
| 104850 | 104859 | */ |
| 104851 | 104860 | #define N_OR_COST 3 |
| 104852 | 104861 | struct WhereOrSet { |
| 104853 | 104862 | u16 n; /* Number of valid a[] entries */ |
| 104854 | 104863 | WhereOrCost a[N_OR_COST]; /* Set of best costs */ |
| @@ -104910,13 +104919,13 @@ | ||
| 104910 | 104919 | ** |
| 104911 | 104920 | ** A WhereTerm might also be two or more subterms connected by OR: |
| 104912 | 104921 | ** |
| 104913 | 104922 | ** (t1.X <op> <expr>) OR (t1.Y <op> <expr>) OR .... |
| 104914 | 104923 | ** |
| 104915 | -** In this second case, wtFlag as the TERM_ORINFO set and eOperator==WO_OR | |
| 104924 | +** In this second case, wtFlag has the TERM_ORINFO bit set and eOperator==WO_OR | |
| 104916 | 104925 | ** and the WhereTerm.u.pOrInfo field points to auxiliary information that |
| 104917 | -** is collected about the | |
| 104926 | +** is collected about the OR clause. | |
| 104918 | 104927 | ** |
| 104919 | 104928 | ** If a term in the WHERE clause does not match either of the two previous |
| 104920 | 104929 | ** categories, then eOperator==0. The WhereTerm.pExpr field is still set |
| 104921 | 104930 | ** to the original subexpression content and wtFlags is set up appropriately |
| 104922 | 104931 | ** but no other fields in the WhereTerm object are meaningful. |
| @@ -105424,11 +105433,11 @@ | ||
| 105424 | 105433 | assert( pMaskSet->n < ArraySize(pMaskSet->ix) ); |
| 105425 | 105434 | pMaskSet->ix[pMaskSet->n++] = iCursor; |
| 105426 | 105435 | } |
| 105427 | 105436 | |
| 105428 | 105437 | /* |
| 105429 | -** These routine walk (recursively) an expression tree and generates | |
| 105438 | +** These routines walk (recursively) an expression tree and generate | |
| 105430 | 105439 | ** a bitmask indicating which tables are used in that expression |
| 105431 | 105440 | ** tree. |
| 105432 | 105441 | */ |
| 105433 | 105442 | static Bitmask exprListTableUsage(WhereMaskSet*, ExprList*); |
| 105434 | 105443 | static Bitmask exprSelectTableUsage(WhereMaskSet*, Select*); |
| @@ -105941,14 +105950,14 @@ | ||
| 105941 | 105950 | ** u.pAndInfo set to a dynamically allocated WhereAndTerm object. |
| 105942 | 105951 | ** |
| 105943 | 105952 | ** From another point of view, "indexable" means that the subterm could |
| 105944 | 105953 | ** potentially be used with an index if an appropriate index exists. |
| 105945 | 105954 | ** This analysis does not consider whether or not the index exists; that |
| 105946 | -** is something the bestIndex() routine will determine. This analysis | |
| 105947 | -** only looks at whether subterms appropriate for indexing exist. | |
| 105955 | +** is decided elsewhere. This analysis only looks at whether subterms | |
| 105956 | +** appropriate for indexing exist. | |
| 105948 | 105957 | ** |
| 105949 | -** All examples A through E above all satisfy case 2. But if a term | |
| 105958 | +** All examples A through E above satisfy case 2. But if a term | |
| 105950 | 105959 | ** also statisfies case 1 (such as B) we know that the optimizer will |
| 105951 | 105960 | ** always prefer case 1, so in that case we pretend that case 2 is not |
| 105952 | 105961 | ** satisfied. |
| 105953 | 105962 | ** |
| 105954 | 105963 | ** It might be the case that multiple tables are indexable. For example, |
| @@ -106611,11 +106620,11 @@ | ||
| 106611 | 106620 | |
| 106612 | 106621 | return 0; |
| 106613 | 106622 | } |
| 106614 | 106623 | |
| 106615 | 106624 | /* |
| 106616 | -** The (an approximate) sum of two WhereCosts. This computation is | |
| 106625 | +** Find (an approximate) sum of two WhereCosts. This computation is | |
| 106617 | 106626 | ** not a simple "+" operator because WhereCost is stored as a logarithmic |
| 106618 | 106627 | ** value. |
| 106619 | 106628 | ** |
| 106620 | 106629 | */ |
| 106621 | 106630 | static WhereCost whereCostAdd(WhereCost a, WhereCost b){ |
| @@ -115614,10 +115623,13 @@ | ||
| 115614 | 115623 | ** without blocking. |
| 115615 | 115624 | */ |
| 115616 | 115625 | SQLITE_API int sqlite3_initialize(void){ |
| 115617 | 115626 | MUTEX_LOGIC( sqlite3_mutex *pMaster; ) /* The main static mutex */ |
| 115618 | 115627 | int rc; /* Result code */ |
| 115628 | +#ifdef SQLITE_EXTRA_INIT | |
| 115629 | + int bRunExtraInit = 0; /* Extra initialization needed */ | |
| 115630 | +#endif | |
| 115619 | 115631 | |
| 115620 | 115632 | #ifdef SQLITE_OMIT_WSD |
| 115621 | 115633 | rc = sqlite3_wsd_init(4096, 24); |
| 115622 | 115634 | if( rc!=SQLITE_OK ){ |
| 115623 | 115635 | return rc; |
| @@ -115711,10 +115723,13 @@ | ||
| 115711 | 115723 | } |
| 115712 | 115724 | if( rc==SQLITE_OK ){ |
| 115713 | 115725 | sqlite3PCacheBufferSetup( sqlite3GlobalConfig.pPage, |
| 115714 | 115726 | sqlite3GlobalConfig.szPage, sqlite3GlobalConfig.nPage); |
| 115715 | 115727 | sqlite3GlobalConfig.isInit = 1; |
| 115728 | +#ifdef SQLITE_EXTRA_INIT | |
| 115729 | + bRunExtraInit = 1; | |
| 115730 | +#endif | |
| 115716 | 115731 | } |
| 115717 | 115732 | sqlite3GlobalConfig.inProgress = 0; |
| 115718 | 115733 | } |
| 115719 | 115734 | sqlite3_mutex_leave(sqlite3GlobalConfig.pInitMutex); |
| 115720 | 115735 | |
| @@ -115751,11 +115766,11 @@ | ||
| 115751 | 115766 | |
| 115752 | 115767 | /* Do extra initialization steps requested by the SQLITE_EXTRA_INIT |
| 115753 | 115768 | ** compile-time option. |
| 115754 | 115769 | */ |
| 115755 | 115770 | #ifdef SQLITE_EXTRA_INIT |
| 115756 | - if( rc==SQLITE_OK && sqlite3GlobalConfig.isInit ){ | |
| 115771 | + if( bRunExtraInit ){ | |
| 115757 | 115772 | int SQLITE_EXTRA_INIT(const char*); |
| 115758 | 115773 | rc = SQLITE_EXTRA_INIT(0); |
| 115759 | 115774 | } |
| 115760 | 115775 | #endif |
| 115761 | 115776 | |
| @@ -115939,12 +115954,12 @@ | ||
| 115939 | 115954 | ** run. |
| 115940 | 115955 | */ |
| 115941 | 115956 | memset(&sqlite3GlobalConfig.m, 0, sizeof(sqlite3GlobalConfig.m)); |
| 115942 | 115957 | }else{ |
| 115943 | 115958 | /* The heap pointer is not NULL, then install one of the |
| 115944 | - ** mem5.c/mem3.c methods. If neither ENABLE_MEMSYS3 nor | |
| 115945 | - ** ENABLE_MEMSYS5 is defined, return an error. | |
| 115959 | + ** mem5.c/mem3.c methods. The enclosing #if guarantees at | |
| 115960 | + ** least one of these methods is currently enabled. | |
| 115946 | 115961 | */ |
| 115947 | 115962 | #ifdef SQLITE_ENABLE_MEMSYS3 |
| 115948 | 115963 | sqlite3GlobalConfig.m = *sqlite3MemGetMemsys3(); |
| 115949 | 115964 | #endif |
| 115950 | 115965 | #ifdef SQLITE_ENABLE_MEMSYS5 |
| @@ -115959,11 +115974,11 @@ | ||
| 115959 | 115974 | sqlite3GlobalConfig.szLookaside = va_arg(ap, int); |
| 115960 | 115975 | sqlite3GlobalConfig.nLookaside = va_arg(ap, int); |
| 115961 | 115976 | break; |
| 115962 | 115977 | } |
| 115963 | 115978 | |
| 115964 | - /* Record a pointer to the logger funcction and its first argument. | |
| 115979 | + /* Record a pointer to the logger function and its first argument. | |
| 115965 | 115980 | ** The default is NULL. Logging is disabled if the function pointer is |
| 115966 | 115981 | ** NULL. |
| 115967 | 115982 | */ |
| 115968 | 115983 | case SQLITE_CONFIG_LOG: { |
| 115969 | 115984 | /* MSVC is picky about pulling func ptrs from va lists. |
| @@ -117675,24 +117690,24 @@ | ||
| 117675 | 117690 | |
| 117676 | 117691 | for(iIn=0; iIn<nUri; iIn++) nByte += (zUri[iIn]=='&'); |
| 117677 | 117692 | zFile = sqlite3_malloc(nByte); |
| 117678 | 117693 | if( !zFile ) return SQLITE_NOMEM; |
| 117679 | 117694 | |
| 117695 | + iIn = 5; | |
| 117696 | +#ifndef SQLITE_ALLOW_URI_AUTHORITY | |
| 117680 | 117697 | /* Discard the scheme and authority segments of the URI. */ |
| 117681 | 117698 | if( zUri[5]=='/' && zUri[6]=='/' ){ |
| 117682 | 117699 | iIn = 7; |
| 117683 | 117700 | while( zUri[iIn] && zUri[iIn]!='/' ) iIn++; |
| 117684 | - | |
| 117685 | 117701 | if( iIn!=7 && (iIn!=16 || memcmp("localhost", &zUri[7], 9)) ){ |
| 117686 | 117702 | *pzErrMsg = sqlite3_mprintf("invalid uri authority: %.*s", |
| 117687 | 117703 | iIn-7, &zUri[7]); |
| 117688 | 117704 | rc = SQLITE_ERROR; |
| 117689 | 117705 | goto parse_uri_out; |
| 117690 | 117706 | } |
| 117691 | - }else{ | |
| 117692 | - iIn = 5; | |
| 117693 | 117707 | } |
| 117708 | +#endif | |
| 117694 | 117709 | |
| 117695 | 117710 | /* Copy the filename and any query parameters into the zFile buffer. |
| 117696 | 117711 | ** Decode %HH escape codes along the way. |
| 117697 | 117712 | ** |
| 117698 | 117713 | ** Within this loop, variable eState may be set to 0, 1 or 2, depending |
| 117699 | 117714 |
| --- src/sqlite3.c | |
| +++ src/sqlite3.c | |
| @@ -656,11 +656,11 @@ | |
| 656 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 657 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 658 | */ |
| 659 | #define SQLITE_VERSION "3.8.0" |
| 660 | #define SQLITE_VERSION_NUMBER 3008000 |
| 661 | #define SQLITE_SOURCE_ID "2013-08-06 07:45:08 924f7e4d7a8fa2fe9100836663f3733b6e1a9084" |
| 662 | |
| 663 | /* |
| 664 | ** CAPI3REF: Run-Time Library Version Numbers |
| 665 | ** KEYWORDS: sqlite3_version, sqlite3_sourceid |
| 666 | ** |
| @@ -7780,11 +7780,11 @@ | |
| 7780 | #endif |
| 7781 | |
| 7782 | #if 0 |
| 7783 | } /* End of the 'extern "C"' block */ |
| 7784 | #endif |
| 7785 | #endif |
| 7786 | |
| 7787 | /* |
| 7788 | ** 2010 August 30 |
| 7789 | ** |
| 7790 | ** The author disclaims copyright to this source code. In place of |
| @@ -11214,11 +11214,11 @@ | |
| 11214 | ** subqueries looking for a match. |
| 11215 | */ |
| 11216 | struct NameContext { |
| 11217 | Parse *pParse; /* The parser */ |
| 11218 | SrcList *pSrcList; /* One or more tables used to resolve names */ |
| 11219 | ExprList *pEList; /* Optional list of named expressions */ |
| 11220 | AggInfo *pAggInfo; /* Information about aggregates at this level */ |
| 11221 | NameContext *pNext; /* Next outer name context. NULL for outermost */ |
| 11222 | int nRef; /* Number of names resolved by this context */ |
| 11223 | int nErr; /* Number of errors encountered while resolving names */ |
| 11224 | u8 ncFlags; /* Zero or more NC_* flags defined below */ |
| @@ -11229,13 +11229,11 @@ | |
| 11229 | */ |
| 11230 | #define NC_AllowAgg 0x01 /* Aggregate functions are allowed here */ |
| 11231 | #define NC_HasAgg 0x02 /* One or more aggregate functions seen */ |
| 11232 | #define NC_IsCheck 0x04 /* True if resolving names in a CHECK constraint */ |
| 11233 | #define NC_InAggFunc 0x08 /* True if analyzing arguments to an agg func */ |
| 11234 | #define NC_AsMaybe 0x10 /* Resolve to AS terms of the result set only |
| 11235 | ** if no other resolution is available */ |
| 11236 | #define NC_PartIdx 0x20 /* True if resolving a partial index WHERE */ |
| 11237 | |
| 11238 | /* |
| 11239 | ** An instance of the following structure contains all information |
| 11240 | ** needed to generate code for a single SELECT statement. |
| 11241 | ** |
| @@ -17231,17 +17229,17 @@ | |
| 17231 | u8 *aCtrl; |
| 17232 | |
| 17233 | } mem5; |
| 17234 | |
| 17235 | /* |
| 17236 | ** Access the static variable through a macro for SQLITE_OMIT_WSD |
| 17237 | */ |
| 17238 | #define mem5 GLOBAL(struct Mem5Global, mem5) |
| 17239 | |
| 17240 | /* |
| 17241 | ** Assuming mem5.zPool is divided up into an array of Mem5Link |
| 17242 | ** structures, return a pointer to the idx-th such lik. |
| 17243 | */ |
| 17244 | #define MEM5LINK(idx) ((Mem5Link *)(&mem5.zPool[(idx)*mem5.szAtom])) |
| 17245 | |
| 17246 | /* |
| 17247 | ** Unlink the chunk at mem5.aPool[i] from list it is currently |
| @@ -17333,11 +17331,11 @@ | |
| 17333 | |
| 17334 | /* |
| 17335 | ** Return a block of memory of at least nBytes in size. |
| 17336 | ** Return NULL if unable. Return NULL if nBytes==0. |
| 17337 | ** |
| 17338 | ** The caller guarantees that nByte positive. |
| 17339 | ** |
| 17340 | ** The caller has obtained a mutex prior to invoking this |
| 17341 | ** routine so there is never any chance that two or more |
| 17342 | ** threads can be in this routine at the same time. |
| 17343 | */ |
| @@ -17455,11 +17453,11 @@ | |
| 17455 | } |
| 17456 | memsys5Link(iBlock, iLogsize); |
| 17457 | } |
| 17458 | |
| 17459 | /* |
| 17460 | ** Allocate nBytes of memory |
| 17461 | */ |
| 17462 | static void *memsys5Malloc(int nBytes){ |
| 17463 | sqlite3_int64 *p = 0; |
| 17464 | if( nBytes>0 ){ |
| 17465 | memsys5Enter(); |
| @@ -74041,22 +74039,23 @@ | |
| 74041 | ** |
| 74042 | ** The reason for suppressing the TK_AS term when the expression is a simple |
| 74043 | ** column reference is so that the column reference will be recognized as |
| 74044 | ** usable by indices within the WHERE clause processing logic. |
| 74045 | ** |
| 74046 | ** Hack: The TK_AS operator is inhibited if zType[0]=='G'. This means |
| 74047 | ** that in a GROUP BY clause, the expression is evaluated twice. Hence: |
| 74048 | ** |
| 74049 | ** SELECT random()%5 AS x, count(*) FROM tab GROUP BY x |
| 74050 | ** |
| 74051 | ** Is equivalent to: |
| 74052 | ** |
| 74053 | ** SELECT random()%5 AS x, count(*) FROM tab GROUP BY random()%5 |
| 74054 | ** |
| 74055 | ** The result of random()%5 in the GROUP BY clause is probably different |
| 74056 | ** from the result in the result-set. We might fix this someday. Or |
| 74057 | ** then again, we might not... |
| 74058 | ** |
| 74059 | ** If the reference is followed by a COLLATE operator, then make sure |
| 74060 | ** the COLLATE operator is preserved. For example: |
| 74061 | ** |
| 74062 | ** SELECT a+b, c+d FROM t1 ORDER BY 1 COLLATE nocase; |
| @@ -74382,14 +74381,20 @@ | |
| 74382 | ** |
| 74383 | ** In cases like this, replace pExpr with a copy of the expression that |
| 74384 | ** forms the result set entry ("a+b" in the example) and return immediately. |
| 74385 | ** Note that the expression in the result set should have already been |
| 74386 | ** resolved by the time the WHERE clause is resolved. |
| 74387 | */ |
| 74388 | if( (pEList = pNC->pEList)!=0 |
| 74389 | && zTab==0 |
| 74390 | && ((pNC->ncFlags & NC_AsMaybe)==0 || cnt==0) |
| 74391 | ){ |
| 74392 | for(j=0; j<pEList->nExpr; j++){ |
| 74393 | char *zAs = pEList->a[j].zName; |
| 74394 | if( zAs!=0 && sqlite3StrICmp(zAs, zCol)==0 ){ |
| 74395 | Expr *pOrig; |
| @@ -74947,11 +74952,11 @@ | |
| 74947 | } |
| 74948 | |
| 74949 | /* |
| 74950 | ** Check every term in the ORDER BY or GROUP BY clause pOrderBy of |
| 74951 | ** the SELECT statement pSelect. If any term is reference to a |
| 74952 | ** result set expression (as determined by the ExprList.a.iCol field) |
| 74953 | ** then convert that term into a copy of the corresponding result set |
| 74954 | ** column. |
| 74955 | ** |
| 74956 | ** If any errors are detected, add an error message to pParse and |
| 74957 | ** return non-zero. Return zero if no errors are seen. |
| @@ -74995,11 +75000,11 @@ | |
| 74995 | ** |
| 74996 | ** This routine resolves each term of the clause into an expression. |
| 74997 | ** If the order-by term is an integer I between 1 and N (where N is the |
| 74998 | ** number of columns in the result set of the SELECT) then the expression |
| 74999 | ** in the resolution is a copy of the I-th result-set expression. If |
| 75000 | ** the order-by term is an identify that corresponds to the AS-name of |
| 75001 | ** a result-set expression, then the term resolves to a copy of the |
| 75002 | ** result-set expression. Otherwise, the expression is resolved in |
| 75003 | ** the usual way - using sqlite3ResolveExprNames(). |
| 75004 | ** |
| 75005 | ** This routine returns the number of errors. If errors occur, then |
| @@ -75021,20 +75026,23 @@ | |
| 75021 | if( pOrderBy==0 ) return 0; |
| 75022 | nResult = pSelect->pEList->nExpr; |
| 75023 | pParse = pNC->pParse; |
| 75024 | for(i=0, pItem=pOrderBy->a; i<pOrderBy->nExpr; i++, pItem++){ |
| 75025 | Expr *pE = pItem->pExpr; |
| 75026 | iCol = resolveAsName(pParse, pSelect->pEList, pE); |
| 75027 | if( iCol>0 ){ |
| 75028 | /* If an AS-name match is found, mark this ORDER BY column as being |
| 75029 | ** a copy of the iCol-th result-set column. The subsequent call to |
| 75030 | ** sqlite3ResolveOrderGroupBy() will convert the expression to a |
| 75031 | ** copy of the iCol-th result-set expression. */ |
| 75032 | pItem->iOrderByCol = (u16)iCol; |
| 75033 | continue; |
| 75034 | } |
| 75035 | if( sqlite3ExprIsInteger(sqlite3ExprSkipCollate(pE), &iCol) ){ |
| 75036 | /* The ORDER BY term is an integer constant. Again, set the column |
| 75037 | ** number so that sqlite3ResolveOrderGroupBy() will convert the |
| 75038 | ** order-by term to a copy of the result-set expression */ |
| 75039 | if( iCol<1 || iCol>0xffff ){ |
| 75040 | resolveOutOfRangeError(pParse, zType, i+1, nResult); |
| @@ -75173,23 +75181,21 @@ | |
| 75173 | if( p->pHaving && !pGroupBy ){ |
| 75174 | sqlite3ErrorMsg(pParse, "a GROUP BY clause is required before HAVING"); |
| 75175 | return WRC_Abort; |
| 75176 | } |
| 75177 | |
| 75178 | /* Add the expression list to the name-context before parsing the |
| 75179 | ** other expressions in the SELECT statement. This is so that |
| 75180 | ** expressions in the WHERE clause (etc.) can refer to expressions by |
| 75181 | ** aliases in the result set. |
| 75182 | ** |
| 75183 | ** Minor point: If this is the case, then the expression will be |
| 75184 | ** re-evaluated for each reference to it. |
| 75185 | */ |
| 75186 | sNC.pEList = p->pEList; |
| 75187 | sNC.ncFlags |= NC_AsMaybe; |
| 75188 | if( sqlite3ResolveExprNames(&sNC, p->pHaving) ) return WRC_Abort; |
| 75189 | if( sqlite3ResolveExprNames(&sNC, p->pWhere) ) return WRC_Abort; |
| 75190 | sNC.ncFlags &= ~NC_AsMaybe; |
| 75191 | |
| 75192 | /* The ORDER BY and GROUP BY clauses may not refer to terms in |
| 75193 | ** outer queries |
| 75194 | */ |
| 75195 | sNC.pNext = 0; |
| @@ -77134,19 +77140,21 @@ | |
| 77134 | assert( !isRowid ); |
| 77135 | sqlite3SelectDestInit(&dest, SRT_Set, pExpr->iTable); |
| 77136 | dest.affSdst = (u8)affinity; |
| 77137 | assert( (pExpr->iTable&0x0000FFFF)==pExpr->iTable ); |
| 77138 | pExpr->x.pSelect->iLimit = 0; |
| 77139 | if( sqlite3Select(pParse, pExpr->x.pSelect, &dest) ){ |
| 77140 | sqlite3DbFree(pParse->db, pKeyInfo); |
| 77141 | return 0; |
| 77142 | } |
| 77143 | pEList = pExpr->x.pSelect->pEList; |
| 77144 | if( pKeyInfo && ALWAYS(pEList!=0 && pEList->nExpr>0) ){ |
| 77145 | pKeyInfo->aColl[0] = sqlite3BinaryCompareCollSeq(pParse, pExpr->pLeft, |
| 77146 | pEList->a[0].pExpr); |
| 77147 | } |
| 77148 | }else if( ALWAYS(pExpr->x.pList!=0) ){ |
| 77149 | /* Case 2: expr IN (exprlist) |
| 77150 | ** |
| 77151 | ** For each expression, build an index key from the evaluation and |
| 77152 | ** store it in the temporary table. If <expr> is a column, then use |
| @@ -104711,11 +104719,11 @@ | |
| 104711 | # define WHERETRACE_ENABLED 1 |
| 104712 | #else |
| 104713 | # define WHERETRACE(K,X) |
| 104714 | #endif |
| 104715 | |
| 104716 | /* Forward reference |
| 104717 | */ |
| 104718 | typedef struct WhereClause WhereClause; |
| 104719 | typedef struct WhereMaskSet WhereMaskSet; |
| 104720 | typedef struct WhereOrInfo WhereOrInfo; |
| 104721 | typedef struct WhereAndInfo WhereAndInfo; |
| @@ -104733,18 +104741,19 @@ | |
| 104733 | ** maximum cost for ordinary tables is 64*(2**63) which becomes 6900. |
| 104734 | ** (Virtual tables can return a larger cost, but let's assume they do not.) |
| 104735 | ** So all costs can be stored in a 16-bit unsigned integer without risk |
| 104736 | ** of overflow. |
| 104737 | ** |
| 104738 | ** Costs are estimates, so don't go to the computational trouble to compute |
| 104739 | ** 10*log2(X) exactly. Instead, a close estimate is used. Any value of |
| 104740 | ** X<=1 is stored as 0. X=2 is 10. X=3 is 16. X=1000 is 99. etc. |
| 104741 | ** |
| 104742 | ** The tool/wherecosttest.c source file implements a command-line program |
| 104743 | ** that will convert between WhereCost to integers and do addition and |
| 104744 | ** multiplication on WhereCost values. That command-line program is a |
| 104745 | ** useful utility to have around when working with this module. |
| 104746 | */ |
| 104747 | typedef unsigned short int WhereCost; |
| 104748 | |
| 104749 | /* |
| 104750 | ** This object contains information needed to implement a single nested |
| @@ -104843,12 +104852,12 @@ | |
| 104843 | WhereCost rRun; /* Cost of running this subquery */ |
| 104844 | WhereCost nOut; /* Number of outputs for this subquery */ |
| 104845 | }; |
| 104846 | |
| 104847 | /* The WhereOrSet object holds a set of possible WhereOrCosts that |
| 104848 | ** correspond to the subquery(s) of OR-clause processing. At most |
| 104849 | ** favorable N_OR_COST elements are retained. |
| 104850 | */ |
| 104851 | #define N_OR_COST 3 |
| 104852 | struct WhereOrSet { |
| 104853 | u16 n; /* Number of valid a[] entries */ |
| 104854 | WhereOrCost a[N_OR_COST]; /* Set of best costs */ |
| @@ -104910,13 +104919,13 @@ | |
| 104910 | ** |
| 104911 | ** A WhereTerm might also be two or more subterms connected by OR: |
| 104912 | ** |
| 104913 | ** (t1.X <op> <expr>) OR (t1.Y <op> <expr>) OR .... |
| 104914 | ** |
| 104915 | ** In this second case, wtFlag as the TERM_ORINFO set and eOperator==WO_OR |
| 104916 | ** and the WhereTerm.u.pOrInfo field points to auxiliary information that |
| 104917 | ** is collected about the |
| 104918 | ** |
| 104919 | ** If a term in the WHERE clause does not match either of the two previous |
| 104920 | ** categories, then eOperator==0. The WhereTerm.pExpr field is still set |
| 104921 | ** to the original subexpression content and wtFlags is set up appropriately |
| 104922 | ** but no other fields in the WhereTerm object are meaningful. |
| @@ -105424,11 +105433,11 @@ | |
| 105424 | assert( pMaskSet->n < ArraySize(pMaskSet->ix) ); |
| 105425 | pMaskSet->ix[pMaskSet->n++] = iCursor; |
| 105426 | } |
| 105427 | |
| 105428 | /* |
| 105429 | ** These routine walk (recursively) an expression tree and generates |
| 105430 | ** a bitmask indicating which tables are used in that expression |
| 105431 | ** tree. |
| 105432 | */ |
| 105433 | static Bitmask exprListTableUsage(WhereMaskSet*, ExprList*); |
| 105434 | static Bitmask exprSelectTableUsage(WhereMaskSet*, Select*); |
| @@ -105941,14 +105950,14 @@ | |
| 105941 | ** u.pAndInfo set to a dynamically allocated WhereAndTerm object. |
| 105942 | ** |
| 105943 | ** From another point of view, "indexable" means that the subterm could |
| 105944 | ** potentially be used with an index if an appropriate index exists. |
| 105945 | ** This analysis does not consider whether or not the index exists; that |
| 105946 | ** is something the bestIndex() routine will determine. This analysis |
| 105947 | ** only looks at whether subterms appropriate for indexing exist. |
| 105948 | ** |
| 105949 | ** All examples A through E above all satisfy case 2. But if a term |
| 105950 | ** also statisfies case 1 (such as B) we know that the optimizer will |
| 105951 | ** always prefer case 1, so in that case we pretend that case 2 is not |
| 105952 | ** satisfied. |
| 105953 | ** |
| 105954 | ** It might be the case that multiple tables are indexable. For example, |
| @@ -106611,11 +106620,11 @@ | |
| 106611 | |
| 106612 | return 0; |
| 106613 | } |
| 106614 | |
| 106615 | /* |
| 106616 | ** The (an approximate) sum of two WhereCosts. This computation is |
| 106617 | ** not a simple "+" operator because WhereCost is stored as a logarithmic |
| 106618 | ** value. |
| 106619 | ** |
| 106620 | */ |
| 106621 | static WhereCost whereCostAdd(WhereCost a, WhereCost b){ |
| @@ -115614,10 +115623,13 @@ | |
| 115614 | ** without blocking. |
| 115615 | */ |
| 115616 | SQLITE_API int sqlite3_initialize(void){ |
| 115617 | MUTEX_LOGIC( sqlite3_mutex *pMaster; ) /* The main static mutex */ |
| 115618 | int rc; /* Result code */ |
| 115619 | |
| 115620 | #ifdef SQLITE_OMIT_WSD |
| 115621 | rc = sqlite3_wsd_init(4096, 24); |
| 115622 | if( rc!=SQLITE_OK ){ |
| 115623 | return rc; |
| @@ -115711,10 +115723,13 @@ | |
| 115711 | } |
| 115712 | if( rc==SQLITE_OK ){ |
| 115713 | sqlite3PCacheBufferSetup( sqlite3GlobalConfig.pPage, |
| 115714 | sqlite3GlobalConfig.szPage, sqlite3GlobalConfig.nPage); |
| 115715 | sqlite3GlobalConfig.isInit = 1; |
| 115716 | } |
| 115717 | sqlite3GlobalConfig.inProgress = 0; |
| 115718 | } |
| 115719 | sqlite3_mutex_leave(sqlite3GlobalConfig.pInitMutex); |
| 115720 | |
| @@ -115751,11 +115766,11 @@ | |
| 115751 | |
| 115752 | /* Do extra initialization steps requested by the SQLITE_EXTRA_INIT |
| 115753 | ** compile-time option. |
| 115754 | */ |
| 115755 | #ifdef SQLITE_EXTRA_INIT |
| 115756 | if( rc==SQLITE_OK && sqlite3GlobalConfig.isInit ){ |
| 115757 | int SQLITE_EXTRA_INIT(const char*); |
| 115758 | rc = SQLITE_EXTRA_INIT(0); |
| 115759 | } |
| 115760 | #endif |
| 115761 | |
| @@ -115939,12 +115954,12 @@ | |
| 115939 | ** run. |
| 115940 | */ |
| 115941 | memset(&sqlite3GlobalConfig.m, 0, sizeof(sqlite3GlobalConfig.m)); |
| 115942 | }else{ |
| 115943 | /* The heap pointer is not NULL, then install one of the |
| 115944 | ** mem5.c/mem3.c methods. If neither ENABLE_MEMSYS3 nor |
| 115945 | ** ENABLE_MEMSYS5 is defined, return an error. |
| 115946 | */ |
| 115947 | #ifdef SQLITE_ENABLE_MEMSYS3 |
| 115948 | sqlite3GlobalConfig.m = *sqlite3MemGetMemsys3(); |
| 115949 | #endif |
| 115950 | #ifdef SQLITE_ENABLE_MEMSYS5 |
| @@ -115959,11 +115974,11 @@ | |
| 115959 | sqlite3GlobalConfig.szLookaside = va_arg(ap, int); |
| 115960 | sqlite3GlobalConfig.nLookaside = va_arg(ap, int); |
| 115961 | break; |
| 115962 | } |
| 115963 | |
| 115964 | /* Record a pointer to the logger funcction and its first argument. |
| 115965 | ** The default is NULL. Logging is disabled if the function pointer is |
| 115966 | ** NULL. |
| 115967 | */ |
| 115968 | case SQLITE_CONFIG_LOG: { |
| 115969 | /* MSVC is picky about pulling func ptrs from va lists. |
| @@ -117675,24 +117690,24 @@ | |
| 117675 | |
| 117676 | for(iIn=0; iIn<nUri; iIn++) nByte += (zUri[iIn]=='&'); |
| 117677 | zFile = sqlite3_malloc(nByte); |
| 117678 | if( !zFile ) return SQLITE_NOMEM; |
| 117679 | |
| 117680 | /* Discard the scheme and authority segments of the URI. */ |
| 117681 | if( zUri[5]=='/' && zUri[6]=='/' ){ |
| 117682 | iIn = 7; |
| 117683 | while( zUri[iIn] && zUri[iIn]!='/' ) iIn++; |
| 117684 | |
| 117685 | if( iIn!=7 && (iIn!=16 || memcmp("localhost", &zUri[7], 9)) ){ |
| 117686 | *pzErrMsg = sqlite3_mprintf("invalid uri authority: %.*s", |
| 117687 | iIn-7, &zUri[7]); |
| 117688 | rc = SQLITE_ERROR; |
| 117689 | goto parse_uri_out; |
| 117690 | } |
| 117691 | }else{ |
| 117692 | iIn = 5; |
| 117693 | } |
| 117694 | |
| 117695 | /* Copy the filename and any query parameters into the zFile buffer. |
| 117696 | ** Decode %HH escape codes along the way. |
| 117697 | ** |
| 117698 | ** Within this loop, variable eState may be set to 0, 1 or 2, depending |
| 117699 |
| --- src/sqlite3.c | |
| +++ src/sqlite3.c | |
| @@ -656,11 +656,11 @@ | |
| 656 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 657 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 658 | */ |
| 659 | #define SQLITE_VERSION "3.8.0" |
| 660 | #define SQLITE_VERSION_NUMBER 3008000 |
| 661 | #define SQLITE_SOURCE_ID "2013-08-15 22:40:21 f2d175f975cd0be63425424ec322a98fb650019e" |
| 662 | |
| 663 | /* |
| 664 | ** CAPI3REF: Run-Time Library Version Numbers |
| 665 | ** KEYWORDS: sqlite3_version, sqlite3_sourceid |
| 666 | ** |
| @@ -7780,11 +7780,11 @@ | |
| 7780 | #endif |
| 7781 | |
| 7782 | #if 0 |
| 7783 | } /* End of the 'extern "C"' block */ |
| 7784 | #endif |
| 7785 | #endif /* _SQLITE3_H_ */ |
| 7786 | |
| 7787 | /* |
| 7788 | ** 2010 August 30 |
| 7789 | ** |
| 7790 | ** The author disclaims copyright to this source code. In place of |
| @@ -11214,11 +11214,11 @@ | |
| 11214 | ** subqueries looking for a match. |
| 11215 | */ |
| 11216 | struct NameContext { |
| 11217 | Parse *pParse; /* The parser */ |
| 11218 | SrcList *pSrcList; /* One or more tables used to resolve names */ |
| 11219 | ExprList *pEList; /* Optional list of result-set columns */ |
| 11220 | AggInfo *pAggInfo; /* Information about aggregates at this level */ |
| 11221 | NameContext *pNext; /* Next outer name context. NULL for outermost */ |
| 11222 | int nRef; /* Number of names resolved by this context */ |
| 11223 | int nErr; /* Number of errors encountered while resolving names */ |
| 11224 | u8 ncFlags; /* Zero or more NC_* flags defined below */ |
| @@ -11229,13 +11229,11 @@ | |
| 11229 | */ |
| 11230 | #define NC_AllowAgg 0x01 /* Aggregate functions are allowed here */ |
| 11231 | #define NC_HasAgg 0x02 /* One or more aggregate functions seen */ |
| 11232 | #define NC_IsCheck 0x04 /* True if resolving names in a CHECK constraint */ |
| 11233 | #define NC_InAggFunc 0x08 /* True if analyzing arguments to an agg func */ |
| 11234 | #define NC_PartIdx 0x10 /* True if resolving a partial index WHERE */ |
| 11235 | |
| 11236 | /* |
| 11237 | ** An instance of the following structure contains all information |
| 11238 | ** needed to generate code for a single SELECT statement. |
| 11239 | ** |
| @@ -17231,17 +17229,17 @@ | |
| 17229 | u8 *aCtrl; |
| 17230 | |
| 17231 | } mem5; |
| 17232 | |
| 17233 | /* |
| 17234 | ** Access the static variable through a macro for SQLITE_OMIT_WSD. |
| 17235 | */ |
| 17236 | #define mem5 GLOBAL(struct Mem5Global, mem5) |
| 17237 | |
| 17238 | /* |
| 17239 | ** Assuming mem5.zPool is divided up into an array of Mem5Link |
| 17240 | ** structures, return a pointer to the idx-th such link. |
| 17241 | */ |
| 17242 | #define MEM5LINK(idx) ((Mem5Link *)(&mem5.zPool[(idx)*mem5.szAtom])) |
| 17243 | |
| 17244 | /* |
| 17245 | ** Unlink the chunk at mem5.aPool[i] from list it is currently |
| @@ -17333,11 +17331,11 @@ | |
| 17331 | |
| 17332 | /* |
| 17333 | ** Return a block of memory of at least nBytes in size. |
| 17334 | ** Return NULL if unable. Return NULL if nBytes==0. |
| 17335 | ** |
| 17336 | ** The caller guarantees that nByte is positive. |
| 17337 | ** |
| 17338 | ** The caller has obtained a mutex prior to invoking this |
| 17339 | ** routine so there is never any chance that two or more |
| 17340 | ** threads can be in this routine at the same time. |
| 17341 | */ |
| @@ -17455,11 +17453,11 @@ | |
| 17453 | } |
| 17454 | memsys5Link(iBlock, iLogsize); |
| 17455 | } |
| 17456 | |
| 17457 | /* |
| 17458 | ** Allocate nBytes of memory. |
| 17459 | */ |
| 17460 | static void *memsys5Malloc(int nBytes){ |
| 17461 | sqlite3_int64 *p = 0; |
| 17462 | if( nBytes>0 ){ |
| 17463 | memsys5Enter(); |
| @@ -74041,22 +74039,23 @@ | |
| 74039 | ** |
| 74040 | ** The reason for suppressing the TK_AS term when the expression is a simple |
| 74041 | ** column reference is so that the column reference will be recognized as |
| 74042 | ** usable by indices within the WHERE clause processing logic. |
| 74043 | ** |
| 74044 | ** The TK_AS operator is inhibited if zType[0]=='G'. This means |
| 74045 | ** that in a GROUP BY clause, the expression is evaluated twice. Hence: |
| 74046 | ** |
| 74047 | ** SELECT random()%5 AS x, count(*) FROM tab GROUP BY x |
| 74048 | ** |
| 74049 | ** Is equivalent to: |
| 74050 | ** |
| 74051 | ** SELECT random()%5 AS x, count(*) FROM tab GROUP BY random()%5 |
| 74052 | ** |
| 74053 | ** The result of random()%5 in the GROUP BY clause is probably different |
| 74054 | ** from the result in the result-set. On the other hand Standard SQL does |
| 74055 | ** not allow the GROUP BY clause to contain references to result-set columns. |
| 74056 | ** So this should never come up in well-formed queries. |
| 74057 | ** |
| 74058 | ** If the reference is followed by a COLLATE operator, then make sure |
| 74059 | ** the COLLATE operator is preserved. For example: |
| 74060 | ** |
| 74061 | ** SELECT a+b, c+d FROM t1 ORDER BY 1 COLLATE nocase; |
| @@ -74382,14 +74381,20 @@ | |
| 74381 | ** |
| 74382 | ** In cases like this, replace pExpr with a copy of the expression that |
| 74383 | ** forms the result set entry ("a+b" in the example) and return immediately. |
| 74384 | ** Note that the expression in the result set should have already been |
| 74385 | ** resolved by the time the WHERE clause is resolved. |
| 74386 | ** |
| 74387 | ** The ability to use an output result-set column in the WHERE, GROUP BY, |
| 74388 | ** or HAVING clauses, or as part of a larger expression in the ORDRE BY |
| 74389 | ** clause is not standard SQL. This is a (goofy) SQLite extension, that |
| 74390 | ** is supported for backwards compatibility only. TO DO: Issue a warning |
| 74391 | ** on sqlite3_log() whenever the capability is used. |
| 74392 | */ |
| 74393 | if( (pEList = pNC->pEList)!=0 |
| 74394 | && zTab==0 |
| 74395 | && cnt==0 |
| 74396 | ){ |
| 74397 | for(j=0; j<pEList->nExpr; j++){ |
| 74398 | char *zAs = pEList->a[j].zName; |
| 74399 | if( zAs!=0 && sqlite3StrICmp(zAs, zCol)==0 ){ |
| 74400 | Expr *pOrig; |
| @@ -74947,11 +74952,11 @@ | |
| 74952 | } |
| 74953 | |
| 74954 | /* |
| 74955 | ** Check every term in the ORDER BY or GROUP BY clause pOrderBy of |
| 74956 | ** the SELECT statement pSelect. If any term is reference to a |
| 74957 | ** result set expression (as determined by the ExprList.a.iOrderByCol field) |
| 74958 | ** then convert that term into a copy of the corresponding result set |
| 74959 | ** column. |
| 74960 | ** |
| 74961 | ** If any errors are detected, add an error message to pParse and |
| 74962 | ** return non-zero. Return zero if no errors are seen. |
| @@ -74995,11 +75000,11 @@ | |
| 75000 | ** |
| 75001 | ** This routine resolves each term of the clause into an expression. |
| 75002 | ** If the order-by term is an integer I between 1 and N (where N is the |
| 75003 | ** number of columns in the result set of the SELECT) then the expression |
| 75004 | ** in the resolution is a copy of the I-th result-set expression. If |
| 75005 | ** the order-by term is an identifier that corresponds to the AS-name of |
| 75006 | ** a result-set expression, then the term resolves to a copy of the |
| 75007 | ** result-set expression. Otherwise, the expression is resolved in |
| 75008 | ** the usual way - using sqlite3ResolveExprNames(). |
| 75009 | ** |
| 75010 | ** This routine returns the number of errors. If errors occur, then |
| @@ -75021,20 +75026,23 @@ | |
| 75026 | if( pOrderBy==0 ) return 0; |
| 75027 | nResult = pSelect->pEList->nExpr; |
| 75028 | pParse = pNC->pParse; |
| 75029 | for(i=0, pItem=pOrderBy->a; i<pOrderBy->nExpr; i++, pItem++){ |
| 75030 | Expr *pE = pItem->pExpr; |
| 75031 | Expr *pE2 = sqlite3ExprSkipCollate(pE); |
| 75032 | if( zType[0]!='G' ){ |
| 75033 | iCol = resolveAsName(pParse, pSelect->pEList, pE2); |
| 75034 | if( iCol>0 ){ |
| 75035 | /* If an AS-name match is found, mark this ORDER BY column as being |
| 75036 | ** a copy of the iCol-th result-set column. The subsequent call to |
| 75037 | ** sqlite3ResolveOrderGroupBy() will convert the expression to a |
| 75038 | ** copy of the iCol-th result-set expression. */ |
| 75039 | pItem->iOrderByCol = (u16)iCol; |
| 75040 | continue; |
| 75041 | } |
| 75042 | } |
| 75043 | if( sqlite3ExprIsInteger(pE2, &iCol) ){ |
| 75044 | /* The ORDER BY term is an integer constant. Again, set the column |
| 75045 | ** number so that sqlite3ResolveOrderGroupBy() will convert the |
| 75046 | ** order-by term to a copy of the result-set expression */ |
| 75047 | if( iCol<1 || iCol>0xffff ){ |
| 75048 | resolveOutOfRangeError(pParse, zType, i+1, nResult); |
| @@ -75173,23 +75181,21 @@ | |
| 75181 | if( p->pHaving && !pGroupBy ){ |
| 75182 | sqlite3ErrorMsg(pParse, "a GROUP BY clause is required before HAVING"); |
| 75183 | return WRC_Abort; |
| 75184 | } |
| 75185 | |
| 75186 | /* Add the output column list to the name-context before parsing the |
| 75187 | ** other expressions in the SELECT statement. This is so that |
| 75188 | ** expressions in the WHERE clause (etc.) can refer to expressions by |
| 75189 | ** aliases in the result set. |
| 75190 | ** |
| 75191 | ** Minor point: If this is the case, then the expression will be |
| 75192 | ** re-evaluated for each reference to it. |
| 75193 | */ |
| 75194 | sNC.pEList = p->pEList; |
| 75195 | if( sqlite3ResolveExprNames(&sNC, p->pHaving) ) return WRC_Abort; |
| 75196 | if( sqlite3ResolveExprNames(&sNC, p->pWhere) ) return WRC_Abort; |
| 75197 | |
| 75198 | /* The ORDER BY and GROUP BY clauses may not refer to terms in |
| 75199 | ** outer queries |
| 75200 | */ |
| 75201 | sNC.pNext = 0; |
| @@ -77134,19 +77140,21 @@ | |
| 77140 | assert( !isRowid ); |
| 77141 | sqlite3SelectDestInit(&dest, SRT_Set, pExpr->iTable); |
| 77142 | dest.affSdst = (u8)affinity; |
| 77143 | assert( (pExpr->iTable&0x0000FFFF)==pExpr->iTable ); |
| 77144 | pExpr->x.pSelect->iLimit = 0; |
| 77145 | testcase( pKeyInfo==0 ); /* Caused by OOM in sqlite3KeyInfoAlloc() */ |
| 77146 | if( sqlite3Select(pParse, pExpr->x.pSelect, &dest) ){ |
| 77147 | sqlite3DbFree(pParse->db, pKeyInfo); |
| 77148 | return 0; |
| 77149 | } |
| 77150 | pEList = pExpr->x.pSelect->pEList; |
| 77151 | assert( pKeyInfo!=0 ); /* OOM will cause exit after sqlite3Select() */ |
| 77152 | assert( pEList!=0 ); |
| 77153 | assert( pEList->nExpr>0 ); |
| 77154 | pKeyInfo->aColl[0] = sqlite3BinaryCompareCollSeq(pParse, pExpr->pLeft, |
| 77155 | pEList->a[0].pExpr); |
| 77156 | }else if( ALWAYS(pExpr->x.pList!=0) ){ |
| 77157 | /* Case 2: expr IN (exprlist) |
| 77158 | ** |
| 77159 | ** For each expression, build an index key from the evaluation and |
| 77160 | ** store it in the temporary table. If <expr> is a column, then use |
| @@ -104711,11 +104719,11 @@ | |
| 104719 | # define WHERETRACE_ENABLED 1 |
| 104720 | #else |
| 104721 | # define WHERETRACE(K,X) |
| 104722 | #endif |
| 104723 | |
| 104724 | /* Forward references |
| 104725 | */ |
| 104726 | typedef struct WhereClause WhereClause; |
| 104727 | typedef struct WhereMaskSet WhereMaskSet; |
| 104728 | typedef struct WhereOrInfo WhereOrInfo; |
| 104729 | typedef struct WhereAndInfo WhereAndInfo; |
| @@ -104733,18 +104741,19 @@ | |
| 104741 | ** maximum cost for ordinary tables is 64*(2**63) which becomes 6900. |
| 104742 | ** (Virtual tables can return a larger cost, but let's assume they do not.) |
| 104743 | ** So all costs can be stored in a 16-bit unsigned integer without risk |
| 104744 | ** of overflow. |
| 104745 | ** |
| 104746 | ** Costs are estimates, so no effort is made to compute 10*log2(X) exactly. |
| 104747 | ** Instead, a close estimate is used. Any value of X<=1 is stored as 0. |
| 104748 | ** X=2 is 10. X=3 is 16. X=1000 is 99. etc. |
| 104749 | ** |
| 104750 | ** The tool/wherecosttest.c source file implements a command-line program |
| 104751 | ** that will convert WhereCosts to integers, convert integers to WhereCosts |
| 104752 | ** and do addition and multiplication on WhereCost values. The wherecosttest |
| 104753 | ** command-line program is a useful utility to have around when working with |
| 104754 | ** this module. |
| 104755 | */ |
| 104756 | typedef unsigned short int WhereCost; |
| 104757 | |
| 104758 | /* |
| 104759 | ** This object contains information needed to implement a single nested |
| @@ -104843,12 +104852,12 @@ | |
| 104852 | WhereCost rRun; /* Cost of running this subquery */ |
| 104853 | WhereCost nOut; /* Number of outputs for this subquery */ |
| 104854 | }; |
| 104855 | |
| 104856 | /* The WhereOrSet object holds a set of possible WhereOrCosts that |
| 104857 | ** correspond to the subquery(s) of OR-clause processing. Only the |
| 104858 | ** best N_OR_COST elements are retained. |
| 104859 | */ |
| 104860 | #define N_OR_COST 3 |
| 104861 | struct WhereOrSet { |
| 104862 | u16 n; /* Number of valid a[] entries */ |
| 104863 | WhereOrCost a[N_OR_COST]; /* Set of best costs */ |
| @@ -104910,13 +104919,13 @@ | |
| 104919 | ** |
| 104920 | ** A WhereTerm might also be two or more subterms connected by OR: |
| 104921 | ** |
| 104922 | ** (t1.X <op> <expr>) OR (t1.Y <op> <expr>) OR .... |
| 104923 | ** |
| 104924 | ** In this second case, wtFlag has the TERM_ORINFO bit set and eOperator==WO_OR |
| 104925 | ** and the WhereTerm.u.pOrInfo field points to auxiliary information that |
| 104926 | ** is collected about the OR clause. |
| 104927 | ** |
| 104928 | ** If a term in the WHERE clause does not match either of the two previous |
| 104929 | ** categories, then eOperator==0. The WhereTerm.pExpr field is still set |
| 104930 | ** to the original subexpression content and wtFlags is set up appropriately |
| 104931 | ** but no other fields in the WhereTerm object are meaningful. |
| @@ -105424,11 +105433,11 @@ | |
| 105433 | assert( pMaskSet->n < ArraySize(pMaskSet->ix) ); |
| 105434 | pMaskSet->ix[pMaskSet->n++] = iCursor; |
| 105435 | } |
| 105436 | |
| 105437 | /* |
| 105438 | ** These routines walk (recursively) an expression tree and generate |
| 105439 | ** a bitmask indicating which tables are used in that expression |
| 105440 | ** tree. |
| 105441 | */ |
| 105442 | static Bitmask exprListTableUsage(WhereMaskSet*, ExprList*); |
| 105443 | static Bitmask exprSelectTableUsage(WhereMaskSet*, Select*); |
| @@ -105941,14 +105950,14 @@ | |
| 105950 | ** u.pAndInfo set to a dynamically allocated WhereAndTerm object. |
| 105951 | ** |
| 105952 | ** From another point of view, "indexable" means that the subterm could |
| 105953 | ** potentially be used with an index if an appropriate index exists. |
| 105954 | ** This analysis does not consider whether or not the index exists; that |
| 105955 | ** is decided elsewhere. This analysis only looks at whether subterms |
| 105956 | ** appropriate for indexing exist. |
| 105957 | ** |
| 105958 | ** All examples A through E above satisfy case 2. But if a term |
| 105959 | ** also statisfies case 1 (such as B) we know that the optimizer will |
| 105960 | ** always prefer case 1, so in that case we pretend that case 2 is not |
| 105961 | ** satisfied. |
| 105962 | ** |
| 105963 | ** It might be the case that multiple tables are indexable. For example, |
| @@ -106611,11 +106620,11 @@ | |
| 106620 | |
| 106621 | return 0; |
| 106622 | } |
| 106623 | |
| 106624 | /* |
| 106625 | ** Find (an approximate) sum of two WhereCosts. This computation is |
| 106626 | ** not a simple "+" operator because WhereCost is stored as a logarithmic |
| 106627 | ** value. |
| 106628 | ** |
| 106629 | */ |
| 106630 | static WhereCost whereCostAdd(WhereCost a, WhereCost b){ |
| @@ -115614,10 +115623,13 @@ | |
| 115623 | ** without blocking. |
| 115624 | */ |
| 115625 | SQLITE_API int sqlite3_initialize(void){ |
| 115626 | MUTEX_LOGIC( sqlite3_mutex *pMaster; ) /* The main static mutex */ |
| 115627 | int rc; /* Result code */ |
| 115628 | #ifdef SQLITE_EXTRA_INIT |
| 115629 | int bRunExtraInit = 0; /* Extra initialization needed */ |
| 115630 | #endif |
| 115631 | |
| 115632 | #ifdef SQLITE_OMIT_WSD |
| 115633 | rc = sqlite3_wsd_init(4096, 24); |
| 115634 | if( rc!=SQLITE_OK ){ |
| 115635 | return rc; |
| @@ -115711,10 +115723,13 @@ | |
| 115723 | } |
| 115724 | if( rc==SQLITE_OK ){ |
| 115725 | sqlite3PCacheBufferSetup( sqlite3GlobalConfig.pPage, |
| 115726 | sqlite3GlobalConfig.szPage, sqlite3GlobalConfig.nPage); |
| 115727 | sqlite3GlobalConfig.isInit = 1; |
| 115728 | #ifdef SQLITE_EXTRA_INIT |
| 115729 | bRunExtraInit = 1; |
| 115730 | #endif |
| 115731 | } |
| 115732 | sqlite3GlobalConfig.inProgress = 0; |
| 115733 | } |
| 115734 | sqlite3_mutex_leave(sqlite3GlobalConfig.pInitMutex); |
| 115735 | |
| @@ -115751,11 +115766,11 @@ | |
| 115766 | |
| 115767 | /* Do extra initialization steps requested by the SQLITE_EXTRA_INIT |
| 115768 | ** compile-time option. |
| 115769 | */ |
| 115770 | #ifdef SQLITE_EXTRA_INIT |
| 115771 | if( bRunExtraInit ){ |
| 115772 | int SQLITE_EXTRA_INIT(const char*); |
| 115773 | rc = SQLITE_EXTRA_INIT(0); |
| 115774 | } |
| 115775 | #endif |
| 115776 | |
| @@ -115939,12 +115954,12 @@ | |
| 115954 | ** run. |
| 115955 | */ |
| 115956 | memset(&sqlite3GlobalConfig.m, 0, sizeof(sqlite3GlobalConfig.m)); |
| 115957 | }else{ |
| 115958 | /* The heap pointer is not NULL, then install one of the |
| 115959 | ** mem5.c/mem3.c methods. The enclosing #if guarantees at |
| 115960 | ** least one of these methods is currently enabled. |
| 115961 | */ |
| 115962 | #ifdef SQLITE_ENABLE_MEMSYS3 |
| 115963 | sqlite3GlobalConfig.m = *sqlite3MemGetMemsys3(); |
| 115964 | #endif |
| 115965 | #ifdef SQLITE_ENABLE_MEMSYS5 |
| @@ -115959,11 +115974,11 @@ | |
| 115974 | sqlite3GlobalConfig.szLookaside = va_arg(ap, int); |
| 115975 | sqlite3GlobalConfig.nLookaside = va_arg(ap, int); |
| 115976 | break; |
| 115977 | } |
| 115978 | |
| 115979 | /* Record a pointer to the logger function and its first argument. |
| 115980 | ** The default is NULL. Logging is disabled if the function pointer is |
| 115981 | ** NULL. |
| 115982 | */ |
| 115983 | case SQLITE_CONFIG_LOG: { |
| 115984 | /* MSVC is picky about pulling func ptrs from va lists. |
| @@ -117675,24 +117690,24 @@ | |
| 117690 | |
| 117691 | for(iIn=0; iIn<nUri; iIn++) nByte += (zUri[iIn]=='&'); |
| 117692 | zFile = sqlite3_malloc(nByte); |
| 117693 | if( !zFile ) return SQLITE_NOMEM; |
| 117694 | |
| 117695 | iIn = 5; |
| 117696 | #ifndef SQLITE_ALLOW_URI_AUTHORITY |
| 117697 | /* Discard the scheme and authority segments of the URI. */ |
| 117698 | if( zUri[5]=='/' && zUri[6]=='/' ){ |
| 117699 | iIn = 7; |
| 117700 | while( zUri[iIn] && zUri[iIn]!='/' ) iIn++; |
| 117701 | if( iIn!=7 && (iIn!=16 || memcmp("localhost", &zUri[7], 9)) ){ |
| 117702 | *pzErrMsg = sqlite3_mprintf("invalid uri authority: %.*s", |
| 117703 | iIn-7, &zUri[7]); |
| 117704 | rc = SQLITE_ERROR; |
| 117705 | goto parse_uri_out; |
| 117706 | } |
| 117707 | } |
| 117708 | #endif |
| 117709 | |
| 117710 | /* Copy the filename and any query parameters into the zFile buffer. |
| 117711 | ** Decode %HH escape codes along the way. |
| 117712 | ** |
| 117713 | ** Within this loop, variable eState may be set to 0, 1 or 2, depending |
| 117714 |
+2
-2
| --- src/sqlite3.h | ||
| +++ src/sqlite3.h | ||
| @@ -107,11 +107,11 @@ | ||
| 107 | 107 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 108 | 108 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 109 | 109 | */ |
| 110 | 110 | #define SQLITE_VERSION "3.8.0" |
| 111 | 111 | #define SQLITE_VERSION_NUMBER 3008000 |
| 112 | -#define SQLITE_SOURCE_ID "2013-08-06 07:45:08 924f7e4d7a8fa2fe9100836663f3733b6e1a9084" | |
| 112 | +#define SQLITE_SOURCE_ID "2013-08-15 22:40:21 f2d175f975cd0be63425424ec322a98fb650019e" | |
| 113 | 113 | |
| 114 | 114 | /* |
| 115 | 115 | ** CAPI3REF: Run-Time Library Version Numbers |
| 116 | 116 | ** KEYWORDS: sqlite3_version, sqlite3_sourceid |
| 117 | 117 | ** |
| @@ -7231,11 +7231,11 @@ | ||
| 7231 | 7231 | #endif |
| 7232 | 7232 | |
| 7233 | 7233 | #ifdef __cplusplus |
| 7234 | 7234 | } /* End of the 'extern "C"' block */ |
| 7235 | 7235 | #endif |
| 7236 | -#endif | |
| 7236 | +#endif /* _SQLITE3_H_ */ | |
| 7237 | 7237 | |
| 7238 | 7238 | /* |
| 7239 | 7239 | ** 2010 August 30 |
| 7240 | 7240 | ** |
| 7241 | 7241 | ** The author disclaims copyright to this source code. In place of |
| 7242 | 7242 |
| --- src/sqlite3.h | |
| +++ src/sqlite3.h | |
| @@ -107,11 +107,11 @@ | |
| 107 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 108 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 109 | */ |
| 110 | #define SQLITE_VERSION "3.8.0" |
| 111 | #define SQLITE_VERSION_NUMBER 3008000 |
| 112 | #define SQLITE_SOURCE_ID "2013-08-06 07:45:08 924f7e4d7a8fa2fe9100836663f3733b6e1a9084" |
| 113 | |
| 114 | /* |
| 115 | ** CAPI3REF: Run-Time Library Version Numbers |
| 116 | ** KEYWORDS: sqlite3_version, sqlite3_sourceid |
| 117 | ** |
| @@ -7231,11 +7231,11 @@ | |
| 7231 | #endif |
| 7232 | |
| 7233 | #ifdef __cplusplus |
| 7234 | } /* End of the 'extern "C"' block */ |
| 7235 | #endif |
| 7236 | #endif |
| 7237 | |
| 7238 | /* |
| 7239 | ** 2010 August 30 |
| 7240 | ** |
| 7241 | ** The author disclaims copyright to this source code. In place of |
| 7242 |
| --- src/sqlite3.h | |
| +++ src/sqlite3.h | |
| @@ -107,11 +107,11 @@ | |
| 107 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 108 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 109 | */ |
| 110 | #define SQLITE_VERSION "3.8.0" |
| 111 | #define SQLITE_VERSION_NUMBER 3008000 |
| 112 | #define SQLITE_SOURCE_ID "2013-08-15 22:40:21 f2d175f975cd0be63425424ec322a98fb650019e" |
| 113 | |
| 114 | /* |
| 115 | ** CAPI3REF: Run-Time Library Version Numbers |
| 116 | ** KEYWORDS: sqlite3_version, sqlite3_sourceid |
| 117 | ** |
| @@ -7231,11 +7231,11 @@ | |
| 7231 | #endif |
| 7232 | |
| 7233 | #ifdef __cplusplus |
| 7234 | } /* End of the 'extern "C"' block */ |
| 7235 | #endif |
| 7236 | #endif /* _SQLITE3_H_ */ |
| 7237 | |
| 7238 | /* |
| 7239 | ** 2010 August 30 |
| 7240 | ** |
| 7241 | ** The author disclaims copyright to this source code. In place of |
| 7242 |
+14
-2
| --- src/style.c | ||
| +++ src/style.c | ||
| @@ -112,11 +112,11 @@ | ||
| 112 | 112 | if( nHref>=nHrefAlloc ){ |
| 113 | 113 | nHrefAlloc = nHrefAlloc*2 + 10; |
| 114 | 114 | aHref = fossil_realloc(aHref, nHrefAlloc*sizeof(aHref[0])); |
| 115 | 115 | } |
| 116 | 116 | aHref[nHref++] = zUrl; |
| 117 | - return mprintf("<a %s id='a%d'>", zExtra, nHref); | |
| 117 | + return mprintf("<a %s id='a%d' href='%R/honeypot'>", zExtra, nHref); | |
| 118 | 118 | } |
| 119 | 119 | char *href(const char *zFormat, ...){ |
| 120 | 120 | char *zUrl; |
| 121 | 121 | va_list ap; |
| 122 | 122 | va_start(ap, zFormat); |
| @@ -130,11 +130,11 @@ | ||
| 130 | 130 | if( nHref>=nHrefAlloc ){ |
| 131 | 131 | nHrefAlloc = nHrefAlloc*2 + 10; |
| 132 | 132 | aHref = fossil_realloc(aHref, nHrefAlloc*sizeof(aHref[0])); |
| 133 | 133 | } |
| 134 | 134 | aHref[nHref++] = zUrl; |
| 135 | - return mprintf("<a id='a%d'>", nHref); | |
| 135 | + return mprintf("<a id='a%d' href='%R/honeypot'>", nHref); | |
| 136 | 136 | } |
| 137 | 137 | |
| 138 | 138 | /* |
| 139 | 139 | ** Generate <form method="post" action=ARG>. The ARG value is inserted |
| 140 | 140 | ** by javascript. |
| @@ -972,10 +972,11 @@ | ||
| 972 | 972 | @ margin-top: 3px; |
| 973 | 973 | @ line-height: 100%; |
| 974 | 974 | }, |
| 975 | 975 | { "table.sbsdiffcols", |
| 976 | 976 | "side-by-side diff display (column-based)", |
| 977 | + @ width: 90%; | |
| 977 | 978 | @ border-spacing: 0; |
| 978 | 979 | @ font-size: xx-small; |
| 979 | 980 | }, |
| 980 | 981 | { "table.sbsdiffcols td", |
| 981 | 982 | "sbs diff table cell", |
| @@ -1176,10 +1177,11 @@ | ||
| 1176 | 1177 | if( login_has_capability(&c, 1) ) zCap[i++] = c; |
| 1177 | 1178 | } |
| 1178 | 1179 | zCap[i] = 0; |
| 1179 | 1180 | @ g.userUid = %d(g.userUid)<br /> |
| 1180 | 1181 | @ g.zLogin = %h(g.zLogin)<br /> |
| 1182 | + @ g.isHuman = %d(g.isHuman)<br /> | |
| 1181 | 1183 | @ capabilities = %s(zCap)<br /> |
| 1182 | 1184 | @ <hr> |
| 1183 | 1185 | P("HTTP_USER_AGENT"); |
| 1184 | 1186 | cgi_print_all(showAll); |
| 1185 | 1187 | if( showAll && blob_size(&g.httpHeader)>0 ){ |
| @@ -1192,5 +1194,15 @@ | ||
| 1192 | 1194 | const char *zRedir = P("redirect"); |
| 1193 | 1195 | if( zRedir ) cgi_redirect(zRedir); |
| 1194 | 1196 | } |
| 1195 | 1197 | style_footer(); |
| 1196 | 1198 | } |
| 1199 | + | |
| 1200 | +/* | |
| 1201 | +** This page is a honeypot for spiders and bots. | |
| 1202 | +** | |
| 1203 | +** WEBPAGE: honeypot | |
| 1204 | +*/ | |
| 1205 | +void honeypot_page(void){ | |
| 1206 | + cgi_set_status(403, "Forbidden"); | |
| 1207 | + @ <p>Access by spiders and robots is forbidden</p> | |
| 1208 | +} | |
| 1197 | 1209 |
| --- src/style.c | |
| +++ src/style.c | |
| @@ -112,11 +112,11 @@ | |
| 112 | if( nHref>=nHrefAlloc ){ |
| 113 | nHrefAlloc = nHrefAlloc*2 + 10; |
| 114 | aHref = fossil_realloc(aHref, nHrefAlloc*sizeof(aHref[0])); |
| 115 | } |
| 116 | aHref[nHref++] = zUrl; |
| 117 | return mprintf("<a %s id='a%d'>", zExtra, nHref); |
| 118 | } |
| 119 | char *href(const char *zFormat, ...){ |
| 120 | char *zUrl; |
| 121 | va_list ap; |
| 122 | va_start(ap, zFormat); |
| @@ -130,11 +130,11 @@ | |
| 130 | if( nHref>=nHrefAlloc ){ |
| 131 | nHrefAlloc = nHrefAlloc*2 + 10; |
| 132 | aHref = fossil_realloc(aHref, nHrefAlloc*sizeof(aHref[0])); |
| 133 | } |
| 134 | aHref[nHref++] = zUrl; |
| 135 | return mprintf("<a id='a%d'>", nHref); |
| 136 | } |
| 137 | |
| 138 | /* |
| 139 | ** Generate <form method="post" action=ARG>. The ARG value is inserted |
| 140 | ** by javascript. |
| @@ -972,10 +972,11 @@ | |
| 972 | @ margin-top: 3px; |
| 973 | @ line-height: 100%; |
| 974 | }, |
| 975 | { "table.sbsdiffcols", |
| 976 | "side-by-side diff display (column-based)", |
| 977 | @ border-spacing: 0; |
| 978 | @ font-size: xx-small; |
| 979 | }, |
| 980 | { "table.sbsdiffcols td", |
| 981 | "sbs diff table cell", |
| @@ -1176,10 +1177,11 @@ | |
| 1176 | if( login_has_capability(&c, 1) ) zCap[i++] = c; |
| 1177 | } |
| 1178 | zCap[i] = 0; |
| 1179 | @ g.userUid = %d(g.userUid)<br /> |
| 1180 | @ g.zLogin = %h(g.zLogin)<br /> |
| 1181 | @ capabilities = %s(zCap)<br /> |
| 1182 | @ <hr> |
| 1183 | P("HTTP_USER_AGENT"); |
| 1184 | cgi_print_all(showAll); |
| 1185 | if( showAll && blob_size(&g.httpHeader)>0 ){ |
| @@ -1192,5 +1194,15 @@ | |
| 1192 | const char *zRedir = P("redirect"); |
| 1193 | if( zRedir ) cgi_redirect(zRedir); |
| 1194 | } |
| 1195 | style_footer(); |
| 1196 | } |
| 1197 |
| --- src/style.c | |
| +++ src/style.c | |
| @@ -112,11 +112,11 @@ | |
| 112 | if( nHref>=nHrefAlloc ){ |
| 113 | nHrefAlloc = nHrefAlloc*2 + 10; |
| 114 | aHref = fossil_realloc(aHref, nHrefAlloc*sizeof(aHref[0])); |
| 115 | } |
| 116 | aHref[nHref++] = zUrl; |
| 117 | return mprintf("<a %s id='a%d' href='%R/honeypot'>", zExtra, nHref); |
| 118 | } |
| 119 | char *href(const char *zFormat, ...){ |
| 120 | char *zUrl; |
| 121 | va_list ap; |
| 122 | va_start(ap, zFormat); |
| @@ -130,11 +130,11 @@ | |
| 130 | if( nHref>=nHrefAlloc ){ |
| 131 | nHrefAlloc = nHrefAlloc*2 + 10; |
| 132 | aHref = fossil_realloc(aHref, nHrefAlloc*sizeof(aHref[0])); |
| 133 | } |
| 134 | aHref[nHref++] = zUrl; |
| 135 | return mprintf("<a id='a%d' href='%R/honeypot'>", nHref); |
| 136 | } |
| 137 | |
| 138 | /* |
| 139 | ** Generate <form method="post" action=ARG>. The ARG value is inserted |
| 140 | ** by javascript. |
| @@ -972,10 +972,11 @@ | |
| 972 | @ margin-top: 3px; |
| 973 | @ line-height: 100%; |
| 974 | }, |
| 975 | { "table.sbsdiffcols", |
| 976 | "side-by-side diff display (column-based)", |
| 977 | @ width: 90%; |
| 978 | @ border-spacing: 0; |
| 979 | @ font-size: xx-small; |
| 980 | }, |
| 981 | { "table.sbsdiffcols td", |
| 982 | "sbs diff table cell", |
| @@ -1176,10 +1177,11 @@ | |
| 1177 | if( login_has_capability(&c, 1) ) zCap[i++] = c; |
| 1178 | } |
| 1179 | zCap[i] = 0; |
| 1180 | @ g.userUid = %d(g.userUid)<br /> |
| 1181 | @ g.zLogin = %h(g.zLogin)<br /> |
| 1182 | @ g.isHuman = %d(g.isHuman)<br /> |
| 1183 | @ capabilities = %s(zCap)<br /> |
| 1184 | @ <hr> |
| 1185 | P("HTTP_USER_AGENT"); |
| 1186 | cgi_print_all(showAll); |
| 1187 | if( showAll && blob_size(&g.httpHeader)>0 ){ |
| @@ -1192,5 +1194,15 @@ | |
| 1194 | const char *zRedir = P("redirect"); |
| 1195 | if( zRedir ) cgi_redirect(zRedir); |
| 1196 | } |
| 1197 | style_footer(); |
| 1198 | } |
| 1199 | |
| 1200 | /* |
| 1201 | ** This page is a honeypot for spiders and bots. |
| 1202 | ** |
| 1203 | ** WEBPAGE: honeypot |
| 1204 | */ |
| 1205 | void honeypot_page(void){ |
| 1206 | cgi_set_status(403, "Forbidden"); |
| 1207 | @ <p>Access by spiders and robots is forbidden</p> |
| 1208 | } |
| 1209 |
+9
-5
| --- src/sync.c | ||
| +++ src/sync.c | ||
| @@ -154,10 +154,12 @@ | ||
| 154 | 154 | ** Usage: %fossil pull ?URL? ?options? |
| 155 | 155 | ** |
| 156 | 156 | ** Pull changes from a remote repository into the local repository. |
| 157 | 157 | ** Use the "-R REPO" or "--repository REPO" command-line options |
| 158 | 158 | ** to specify an alternative repository file. |
| 159 | +** | |
| 160 | +** See clone usage for possible URL formats. | |
| 159 | 161 | ** |
| 160 | 162 | ** If the URL is not specified, then the URL from the most recent |
| 161 | 163 | ** clone, push, pull, remote-url, or sync command is used. |
| 162 | 164 | ** |
| 163 | 165 | ** The URL specified normally becomes the new "remote-url" used for |
| @@ -183,10 +185,12 @@ | ||
| 183 | 185 | ** Usage: %fossil push ?URL? ?options? |
| 184 | 186 | ** |
| 185 | 187 | ** Push changes in the local repository over into a remote repository. |
| 186 | 188 | ** Use the "-R REPO" or "--repository REPO" command-line options |
| 187 | 189 | ** to specify an alternative repository file. |
| 190 | +** | |
| 191 | +** See clone usage for possible URL formats. | |
| 188 | 192 | ** |
| 189 | 193 | ** If the URL is not specified, then the URL from the most recent |
| 190 | 194 | ** clone, push, pull, remote-url, or sync command is used. |
| 191 | 195 | ** |
| 192 | 196 | ** The URL specified normally becomes the new "remote-url" used for |
| @@ -218,16 +222,14 @@ | ||
| 218 | 222 | ** Synchronize the local repository with a remote repository. This is |
| 219 | 223 | ** the equivalent of running both "push" and "pull" at the same time. |
| 220 | 224 | ** Use the "-R REPO" or "--repository REPO" command-line options |
| 221 | 225 | ** to specify an alternative repository file. |
| 222 | 226 | ** |
| 223 | -** If a user-id and password are required, specify them as follows: | |
| 227 | +** See clone usage for possible URL formats. | |
| 224 | 228 | ** |
| 225 | -** http://userid:[email protected]:1234/path | |
| 226 | -** | |
| 227 | -** If the URL is not specified, then the URL from the most recent successful | |
| 228 | -** clone, push, pull, remote-url, or sync command is used. | |
| 229 | +** If the URL is not specified, then the URL from the most recent | |
| 230 | +** successful clone, push, pull, remote-url, or sync command is used. | |
| 229 | 231 | ** |
| 230 | 232 | ** The URL specified normally becomes the new "remote-url" used for |
| 231 | 233 | ** subsequent push, pull, and sync operations. However, the "--once" |
| 232 | 234 | ** command-line option makes the URL a one-time-use URL that is not |
| 233 | 235 | ** saved. |
| @@ -258,10 +260,12 @@ | ||
| 258 | 260 | ** |
| 259 | 261 | ** The remote-url is set automatically by a "clone" command or by any |
| 260 | 262 | ** "sync", "push", or "pull" command that specifies an explicit URL. |
| 261 | 263 | ** The default remote-url is used by auto-syncing and by "sync", "push", |
| 262 | 264 | ** "pull" that omit the server URL. |
| 265 | +** | |
| 266 | +** See clone usage for possible URL formats. | |
| 263 | 267 | ** |
| 264 | 268 | ** See also: clone, push, pull, sync |
| 265 | 269 | */ |
| 266 | 270 | void remote_url_cmd(void){ |
| 267 | 271 | char *zUrl; |
| 268 | 272 |
| --- src/sync.c | |
| +++ src/sync.c | |
| @@ -154,10 +154,12 @@ | |
| 154 | ** Usage: %fossil pull ?URL? ?options? |
| 155 | ** |
| 156 | ** Pull changes from a remote repository into the local repository. |
| 157 | ** Use the "-R REPO" or "--repository REPO" command-line options |
| 158 | ** to specify an alternative repository file. |
| 159 | ** |
| 160 | ** If the URL is not specified, then the URL from the most recent |
| 161 | ** clone, push, pull, remote-url, or sync command is used. |
| 162 | ** |
| 163 | ** The URL specified normally becomes the new "remote-url" used for |
| @@ -183,10 +185,12 @@ | |
| 183 | ** Usage: %fossil push ?URL? ?options? |
| 184 | ** |
| 185 | ** Push changes in the local repository over into a remote repository. |
| 186 | ** Use the "-R REPO" or "--repository REPO" command-line options |
| 187 | ** to specify an alternative repository file. |
| 188 | ** |
| 189 | ** If the URL is not specified, then the URL from the most recent |
| 190 | ** clone, push, pull, remote-url, or sync command is used. |
| 191 | ** |
| 192 | ** The URL specified normally becomes the new "remote-url" used for |
| @@ -218,16 +222,14 @@ | |
| 218 | ** Synchronize the local repository with a remote repository. This is |
| 219 | ** the equivalent of running both "push" and "pull" at the same time. |
| 220 | ** Use the "-R REPO" or "--repository REPO" command-line options |
| 221 | ** to specify an alternative repository file. |
| 222 | ** |
| 223 | ** If a user-id and password are required, specify them as follows: |
| 224 | ** |
| 225 | ** http://userid:[email protected]:1234/path |
| 226 | ** |
| 227 | ** If the URL is not specified, then the URL from the most recent successful |
| 228 | ** clone, push, pull, remote-url, or sync command is used. |
| 229 | ** |
| 230 | ** The URL specified normally becomes the new "remote-url" used for |
| 231 | ** subsequent push, pull, and sync operations. However, the "--once" |
| 232 | ** command-line option makes the URL a one-time-use URL that is not |
| 233 | ** saved. |
| @@ -258,10 +260,12 @@ | |
| 258 | ** |
| 259 | ** The remote-url is set automatically by a "clone" command or by any |
| 260 | ** "sync", "push", or "pull" command that specifies an explicit URL. |
| 261 | ** The default remote-url is used by auto-syncing and by "sync", "push", |
| 262 | ** "pull" that omit the server URL. |
| 263 | ** |
| 264 | ** See also: clone, push, pull, sync |
| 265 | */ |
| 266 | void remote_url_cmd(void){ |
| 267 | char *zUrl; |
| 268 |
| --- src/sync.c | |
| +++ src/sync.c | |
| @@ -154,10 +154,12 @@ | |
| 154 | ** Usage: %fossil pull ?URL? ?options? |
| 155 | ** |
| 156 | ** Pull changes from a remote repository into the local repository. |
| 157 | ** Use the "-R REPO" or "--repository REPO" command-line options |
| 158 | ** to specify an alternative repository file. |
| 159 | ** |
| 160 | ** See clone usage for possible URL formats. |
| 161 | ** |
| 162 | ** If the URL is not specified, then the URL from the most recent |
| 163 | ** clone, push, pull, remote-url, or sync command is used. |
| 164 | ** |
| 165 | ** The URL specified normally becomes the new "remote-url" used for |
| @@ -183,10 +185,12 @@ | |
| 185 | ** Usage: %fossil push ?URL? ?options? |
| 186 | ** |
| 187 | ** Push changes in the local repository over into a remote repository. |
| 188 | ** Use the "-R REPO" or "--repository REPO" command-line options |
| 189 | ** to specify an alternative repository file. |
| 190 | ** |
| 191 | ** See clone usage for possible URL formats. |
| 192 | ** |
| 193 | ** If the URL is not specified, then the URL from the most recent |
| 194 | ** clone, push, pull, remote-url, or sync command is used. |
| 195 | ** |
| 196 | ** The URL specified normally becomes the new "remote-url" used for |
| @@ -218,16 +222,14 @@ | |
| 222 | ** Synchronize the local repository with a remote repository. This is |
| 223 | ** the equivalent of running both "push" and "pull" at the same time. |
| 224 | ** Use the "-R REPO" or "--repository REPO" command-line options |
| 225 | ** to specify an alternative repository file. |
| 226 | ** |
| 227 | ** See clone usage for possible URL formats. |
| 228 | ** |
| 229 | ** If the URL is not specified, then the URL from the most recent |
| 230 | ** successful clone, push, pull, remote-url, or sync command is used. |
| 231 | ** |
| 232 | ** The URL specified normally becomes the new "remote-url" used for |
| 233 | ** subsequent push, pull, and sync operations. However, the "--once" |
| 234 | ** command-line option makes the URL a one-time-use URL that is not |
| 235 | ** saved. |
| @@ -258,10 +260,12 @@ | |
| 260 | ** |
| 261 | ** The remote-url is set automatically by a "clone" command or by any |
| 262 | ** "sync", "push", or "pull" command that specifies an explicit URL. |
| 263 | ** The default remote-url is used by auto-syncing and by "sync", "push", |
| 264 | ** "pull" that omit the server URL. |
| 265 | ** |
| 266 | ** See clone usage for possible URL formats. |
| 267 | ** |
| 268 | ** See also: clone, push, pull, sync |
| 269 | */ |
| 270 | void remote_url_cmd(void){ |
| 271 | char *zUrl; |
| 272 |
+9
-5
| --- src/sync.c | ||
| +++ src/sync.c | ||
| @@ -154,10 +154,12 @@ | ||
| 154 | 154 | ** Usage: %fossil pull ?URL? ?options? |
| 155 | 155 | ** |
| 156 | 156 | ** Pull changes from a remote repository into the local repository. |
| 157 | 157 | ** Use the "-R REPO" or "--repository REPO" command-line options |
| 158 | 158 | ** to specify an alternative repository file. |
| 159 | +** | |
| 160 | +** See clone usage for possible URL formats. | |
| 159 | 161 | ** |
| 160 | 162 | ** If the URL is not specified, then the URL from the most recent |
| 161 | 163 | ** clone, push, pull, remote-url, or sync command is used. |
| 162 | 164 | ** |
| 163 | 165 | ** The URL specified normally becomes the new "remote-url" used for |
| @@ -183,10 +185,12 @@ | ||
| 183 | 185 | ** Usage: %fossil push ?URL? ?options? |
| 184 | 186 | ** |
| 185 | 187 | ** Push changes in the local repository over into a remote repository. |
| 186 | 188 | ** Use the "-R REPO" or "--repository REPO" command-line options |
| 187 | 189 | ** to specify an alternative repository file. |
| 190 | +** | |
| 191 | +** See clone usage for possible URL formats. | |
| 188 | 192 | ** |
| 189 | 193 | ** If the URL is not specified, then the URL from the most recent |
| 190 | 194 | ** clone, push, pull, remote-url, or sync command is used. |
| 191 | 195 | ** |
| 192 | 196 | ** The URL specified normally becomes the new "remote-url" used for |
| @@ -218,16 +222,14 @@ | ||
| 218 | 222 | ** Synchronize the local repository with a remote repository. This is |
| 219 | 223 | ** the equivalent of running both "push" and "pull" at the same time. |
| 220 | 224 | ** Use the "-R REPO" or "--repository REPO" command-line options |
| 221 | 225 | ** to specify an alternative repository file. |
| 222 | 226 | ** |
| 223 | -** If a user-id and password are required, specify them as follows: | |
| 227 | +** See clone usage for possible URL formats. | |
| 224 | 228 | ** |
| 225 | -** http://userid:[email protected]:1234/path | |
| 226 | -** | |
| 227 | -** If the URL is not specified, then the URL from the most recent successful | |
| 228 | -** clone, push, pull, remote-url, or sync command is used. | |
| 229 | +** If the URL is not specified, then the URL from the most recent | |
| 230 | +** successful clone, push, pull, remote-url, or sync command is used. | |
| 229 | 231 | ** |
| 230 | 232 | ** The URL specified normally becomes the new "remote-url" used for |
| 231 | 233 | ** subsequent push, pull, and sync operations. However, the "--once" |
| 232 | 234 | ** command-line option makes the URL a one-time-use URL that is not |
| 233 | 235 | ** saved. |
| @@ -258,10 +260,12 @@ | ||
| 258 | 260 | ** |
| 259 | 261 | ** The remote-url is set automatically by a "clone" command or by any |
| 260 | 262 | ** "sync", "push", or "pull" command that specifies an explicit URL. |
| 261 | 263 | ** The default remote-url is used by auto-syncing and by "sync", "push", |
| 262 | 264 | ** "pull" that omit the server URL. |
| 265 | +** | |
| 266 | +** See clone usage for possible URL formats. | |
| 263 | 267 | ** |
| 264 | 268 | ** See also: clone, push, pull, sync |
| 265 | 269 | */ |
| 266 | 270 | void remote_url_cmd(void){ |
| 267 | 271 | char *zUrl; |
| 268 | 272 |
| --- src/sync.c | |
| +++ src/sync.c | |
| @@ -154,10 +154,12 @@ | |
| 154 | ** Usage: %fossil pull ?URL? ?options? |
| 155 | ** |
| 156 | ** Pull changes from a remote repository into the local repository. |
| 157 | ** Use the "-R REPO" or "--repository REPO" command-line options |
| 158 | ** to specify an alternative repository file. |
| 159 | ** |
| 160 | ** If the URL is not specified, then the URL from the most recent |
| 161 | ** clone, push, pull, remote-url, or sync command is used. |
| 162 | ** |
| 163 | ** The URL specified normally becomes the new "remote-url" used for |
| @@ -183,10 +185,12 @@ | |
| 183 | ** Usage: %fossil push ?URL? ?options? |
| 184 | ** |
| 185 | ** Push changes in the local repository over into a remote repository. |
| 186 | ** Use the "-R REPO" or "--repository REPO" command-line options |
| 187 | ** to specify an alternative repository file. |
| 188 | ** |
| 189 | ** If the URL is not specified, then the URL from the most recent |
| 190 | ** clone, push, pull, remote-url, or sync command is used. |
| 191 | ** |
| 192 | ** The URL specified normally becomes the new "remote-url" used for |
| @@ -218,16 +222,14 @@ | |
| 218 | ** Synchronize the local repository with a remote repository. This is |
| 219 | ** the equivalent of running both "push" and "pull" at the same time. |
| 220 | ** Use the "-R REPO" or "--repository REPO" command-line options |
| 221 | ** to specify an alternative repository file. |
| 222 | ** |
| 223 | ** If a user-id and password are required, specify them as follows: |
| 224 | ** |
| 225 | ** http://userid:[email protected]:1234/path |
| 226 | ** |
| 227 | ** If the URL is not specified, then the URL from the most recent successful |
| 228 | ** clone, push, pull, remote-url, or sync command is used. |
| 229 | ** |
| 230 | ** The URL specified normally becomes the new "remote-url" used for |
| 231 | ** subsequent push, pull, and sync operations. However, the "--once" |
| 232 | ** command-line option makes the URL a one-time-use URL that is not |
| 233 | ** saved. |
| @@ -258,10 +260,12 @@ | |
| 258 | ** |
| 259 | ** The remote-url is set automatically by a "clone" command or by any |
| 260 | ** "sync", "push", or "pull" command that specifies an explicit URL. |
| 261 | ** The default remote-url is used by auto-syncing and by "sync", "push", |
| 262 | ** "pull" that omit the server URL. |
| 263 | ** |
| 264 | ** See also: clone, push, pull, sync |
| 265 | */ |
| 266 | void remote_url_cmd(void){ |
| 267 | char *zUrl; |
| 268 |
| --- src/sync.c | |
| +++ src/sync.c | |
| @@ -154,10 +154,12 @@ | |
| 154 | ** Usage: %fossil pull ?URL? ?options? |
| 155 | ** |
| 156 | ** Pull changes from a remote repository into the local repository. |
| 157 | ** Use the "-R REPO" or "--repository REPO" command-line options |
| 158 | ** to specify an alternative repository file. |
| 159 | ** |
| 160 | ** See clone usage for possible URL formats. |
| 161 | ** |
| 162 | ** If the URL is not specified, then the URL from the most recent |
| 163 | ** clone, push, pull, remote-url, or sync command is used. |
| 164 | ** |
| 165 | ** The URL specified normally becomes the new "remote-url" used for |
| @@ -183,10 +185,12 @@ | |
| 185 | ** Usage: %fossil push ?URL? ?options? |
| 186 | ** |
| 187 | ** Push changes in the local repository over into a remote repository. |
| 188 | ** Use the "-R REPO" or "--repository REPO" command-line options |
| 189 | ** to specify an alternative repository file. |
| 190 | ** |
| 191 | ** See clone usage for possible URL formats. |
| 192 | ** |
| 193 | ** If the URL is not specified, then the URL from the most recent |
| 194 | ** clone, push, pull, remote-url, or sync command is used. |
| 195 | ** |
| 196 | ** The URL specified normally becomes the new "remote-url" used for |
| @@ -218,16 +222,14 @@ | |
| 222 | ** Synchronize the local repository with a remote repository. This is |
| 223 | ** the equivalent of running both "push" and "pull" at the same time. |
| 224 | ** Use the "-R REPO" or "--repository REPO" command-line options |
| 225 | ** to specify an alternative repository file. |
| 226 | ** |
| 227 | ** See clone usage for possible URL formats. |
| 228 | ** |
| 229 | ** If the URL is not specified, then the URL from the most recent |
| 230 | ** successful clone, push, pull, remote-url, or sync command is used. |
| 231 | ** |
| 232 | ** The URL specified normally becomes the new "remote-url" used for |
| 233 | ** subsequent push, pull, and sync operations. However, the "--once" |
| 234 | ** command-line option makes the URL a one-time-use URL that is not |
| 235 | ** saved. |
| @@ -258,10 +260,12 @@ | |
| 260 | ** |
| 261 | ** The remote-url is set automatically by a "clone" command or by any |
| 262 | ** "sync", "push", or "pull" command that specifies an explicit URL. |
| 263 | ** The default remote-url is used by auto-syncing and by "sync", "push", |
| 264 | ** "pull" that omit the server URL. |
| 265 | ** |
| 266 | ** See clone usage for possible URL formats. |
| 267 | ** |
| 268 | ** See also: clone, push, pull, sync |
| 269 | */ |
| 270 | void remote_url_cmd(void){ |
| 271 | char *zUrl; |
| 272 |
+21
-16
| --- src/timeline.c | ||
| +++ src/timeline.c | ||
| @@ -649,10 +649,15 @@ | ||
| 649 | 649 | cgi_printf("],h:\"%s\"}%s", pRow->zUuid, pRow->pNext ? ",\n" : "];\n"); |
| 650 | 650 | } |
| 651 | 651 | cgi_printf("var nrail = %d\n", pGraph->mxRail+1); |
| 652 | 652 | graph_free(pGraph); |
| 653 | 653 | @ var canvasDiv = gebi("canvas"); |
| 654 | + @ var canvasStyle = window.getComputedStyle(canvasDiv,null); | |
| 655 | + @ var lineColor = canvasStyle.getPropertyValue('color') || 'black'; | |
| 656 | + @ var bgColor = canvasStyle.getPropertyValue('background-color') || 'white'; | |
| 657 | + @ if( bgColor=='transparent' ) bgColor = 'white'; | |
| 658 | + @ var boxColor = lineColor; | |
| 654 | 659 | @ function drawBox(color,x0,y0,x1,y1){ |
| 655 | 660 | @ var n = document.createElement("div"); |
| 656 | 661 | @ if( x0>x1 ){ var t=x0; x0=x1; x1=t; } |
| 657 | 662 | @ if( y0>y1 ){ var t=y0; y0=y1; y1=t; } |
| 658 | 663 | @ var w = x1-x0+1; |
| @@ -689,38 +694,38 @@ | ||
| 689 | 694 | @ }while( obj = obj.offsetParent ); |
| 690 | 695 | @ } |
| 691 | 696 | @ return left; |
| 692 | 697 | @ } |
| 693 | 698 | @ function drawUpArrow(x,y0,y1){ |
| 694 | - @ drawBox("black",x,y0,x+1,y1); | |
| 699 | + @ drawBox(lineColor,x,y0,x+1,y1); | |
| 695 | 700 | @ if( y0+10>=y1 ){ |
| 696 | - @ drawBox("black",x-1,y0+1,x+2,y0+2); | |
| 697 | - @ drawBox("black",x-2,y0+3,x+3,y0+4); | |
| 701 | + @ drawBox(lineColor,x-1,y0+1,x+2,y0+2); | |
| 702 | + @ drawBox(lineColor,x-2,y0+3,x+3,y0+4); | |
| 698 | 703 | @ }else{ |
| 699 | - @ drawBox("black",x-1,y0+2,x+2,y0+4); | |
| 700 | - @ drawBox("black",x-2,y0+5,x+3,y0+7); | |
| 704 | + @ drawBox(lineColor,x-1,y0+2,x+2,y0+4); | |
| 705 | + @ drawBox(lineColor,x-2,y0+5,x+3,y0+7); | |
| 701 | 706 | @ } |
| 702 | 707 | @ } |
| 703 | 708 | @ function drawThinArrow(y,xFrom,xTo){ |
| 704 | 709 | @ if( xFrom<xTo ){ |
| 705 | - @ drawBox("black",xFrom,y,xTo,y); | |
| 706 | - @ drawBox("black",xTo-3,y-1,xTo-2,y+1); | |
| 707 | - @ drawBox("black",xTo-4,y-2,xTo-4,y+2); | |
| 710 | + @ drawBox(lineColor,xFrom,y,xTo,y); | |
| 711 | + @ drawBox(lineColor,xTo-3,y-1,xTo-2,y+1); | |
| 712 | + @ drawBox(lineColor,xTo-4,y-2,xTo-4,y+2); | |
| 708 | 713 | @ }else{ |
| 709 | - @ drawBox("black",xTo,y,xFrom,y); | |
| 710 | - @ drawBox("black",xTo+2,y-1,xTo+3,y+1); | |
| 711 | - @ drawBox("black",xTo+4,y-2,xTo+4,y+2); | |
| 714 | + @ drawBox(lineColor,xTo,y,xFrom,y); | |
| 715 | + @ drawBox(lineColor,xTo+2,y-1,xTo+3,y+1); | |
| 716 | + @ drawBox(lineColor,xTo+4,y-2,xTo+4,y+2); | |
| 712 | 717 | @ } |
| 713 | 718 | @ } |
| 714 | 719 | @ function drawThinLine(x0,y0,x1,y1){ |
| 715 | - @ drawBox("black",x0,y0,x1,y1); | |
| 720 | + @ drawBox(lineColor,x0,y0,x1,y1); | |
| 716 | 721 | @ } |
| 717 | 722 | @ function drawNode(p, left, btm){ |
| 718 | - @ drawBox("black",p.x-5,p.y-5,p.x+6,p.y+6); | |
| 719 | - @ drawBox(p.bg,p.x-4,p.y-4,p.x+5,p.y+5); | |
| 723 | + @ drawBox(boxColor,p.x-5,p.y-5,p.x+6,p.y+6); | |
| 724 | + @ drawBox(p.bg||bgColor,p.x-4,p.y-4,p.x+5,p.y+5); | |
| 720 | 725 | @ if( p.u>0 ) drawUpArrow(p.x, rowinfo[p.u-1].y+6, p.y-5); |
| 721 | - @ if( p.f&1 ) drawBox("black",p.x-1,p.y-1,p.x+2,p.y+2); | |
| 726 | + @ if( p.f&1 ) drawBox(boxColor,p.x-1,p.y-1,p.x+2,p.y+2); | |
| 722 | 727 | if( !omitDescenders ){ |
| 723 | 728 | @ if( p.u==0 ) drawUpArrow(p.x, 0, p.y-5); |
| 724 | 729 | @ if( p.d ) drawUpArrow(p.x, p.y+6, btm); |
| 725 | 730 | } |
| 726 | 731 | @ if( p.mo>0 ){ |
| @@ -740,11 +745,11 @@ | ||
| 740 | 745 | @ for(var i=0; i<n; i+=2){ |
| 741 | 746 | @ var x1 = p.au[i]*railPitch + left; |
| 742 | 747 | @ var x0 = x1>p.x ? p.x+7 : p.x-6; |
| 743 | 748 | @ var u = rowinfo[p.au[i+1]-1]; |
| 744 | 749 | @ if(u.id<p.id){ |
| 745 | - @ drawBox("black",x0,p.y,x1,p.y+1); | |
| 750 | + @ drawBox(lineColor,x0,p.y,x1,p.y+1); | |
| 746 | 751 | @ drawUpArrow(x1, u.y+6, p.y); |
| 747 | 752 | @ }else{ |
| 748 | 753 | @ drawBox("#600000",x0,p.y,x1,p.y+1); |
| 749 | 754 | @ drawBox("#600000",x1-1,p.y,x1,u.y+1); |
| 750 | 755 | @ drawBox("#600000",x1,u.y,u.x-6,u.y+1); |
| 751 | 756 |
| --- src/timeline.c | |
| +++ src/timeline.c | |
| @@ -649,10 +649,15 @@ | |
| 649 | cgi_printf("],h:\"%s\"}%s", pRow->zUuid, pRow->pNext ? ",\n" : "];\n"); |
| 650 | } |
| 651 | cgi_printf("var nrail = %d\n", pGraph->mxRail+1); |
| 652 | graph_free(pGraph); |
| 653 | @ var canvasDiv = gebi("canvas"); |
| 654 | @ function drawBox(color,x0,y0,x1,y1){ |
| 655 | @ var n = document.createElement("div"); |
| 656 | @ if( x0>x1 ){ var t=x0; x0=x1; x1=t; } |
| 657 | @ if( y0>y1 ){ var t=y0; y0=y1; y1=t; } |
| 658 | @ var w = x1-x0+1; |
| @@ -689,38 +694,38 @@ | |
| 689 | @ }while( obj = obj.offsetParent ); |
| 690 | @ } |
| 691 | @ return left; |
| 692 | @ } |
| 693 | @ function drawUpArrow(x,y0,y1){ |
| 694 | @ drawBox("black",x,y0,x+1,y1); |
| 695 | @ if( y0+10>=y1 ){ |
| 696 | @ drawBox("black",x-1,y0+1,x+2,y0+2); |
| 697 | @ drawBox("black",x-2,y0+3,x+3,y0+4); |
| 698 | @ }else{ |
| 699 | @ drawBox("black",x-1,y0+2,x+2,y0+4); |
| 700 | @ drawBox("black",x-2,y0+5,x+3,y0+7); |
| 701 | @ } |
| 702 | @ } |
| 703 | @ function drawThinArrow(y,xFrom,xTo){ |
| 704 | @ if( xFrom<xTo ){ |
| 705 | @ drawBox("black",xFrom,y,xTo,y); |
| 706 | @ drawBox("black",xTo-3,y-1,xTo-2,y+1); |
| 707 | @ drawBox("black",xTo-4,y-2,xTo-4,y+2); |
| 708 | @ }else{ |
| 709 | @ drawBox("black",xTo,y,xFrom,y); |
| 710 | @ drawBox("black",xTo+2,y-1,xTo+3,y+1); |
| 711 | @ drawBox("black",xTo+4,y-2,xTo+4,y+2); |
| 712 | @ } |
| 713 | @ } |
| 714 | @ function drawThinLine(x0,y0,x1,y1){ |
| 715 | @ drawBox("black",x0,y0,x1,y1); |
| 716 | @ } |
| 717 | @ function drawNode(p, left, btm){ |
| 718 | @ drawBox("black",p.x-5,p.y-5,p.x+6,p.y+6); |
| 719 | @ drawBox(p.bg,p.x-4,p.y-4,p.x+5,p.y+5); |
| 720 | @ if( p.u>0 ) drawUpArrow(p.x, rowinfo[p.u-1].y+6, p.y-5); |
| 721 | @ if( p.f&1 ) drawBox("black",p.x-1,p.y-1,p.x+2,p.y+2); |
| 722 | if( !omitDescenders ){ |
| 723 | @ if( p.u==0 ) drawUpArrow(p.x, 0, p.y-5); |
| 724 | @ if( p.d ) drawUpArrow(p.x, p.y+6, btm); |
| 725 | } |
| 726 | @ if( p.mo>0 ){ |
| @@ -740,11 +745,11 @@ | |
| 740 | @ for(var i=0; i<n; i+=2){ |
| 741 | @ var x1 = p.au[i]*railPitch + left; |
| 742 | @ var x0 = x1>p.x ? p.x+7 : p.x-6; |
| 743 | @ var u = rowinfo[p.au[i+1]-1]; |
| 744 | @ if(u.id<p.id){ |
| 745 | @ drawBox("black",x0,p.y,x1,p.y+1); |
| 746 | @ drawUpArrow(x1, u.y+6, p.y); |
| 747 | @ }else{ |
| 748 | @ drawBox("#600000",x0,p.y,x1,p.y+1); |
| 749 | @ drawBox("#600000",x1-1,p.y,x1,u.y+1); |
| 750 | @ drawBox("#600000",x1,u.y,u.x-6,u.y+1); |
| 751 |
| --- src/timeline.c | |
| +++ src/timeline.c | |
| @@ -649,10 +649,15 @@ | |
| 649 | cgi_printf("],h:\"%s\"}%s", pRow->zUuid, pRow->pNext ? ",\n" : "];\n"); |
| 650 | } |
| 651 | cgi_printf("var nrail = %d\n", pGraph->mxRail+1); |
| 652 | graph_free(pGraph); |
| 653 | @ var canvasDiv = gebi("canvas"); |
| 654 | @ var canvasStyle = window.getComputedStyle(canvasDiv,null); |
| 655 | @ var lineColor = canvasStyle.getPropertyValue('color') || 'black'; |
| 656 | @ var bgColor = canvasStyle.getPropertyValue('background-color') || 'white'; |
| 657 | @ if( bgColor=='transparent' ) bgColor = 'white'; |
| 658 | @ var boxColor = lineColor; |
| 659 | @ function drawBox(color,x0,y0,x1,y1){ |
| 660 | @ var n = document.createElement("div"); |
| 661 | @ if( x0>x1 ){ var t=x0; x0=x1; x1=t; } |
| 662 | @ if( y0>y1 ){ var t=y0; y0=y1; y1=t; } |
| 663 | @ var w = x1-x0+1; |
| @@ -689,38 +694,38 @@ | |
| 694 | @ }while( obj = obj.offsetParent ); |
| 695 | @ } |
| 696 | @ return left; |
| 697 | @ } |
| 698 | @ function drawUpArrow(x,y0,y1){ |
| 699 | @ drawBox(lineColor,x,y0,x+1,y1); |
| 700 | @ if( y0+10>=y1 ){ |
| 701 | @ drawBox(lineColor,x-1,y0+1,x+2,y0+2); |
| 702 | @ drawBox(lineColor,x-2,y0+3,x+3,y0+4); |
| 703 | @ }else{ |
| 704 | @ drawBox(lineColor,x-1,y0+2,x+2,y0+4); |
| 705 | @ drawBox(lineColor,x-2,y0+5,x+3,y0+7); |
| 706 | @ } |
| 707 | @ } |
| 708 | @ function drawThinArrow(y,xFrom,xTo){ |
| 709 | @ if( xFrom<xTo ){ |
| 710 | @ drawBox(lineColor,xFrom,y,xTo,y); |
| 711 | @ drawBox(lineColor,xTo-3,y-1,xTo-2,y+1); |
| 712 | @ drawBox(lineColor,xTo-4,y-2,xTo-4,y+2); |
| 713 | @ }else{ |
| 714 | @ drawBox(lineColor,xTo,y,xFrom,y); |
| 715 | @ drawBox(lineColor,xTo+2,y-1,xTo+3,y+1); |
| 716 | @ drawBox(lineColor,xTo+4,y-2,xTo+4,y+2); |
| 717 | @ } |
| 718 | @ } |
| 719 | @ function drawThinLine(x0,y0,x1,y1){ |
| 720 | @ drawBox(lineColor,x0,y0,x1,y1); |
| 721 | @ } |
| 722 | @ function drawNode(p, left, btm){ |
| 723 | @ drawBox(boxColor,p.x-5,p.y-5,p.x+6,p.y+6); |
| 724 | @ drawBox(p.bg||bgColor,p.x-4,p.y-4,p.x+5,p.y+5); |
| 725 | @ if( p.u>0 ) drawUpArrow(p.x, rowinfo[p.u-1].y+6, p.y-5); |
| 726 | @ if( p.f&1 ) drawBox(boxColor,p.x-1,p.y-1,p.x+2,p.y+2); |
| 727 | if( !omitDescenders ){ |
| 728 | @ if( p.u==0 ) drawUpArrow(p.x, 0, p.y-5); |
| 729 | @ if( p.d ) drawUpArrow(p.x, p.y+6, btm); |
| 730 | } |
| 731 | @ if( p.mo>0 ){ |
| @@ -740,11 +745,11 @@ | |
| 745 | @ for(var i=0; i<n; i+=2){ |
| 746 | @ var x1 = p.au[i]*railPitch + left; |
| 747 | @ var x0 = x1>p.x ? p.x+7 : p.x-6; |
| 748 | @ var u = rowinfo[p.au[i+1]-1]; |
| 749 | @ if(u.id<p.id){ |
| 750 | @ drawBox(lineColor,x0,p.y,x1,p.y+1); |
| 751 | @ drawUpArrow(x1, u.y+6, p.y); |
| 752 | @ }else{ |
| 753 | @ drawBox("#600000",x0,p.y,x1,p.y+1); |
| 754 | @ drawBox("#600000",x1-1,p.y,x1,u.y+1); |
| 755 | @ drawBox("#600000",x1,u.y,u.x-6,u.y+1); |
| 756 |
+76
-3
| --- src/winhttp.c | ||
| +++ src/winhttp.c | ||
| @@ -61,11 +61,11 @@ | ||
| 61 | 61 | } |
| 62 | 62 | |
| 63 | 63 | /* |
| 64 | 64 | ** Process a single incoming HTTP request. |
| 65 | 65 | */ |
| 66 | -static void win32_process_one_http_request(void *pAppData){ | |
| 66 | +static void win32_http_request(void *pAppData){ | |
| 67 | 67 | HttpRequest *p = (HttpRequest*)pAppData; |
| 68 | 68 | FILE *in = 0, *out = 0; |
| 69 | 69 | int amt, got; |
| 70 | 70 | int wanted = 0; |
| 71 | 71 | char *z; |
| @@ -128,10 +128,72 @@ | ||
| 128 | 128 | closesocket(p->s); |
| 129 | 129 | file_delete(zRequestFName); |
| 130 | 130 | file_delete(zReplyFName); |
| 131 | 131 | free(p); |
| 132 | 132 | } |
| 133 | + | |
| 134 | +/* | |
| 135 | +** Process a single incoming SCGI request. | |
| 136 | +*/ | |
| 137 | +static void win32_scgi_request(void *pAppData){ | |
| 138 | + HttpRequest *p = (HttpRequest*)pAppData; | |
| 139 | + FILE *in = 0, *out = 0; | |
| 140 | + int amt, got, nHdr, i; | |
| 141 | + int wanted = 0; | |
| 142 | + char zRequestFName[MAX_PATH]; | |
| 143 | + char zReplyFName[MAX_PATH]; | |
| 144 | + char zCmd[2000]; /* Command-line to process the request */ | |
| 145 | + char zHdr[2000]; /* The SCGI request header */ | |
| 146 | + | |
| 147 | + sqlite3_snprintf(MAX_PATH, zRequestFName, | |
| 148 | + "%s_in%d.txt", zTempPrefix, p->id); | |
| 149 | + sqlite3_snprintf(MAX_PATH, zReplyFName, | |
| 150 | + "%s_out%d.txt", zTempPrefix, p->id); | |
| 151 | + out = fossil_fopen(zRequestFName, "wb"); | |
| 152 | + if( out==0 ) goto end_request; | |
| 153 | + amt = 0; | |
| 154 | + got = recv(p->s, zHdr, sizeof(zHdr), 0); | |
| 155 | + if( got==SOCKET_ERROR ) goto end_request; | |
| 156 | + amt = fwrite(zHdr, 1, got, out); | |
| 157 | + nHdr = 0; | |
| 158 | + for(i=0; zHdr[i]>='0' && zHdr[i]<='9'; i++){ | |
| 159 | + nHdr = 10*nHdr + zHdr[i] - '0'; | |
| 160 | + } | |
| 161 | + wanted = nHdr + i + 1; | |
| 162 | + if( strcmp(zHdr+i+1, "CONTENT_LENGTH")==0 ){ | |
| 163 | + wanted += atoi(zHdr+i+15); | |
| 164 | + } | |
| 165 | + while( wanted>amt ){ | |
| 166 | + got = recv(p->s, zHdr, wanted<sizeof(zHdr) ? wanted : sizeof(zHdr), 0); | |
| 167 | + if( got<=0 ) break; | |
| 168 | + fwrite(zHdr, 1, got, out); | |
| 169 | + wanted += got; | |
| 170 | + } | |
| 171 | + fclose(out); | |
| 172 | + out = 0; | |
| 173 | + sqlite3_snprintf(sizeof(zCmd), zCmd, | |
| 174 | + "\"%s\" http \"%s\" %s %s %s --scgi --nossl%s", | |
| 175 | + g.nameOfExe, g.zRepositoryName, zRequestFName, zReplyFName, | |
| 176 | + inet_ntoa(p->addr.sin_addr), p->zOptions | |
| 177 | + ); | |
| 178 | + fossil_system(zCmd); | |
| 179 | + in = fossil_fopen(zReplyFName, "rb"); | |
| 180 | + if( in ){ | |
| 181 | + while( (got = fread(zHdr, 1, sizeof(zHdr), in))>0 ){ | |
| 182 | + send(p->s, zHdr, got, 0); | |
| 183 | + } | |
| 184 | + } | |
| 185 | + | |
| 186 | +end_request: | |
| 187 | + if( out ) fclose(out); | |
| 188 | + if( in ) fclose(in); | |
| 189 | + closesocket(p->s); | |
| 190 | + file_delete(zRequestFName); | |
| 191 | + file_delete(zReplyFName); | |
| 192 | + free(p); | |
| 193 | +} | |
| 194 | + | |
| 133 | 195 | |
| 134 | 196 | /* |
| 135 | 197 | ** Start a listening socket and process incoming HTTP requests on |
| 136 | 198 | ** that socket. |
| 137 | 199 | */ |
| @@ -206,11 +268,12 @@ | ||
| 206 | 268 | if( !GetTempPathW(MAX_PATH, zTmpPath) ){ |
| 207 | 269 | fossil_fatal("unable to get path to the temporary directory."); |
| 208 | 270 | } |
| 209 | 271 | zTempPrefix = mprintf("%sfossil_server_P%d_", |
| 210 | 272 | fossil_unicode_to_utf8(zTmpPath), iPort); |
| 211 | - fossil_print("Listening for HTTP requests on TCP port %d\n", iPort); | |
| 273 | + fossil_print("Listening for %s requests on TCP port %d\n", | |
| 274 | + (flags&HTTP_SERVER_SCGI)!=0?"SCGI":"HTTP", iPort); | |
| 212 | 275 | if( zBrowser ){ |
| 213 | 276 | zBrowser = mprintf(zBrowser, iPort); |
| 214 | 277 | fossil_print("Launch webbrowser: %s\n", zBrowser); |
| 215 | 278 | fossil_system(zBrowser); |
| 216 | 279 | } |
| @@ -244,11 +307,15 @@ | ||
| 244 | 307 | p = fossil_malloc( sizeof(*p) ); |
| 245 | 308 | p->id = ++idCnt; |
| 246 | 309 | p->s = client; |
| 247 | 310 | p->addr = client_addr; |
| 248 | 311 | p->zOptions = blob_str(&options); |
| 249 | - _beginthread(win32_process_one_http_request, 0, (void*)p); | |
| 312 | + if( flags & HTTP_SERVER_SCGI ){ | |
| 313 | + _beginthread(win32_scgi_request, 0, (void*)p); | |
| 314 | + }else{ | |
| 315 | + _beginthread(win32_http_request, 0, (void*)p); | |
| 316 | + } | |
| 250 | 317 | } |
| 251 | 318 | closesocket(s); |
| 252 | 319 | WSACleanup(); |
| 253 | 320 | } |
| 254 | 321 | |
| @@ -539,10 +606,14 @@ | ||
| 539 | 606 | ** |
| 540 | 607 | ** Enables automatic login if the --localauth option is present |
| 541 | 608 | ** and the "localauth" setting is off and the connection is from |
| 542 | 609 | ** localhost. |
| 543 | 610 | ** |
| 611 | +** --scgi | |
| 612 | +** | |
| 613 | +** Create an SCGI server instead of an HTTP server | |
| 614 | +** | |
| 544 | 615 | ** |
| 545 | 616 | ** fossil winsrv delete ?SERVICE-NAME? |
| 546 | 617 | ** |
| 547 | 618 | ** Deletes a service. If the service is currently running, it will be |
| 548 | 619 | ** stopped first and then deleted. |
| @@ -592,10 +663,11 @@ | ||
| 592 | 663 | const char *zPort = find_option("port", "P", 1); |
| 593 | 664 | const char *zNotFound = find_option("notfound", 0, 1); |
| 594 | 665 | const char *zFileGlob = find_option("files", 0, 1); |
| 595 | 666 | const char *zLocalAuth = find_option("localauth", 0, 0); |
| 596 | 667 | const char *zRepository = find_option("repository", "R", 1); |
| 668 | + int useSCGI = find_option("scgi", 0, 0)!=0; | |
| 597 | 669 | Blob binPath; |
| 598 | 670 | |
| 599 | 671 | verify_all_options(); |
| 600 | 672 | if( g.argc==4 ){ |
| 601 | 673 | zSvcName = g.argv[3]; |
| @@ -632,10 +704,11 @@ | ||
| 632 | 704 | db_close(0); |
| 633 | 705 | /* Build the fully-qualified path to the service binary file. */ |
| 634 | 706 | blob_zero(&binPath); |
| 635 | 707 | blob_appendf(&binPath, "\"%s\" server", g.nameOfExe); |
| 636 | 708 | if( zPort ) blob_appendf(&binPath, " --port %s", zPort); |
| 709 | + if( useSCGI ) blob_appendf(&binPath, " --scgi"); | |
| 637 | 710 | if( zNotFound ) blob_appendf(&binPath, " --notfound \"%s\"", zNotFound); |
| 638 | 711 | if( zFileGlob ) blob_appendf(&binPath, " --files-urlenc %T", zFileGlob); |
| 639 | 712 | if( zLocalAuth ) blob_append(&binPath, " --localauth", -1); |
| 640 | 713 | blob_appendf(&binPath, " \"%s\"", g.zRepositoryName); |
| 641 | 714 | /* Create the service. */ |
| 642 | 715 |
| --- src/winhttp.c | |
| +++ src/winhttp.c | |
| @@ -61,11 +61,11 @@ | |
| 61 | } |
| 62 | |
| 63 | /* |
| 64 | ** Process a single incoming HTTP request. |
| 65 | */ |
| 66 | static void win32_process_one_http_request(void *pAppData){ |
| 67 | HttpRequest *p = (HttpRequest*)pAppData; |
| 68 | FILE *in = 0, *out = 0; |
| 69 | int amt, got; |
| 70 | int wanted = 0; |
| 71 | char *z; |
| @@ -128,10 +128,72 @@ | |
| 128 | closesocket(p->s); |
| 129 | file_delete(zRequestFName); |
| 130 | file_delete(zReplyFName); |
| 131 | free(p); |
| 132 | } |
| 133 | |
| 134 | /* |
| 135 | ** Start a listening socket and process incoming HTTP requests on |
| 136 | ** that socket. |
| 137 | */ |
| @@ -206,11 +268,12 @@ | |
| 206 | if( !GetTempPathW(MAX_PATH, zTmpPath) ){ |
| 207 | fossil_fatal("unable to get path to the temporary directory."); |
| 208 | } |
| 209 | zTempPrefix = mprintf("%sfossil_server_P%d_", |
| 210 | fossil_unicode_to_utf8(zTmpPath), iPort); |
| 211 | fossil_print("Listening for HTTP requests on TCP port %d\n", iPort); |
| 212 | if( zBrowser ){ |
| 213 | zBrowser = mprintf(zBrowser, iPort); |
| 214 | fossil_print("Launch webbrowser: %s\n", zBrowser); |
| 215 | fossil_system(zBrowser); |
| 216 | } |
| @@ -244,11 +307,15 @@ | |
| 244 | p = fossil_malloc( sizeof(*p) ); |
| 245 | p->id = ++idCnt; |
| 246 | p->s = client; |
| 247 | p->addr = client_addr; |
| 248 | p->zOptions = blob_str(&options); |
| 249 | _beginthread(win32_process_one_http_request, 0, (void*)p); |
| 250 | } |
| 251 | closesocket(s); |
| 252 | WSACleanup(); |
| 253 | } |
| 254 | |
| @@ -539,10 +606,14 @@ | |
| 539 | ** |
| 540 | ** Enables automatic login if the --localauth option is present |
| 541 | ** and the "localauth" setting is off and the connection is from |
| 542 | ** localhost. |
| 543 | ** |
| 544 | ** |
| 545 | ** fossil winsrv delete ?SERVICE-NAME? |
| 546 | ** |
| 547 | ** Deletes a service. If the service is currently running, it will be |
| 548 | ** stopped first and then deleted. |
| @@ -592,10 +663,11 @@ | |
| 592 | const char *zPort = find_option("port", "P", 1); |
| 593 | const char *zNotFound = find_option("notfound", 0, 1); |
| 594 | const char *zFileGlob = find_option("files", 0, 1); |
| 595 | const char *zLocalAuth = find_option("localauth", 0, 0); |
| 596 | const char *zRepository = find_option("repository", "R", 1); |
| 597 | Blob binPath; |
| 598 | |
| 599 | verify_all_options(); |
| 600 | if( g.argc==4 ){ |
| 601 | zSvcName = g.argv[3]; |
| @@ -632,10 +704,11 @@ | |
| 632 | db_close(0); |
| 633 | /* Build the fully-qualified path to the service binary file. */ |
| 634 | blob_zero(&binPath); |
| 635 | blob_appendf(&binPath, "\"%s\" server", g.nameOfExe); |
| 636 | if( zPort ) blob_appendf(&binPath, " --port %s", zPort); |
| 637 | if( zNotFound ) blob_appendf(&binPath, " --notfound \"%s\"", zNotFound); |
| 638 | if( zFileGlob ) blob_appendf(&binPath, " --files-urlenc %T", zFileGlob); |
| 639 | if( zLocalAuth ) blob_append(&binPath, " --localauth", -1); |
| 640 | blob_appendf(&binPath, " \"%s\"", g.zRepositoryName); |
| 641 | /* Create the service. */ |
| 642 |
| --- src/winhttp.c | |
| +++ src/winhttp.c | |
| @@ -61,11 +61,11 @@ | |
| 61 | } |
| 62 | |
| 63 | /* |
| 64 | ** Process a single incoming HTTP request. |
| 65 | */ |
| 66 | static void win32_http_request(void *pAppData){ |
| 67 | HttpRequest *p = (HttpRequest*)pAppData; |
| 68 | FILE *in = 0, *out = 0; |
| 69 | int amt, got; |
| 70 | int wanted = 0; |
| 71 | char *z; |
| @@ -128,10 +128,72 @@ | |
| 128 | closesocket(p->s); |
| 129 | file_delete(zRequestFName); |
| 130 | file_delete(zReplyFName); |
| 131 | free(p); |
| 132 | } |
| 133 | |
| 134 | /* |
| 135 | ** Process a single incoming SCGI request. |
| 136 | */ |
| 137 | static void win32_scgi_request(void *pAppData){ |
| 138 | HttpRequest *p = (HttpRequest*)pAppData; |
| 139 | FILE *in = 0, *out = 0; |
| 140 | int amt, got, nHdr, i; |
| 141 | int wanted = 0; |
| 142 | char zRequestFName[MAX_PATH]; |
| 143 | char zReplyFName[MAX_PATH]; |
| 144 | char zCmd[2000]; /* Command-line to process the request */ |
| 145 | char zHdr[2000]; /* The SCGI request header */ |
| 146 | |
| 147 | sqlite3_snprintf(MAX_PATH, zRequestFName, |
| 148 | "%s_in%d.txt", zTempPrefix, p->id); |
| 149 | sqlite3_snprintf(MAX_PATH, zReplyFName, |
| 150 | "%s_out%d.txt", zTempPrefix, p->id); |
| 151 | out = fossil_fopen(zRequestFName, "wb"); |
| 152 | if( out==0 ) goto end_request; |
| 153 | amt = 0; |
| 154 | got = recv(p->s, zHdr, sizeof(zHdr), 0); |
| 155 | if( got==SOCKET_ERROR ) goto end_request; |
| 156 | amt = fwrite(zHdr, 1, got, out); |
| 157 | nHdr = 0; |
| 158 | for(i=0; zHdr[i]>='0' && zHdr[i]<='9'; i++){ |
| 159 | nHdr = 10*nHdr + zHdr[i] - '0'; |
| 160 | } |
| 161 | wanted = nHdr + i + 1; |
| 162 | if( strcmp(zHdr+i+1, "CONTENT_LENGTH")==0 ){ |
| 163 | wanted += atoi(zHdr+i+15); |
| 164 | } |
| 165 | while( wanted>amt ){ |
| 166 | got = recv(p->s, zHdr, wanted<sizeof(zHdr) ? wanted : sizeof(zHdr), 0); |
| 167 | if( got<=0 ) break; |
| 168 | fwrite(zHdr, 1, got, out); |
| 169 | wanted += got; |
| 170 | } |
| 171 | fclose(out); |
| 172 | out = 0; |
| 173 | sqlite3_snprintf(sizeof(zCmd), zCmd, |
| 174 | "\"%s\" http \"%s\" %s %s %s --scgi --nossl%s", |
| 175 | g.nameOfExe, g.zRepositoryName, zRequestFName, zReplyFName, |
| 176 | inet_ntoa(p->addr.sin_addr), p->zOptions |
| 177 | ); |
| 178 | fossil_system(zCmd); |
| 179 | in = fossil_fopen(zReplyFName, "rb"); |
| 180 | if( in ){ |
| 181 | while( (got = fread(zHdr, 1, sizeof(zHdr), in))>0 ){ |
| 182 | send(p->s, zHdr, got, 0); |
| 183 | } |
| 184 | } |
| 185 | |
| 186 | end_request: |
| 187 | if( out ) fclose(out); |
| 188 | if( in ) fclose(in); |
| 189 | closesocket(p->s); |
| 190 | file_delete(zRequestFName); |
| 191 | file_delete(zReplyFName); |
| 192 | free(p); |
| 193 | } |
| 194 | |
| 195 | |
| 196 | /* |
| 197 | ** Start a listening socket and process incoming HTTP requests on |
| 198 | ** that socket. |
| 199 | */ |
| @@ -206,11 +268,12 @@ | |
| 268 | if( !GetTempPathW(MAX_PATH, zTmpPath) ){ |
| 269 | fossil_fatal("unable to get path to the temporary directory."); |
| 270 | } |
| 271 | zTempPrefix = mprintf("%sfossil_server_P%d_", |
| 272 | fossil_unicode_to_utf8(zTmpPath), iPort); |
| 273 | fossil_print("Listening for %s requests on TCP port %d\n", |
| 274 | (flags&HTTP_SERVER_SCGI)!=0?"SCGI":"HTTP", iPort); |
| 275 | if( zBrowser ){ |
| 276 | zBrowser = mprintf(zBrowser, iPort); |
| 277 | fossil_print("Launch webbrowser: %s\n", zBrowser); |
| 278 | fossil_system(zBrowser); |
| 279 | } |
| @@ -244,11 +307,15 @@ | |
| 307 | p = fossil_malloc( sizeof(*p) ); |
| 308 | p->id = ++idCnt; |
| 309 | p->s = client; |
| 310 | p->addr = client_addr; |
| 311 | p->zOptions = blob_str(&options); |
| 312 | if( flags & HTTP_SERVER_SCGI ){ |
| 313 | _beginthread(win32_scgi_request, 0, (void*)p); |
| 314 | }else{ |
| 315 | _beginthread(win32_http_request, 0, (void*)p); |
| 316 | } |
| 317 | } |
| 318 | closesocket(s); |
| 319 | WSACleanup(); |
| 320 | } |
| 321 | |
| @@ -539,10 +606,14 @@ | |
| 606 | ** |
| 607 | ** Enables automatic login if the --localauth option is present |
| 608 | ** and the "localauth" setting is off and the connection is from |
| 609 | ** localhost. |
| 610 | ** |
| 611 | ** --scgi |
| 612 | ** |
| 613 | ** Create an SCGI server instead of an HTTP server |
| 614 | ** |
| 615 | ** |
| 616 | ** fossil winsrv delete ?SERVICE-NAME? |
| 617 | ** |
| 618 | ** Deletes a service. If the service is currently running, it will be |
| 619 | ** stopped first and then deleted. |
| @@ -592,10 +663,11 @@ | |
| 663 | const char *zPort = find_option("port", "P", 1); |
| 664 | const char *zNotFound = find_option("notfound", 0, 1); |
| 665 | const char *zFileGlob = find_option("files", 0, 1); |
| 666 | const char *zLocalAuth = find_option("localauth", 0, 0); |
| 667 | const char *zRepository = find_option("repository", "R", 1); |
| 668 | int useSCGI = find_option("scgi", 0, 0)!=0; |
| 669 | Blob binPath; |
| 670 | |
| 671 | verify_all_options(); |
| 672 | if( g.argc==4 ){ |
| 673 | zSvcName = g.argv[3]; |
| @@ -632,10 +704,11 @@ | |
| 704 | db_close(0); |
| 705 | /* Build the fully-qualified path to the service binary file. */ |
| 706 | blob_zero(&binPath); |
| 707 | blob_appendf(&binPath, "\"%s\" server", g.nameOfExe); |
| 708 | if( zPort ) blob_appendf(&binPath, " --port %s", zPort); |
| 709 | if( useSCGI ) blob_appendf(&binPath, " --scgi"); |
| 710 | if( zNotFound ) blob_appendf(&binPath, " --notfound \"%s\"", zNotFound); |
| 711 | if( zFileGlob ) blob_appendf(&binPath, " --files-urlenc %T", zFileGlob); |
| 712 | if( zLocalAuth ) blob_append(&binPath, " --localauth", -1); |
| 713 | blob_appendf(&binPath, " \"%s\"", g.zRepositoryName); |
| 714 | /* Create the service. */ |
| 715 |
+1
-1
| --- www/adding_code.wiki | ||
| +++ www/adding_code.wiki | ||
| @@ -156,11 +156,11 @@ | ||
| 156 | 156 | |
| 157 | 157 | Once you have the command running, you can then start adding code to |
| 158 | 158 | make it do useful things. There are lots of utility functions in |
| 159 | 159 | Fossil for parsing command-line options and for |
| 160 | 160 | opening and accessing and manipulating the repository and |
| 161 | -the working check-out. Study at the implementations of existing commands | |
| 161 | +the working check-out. Study implementations of existing commands | |
| 162 | 162 | to get an idea of how things are done. You can easily find the implementations |
| 163 | 163 | of existing commands by searching for "COMMAND: <i>name</i>" in the |
| 164 | 164 | files of the "src/" directory. |
| 165 | 165 | |
| 166 | 166 | <h2>5.0 Creating A New Web Page</h2> |
| 167 | 167 |
| --- www/adding_code.wiki | |
| +++ www/adding_code.wiki | |
| @@ -156,11 +156,11 @@ | |
| 156 | |
| 157 | Once you have the command running, you can then start adding code to |
| 158 | make it do useful things. There are lots of utility functions in |
| 159 | Fossil for parsing command-line options and for |
| 160 | opening and accessing and manipulating the repository and |
| 161 | the working check-out. Study at the implementations of existing commands |
| 162 | to get an idea of how things are done. You can easily find the implementations |
| 163 | of existing commands by searching for "COMMAND: <i>name</i>" in the |
| 164 | files of the "src/" directory. |
| 165 | |
| 166 | <h2>5.0 Creating A New Web Page</h2> |
| 167 |
| --- www/adding_code.wiki | |
| +++ www/adding_code.wiki | |
| @@ -156,11 +156,11 @@ | |
| 156 | |
| 157 | Once you have the command running, you can then start adding code to |
| 158 | make it do useful things. There are lots of utility functions in |
| 159 | Fossil for parsing command-line options and for |
| 160 | opening and accessing and manipulating the repository and |
| 161 | the working check-out. Study implementations of existing commands |
| 162 | to get an idea of how things are done. You can easily find the implementations |
| 163 | of existing commands by searching for "COMMAND: <i>name</i>" in the |
| 164 | files of the "src/" directory. |
| 165 | |
| 166 | <h2>5.0 Creating A New Web Page</h2> |
| 167 |
+2
-2
| --- www/embeddeddoc.wiki | ||
| +++ www/embeddeddoc.wiki | ||
| @@ -103,12 +103,12 @@ | ||
| 103 | 103 | <blockquote><pre> |
| 104 | 104 | #!/usr/bin/fossil |
| 105 | 105 | repository: /fossil/fossil.fossil |
| 106 | 106 | </pre></blockquote> |
| 107 | 107 | |
| 108 | -This is one of three ways to set up a | |
| 109 | -<a href="quickstart.wiki#serversetup">fossil web server</a>. | |
| 108 | +This is one of four ways to set up a | |
| 109 | +<a href="./server.wiki">fossil web server</a>. | |
| 110 | 110 | |
| 111 | 111 | The "<b>/trunk/</b>" part of the URL tells fossil to use |
| 112 | 112 | the documentation files from the most recent trunk check-in. |
| 113 | 113 | If you wanted to see an historical version of this document, |
| 114 | 114 | you could substitute the name of a check-in for "<b>/trunk/</b>". |
| 115 | 115 |
| --- www/embeddeddoc.wiki | |
| +++ www/embeddeddoc.wiki | |
| @@ -103,12 +103,12 @@ | |
| 103 | <blockquote><pre> |
| 104 | #!/usr/bin/fossil |
| 105 | repository: /fossil/fossil.fossil |
| 106 | </pre></blockquote> |
| 107 | |
| 108 | This is one of three ways to set up a |
| 109 | <a href="quickstart.wiki#serversetup">fossil web server</a>. |
| 110 | |
| 111 | The "<b>/trunk/</b>" part of the URL tells fossil to use |
| 112 | the documentation files from the most recent trunk check-in. |
| 113 | If you wanted to see an historical version of this document, |
| 114 | you could substitute the name of a check-in for "<b>/trunk/</b>". |
| 115 |
| --- www/embeddeddoc.wiki | |
| +++ www/embeddeddoc.wiki | |
| @@ -103,12 +103,12 @@ | |
| 103 | <blockquote><pre> |
| 104 | #!/usr/bin/fossil |
| 105 | repository: /fossil/fossil.fossil |
| 106 | </pre></blockquote> |
| 107 | |
| 108 | This is one of four ways to set up a |
| 109 | <a href="./server.wiki">fossil web server</a>. |
| 110 | |
| 111 | The "<b>/trunk/</b>" part of the URL tells fossil to use |
| 112 | the documentation files from the most recent trunk check-in. |
| 113 | If you wanted to see an historical version of this document, |
| 114 | you could substitute the name of a check-in for "<b>/trunk/</b>". |
| 115 |
+127
-84
| --- www/fileformat.wiki | ||
| +++ www/fileformat.wiki | ||
| @@ -46,11 +46,11 @@ | ||
| 46 | 46 | <li> [#tktchng | Ticket Changes] </li> |
| 47 | 47 | <li> [#attachment | Attachments] </li> |
| 48 | 48 | <li> [#event | Events] </li> |
| 49 | 49 | </ul> |
| 50 | 50 | |
| 51 | -These seven artifact types are described in the sequel. | |
| 51 | +These seven artifact types are described in the following sections. | |
| 52 | 52 | |
| 53 | 53 | In the current implementation (as of 2009-01-25) the artifacts that |
| 54 | 54 | make up a fossil repository are stored in in as delta- and zlib-compressed |
| 55 | 55 | blobs in an <a href="http://www.sqlite.org/">SQLite</a> database. This |
| 56 | 56 | is an implementation detail and might change in a future release. For |
| @@ -98,16 +98,16 @@ | ||
| 98 | 98 | |
| 99 | 99 | <blockquote> |
| 100 | 100 | <b>B</b> <i>baseline-manifest</i><br> |
| 101 | 101 | <b>C</b> <i>checkin-comment</i><br> |
| 102 | 102 | <b>D</b> <i>time-and-date-stamp</i><br> |
| 103 | -<b>F</b> <i>filename</i> <i>SHA1-hash</i> <i>permissions</i> <i>old-name</i><br> | |
| 103 | +<b>F</b> <i>filename</i> ?<i>SHA1-hash</i>? ?<i>permissions</i>? ?<i>old-name</i>?<br> | |
| 104 | 104 | <b>N</b> <i>mimetype</i><br> |
| 105 | 105 | <b>P</b> <i>SHA1-hash</i>+<br> |
| 106 | -<b>Q</b> (<b>+</b>|<b>-</b>)<i>SHA1-hash ?SHA1-hash?</i><br> | |
| 106 | +<b>Q</b> (<b>+</b>|<b>-</b>)<i>SHA1-hash</i> ?<i>SHA1-hash</i>?<br> | |
| 107 | 107 | <b>R</b> <i>repository-checksum</i><br> |
| 108 | -<b>T</b> (<b>+</b>|<b>-</b>|<b>*</b>)<i>tag-name <b>*</b> ?value?</i><br> | |
| 108 | +<b>T</b> (<b>+</b>|<b>-</b>|<b>*</b>)<i>tag-name</i> <b>*</b> ?<i>value</i>?<br> | |
| 109 | 109 | <b>U</b> <i>user-login</i><br> |
| 110 | 110 | <b>Z</b> <i>manifest-checksum</i> |
| 111 | 111 | </blockquote> |
| 112 | 112 | |
| 113 | 113 | A manifest may optionally have a single B-card. The B-card specifies |
| @@ -291,11 +291,11 @@ | ||
| 291 | 291 | |
| 292 | 292 | Allowed cards in a control artifact are as follows: |
| 293 | 293 | |
| 294 | 294 | <blockquote> |
| 295 | 295 | <b>D</b> <i>time-and-date-stamp</i><br /> |
| 296 | -<b>T</b> (<b>+</b>|<b>-</b>|<b>*</b>)<i>tag-name artifact-id ?value?</i><br /> | |
| 296 | +<b>T</b> (<b>+</b>|<b>-</b>|<b>*</b>)<i>tag-name</i> <i>artifact-id</i> ?<i>value</i>?<br /> | |
| 297 | 297 | <b>U</b> <i>user-name</i><br /> |
| 298 | 298 | <b>Z</b> <i>checksum</i><br /> |
| 299 | 299 | </blockquote> |
| 300 | 300 | |
| 301 | 301 | A control artifact must have one D card, one U card, one Z card and |
| @@ -475,11 +475,11 @@ | ||
| 475 | 475 | <b>C</b> <i>comment</i><br> |
| 476 | 476 | <b>D</b> <i>time-and-date-stamp</i><br /> |
| 477 | 477 | <b>E</b> <i>event-time</i> <i>event-id</i><br /> |
| 478 | 478 | <b>N</b> <i>mimetype</i><br /> |
| 479 | 479 | <b>P</b> <i>parent-artifact-id</i>+<br /> |
| 480 | -<b>T</b> <b>+</b><i>tag-name</i> <b>*</b> <i>value</i><br /> | |
| 480 | +<b>T</b> <b>+</b><i>tag-name</i> <b>*</b> ?<i>value</i>?<br /> | |
| 481 | 481 | <b>U</b> <i>user-name</i><br /> |
| 482 | 482 | <b>W</b> <i>size</i> <b>\n</b> <i>text</i> <b>\n</b><br /> |
| 483 | 483 | <b>Z</b> <i>checksum</i> |
| 484 | 484 | </blockquote> |
| 485 | 485 | |
| @@ -499,11 +499,11 @@ | ||
| 499 | 499 | The optional N card specifies the mimetype of the text of the event |
| 500 | 500 | that is contained in the W card. If the N card is omitted, then the |
| 501 | 501 | W card text mimetype is assumed to be text/x-fossil, which is the |
| 502 | 502 | Fossil wiki format. |
| 503 | 503 | |
| 504 | -The option P card specifies a prior event with the same event-id from | |
| 504 | +The optional P card specifies a prior event with the same event-id from | |
| 505 | 505 | which the current event is an edit. The P card is a hint to the system |
| 506 | 506 | that it might be space efficient to store one event as a delta of the |
| 507 | 507 | other. |
| 508 | 508 | |
| 509 | 509 | An event might contain one or more T-cards used to set |
| @@ -531,12 +531,16 @@ | ||
| 531 | 531 | |
| 532 | 532 | |
| 533 | 533 | <a name="summary"></a> |
| 534 | 534 | <h2>8.0 Card Summary</h2> |
| 535 | 535 | |
| 536 | -The following table summaries the various kinds of cards that | |
| 537 | -appear on Fossil artifacts: | |
| 536 | +The following table summarizes the various kinds of cards that appear | |
| 537 | +on Fossil artifacts. A blank entry means that combination of card and | |
| 538 | +artifact is not legal. A number or range of numbers indicates the number | |
| 539 | +of times a card may (or must) appear in the corresponding artifact type. | |
| 540 | +e.g. a value of 1 indicates a required unique card and 1+ indicates that one | |
| 541 | +or more such cards are required. | |
| 538 | 542 | |
| 539 | 543 | <table border=1 width="100%"> |
| 540 | 544 | <tr> |
| 541 | 545 | <th rowspan=2 valign=bottom>Card Format</th> |
| 542 | 546 | <th colspan=7>Used By</th> |
| @@ -549,184 +553,223 @@ | ||
| 549 | 553 | <th>Ticket</th> |
| 550 | 554 | <th>Attachment</th> |
| 551 | 555 | <th>Event</th> |
| 552 | 556 | </tr> |
| 553 | 557 | <tr> |
| 554 | -<td><b>A</b> <i>filename target source</i></td> | |
| 555 | -<td> </td> | |
| 556 | -<td> </td> | |
| 557 | -<td> </td> | |
| 558 | -<td> </td> | |
| 559 | -<td> </td> | |
| 560 | -<td align=center><b>X</b></td> | |
| 561 | -<td> </td> | |
| 562 | -</tr> | |
| 563 | -<tr> | |
| 564 | -<td><b>B</b> <i>baseline</i></td> | |
| 565 | -<td align=center><b>X</b></td> | |
| 566 | -<td> </td> | |
| 567 | -<td> </td> | |
| 568 | -<td> </td> | |
| 569 | -<td> </td> | |
| 570 | -<td> </td> | |
| 571 | -<td> </td> | |
| 572 | -</tr> | |
| 573 | -<tr> | |
| 574 | -<td><b>C</b> <i>comment-text</i></td> | |
| 575 | -<td align=center><b>X</b></td> | |
| 576 | -<td> </td> | |
| 577 | -<td> </td> | |
| 578 | -<td> </td> | |
| 579 | -<td> </td> | |
| 580 | -<td align=center><b>X</b></td> | |
| 581 | -<td align=center><b>X</b></td> | |
| 582 | -</tr> | |
| 583 | -<tr> | |
| 584 | -<td><b>D</b> <i>date-time-stamp</i></td> | |
| 585 | -<td align=center><b>X</b></td> | |
| 586 | -<td align=center> </td> | |
| 587 | -<td align=center><b>X</b></td> | |
| 588 | -<td align=center><b>X</b></td> | |
| 589 | -<td align=center><b>X</b></td> | |
| 590 | -<td align=center><b>X</b></td> | |
| 591 | -<td align=center><b>X</b></td> | |
| 558 | +<td><b>A</b> <i>filename</i> <i>target</i> ?<i>source</i>?</td> | |
| 559 | +<td> </td> | |
| 560 | +<td> </td> | |
| 561 | +<td> </td> | |
| 562 | +<td> </td> | |
| 563 | +<td> </td> | |
| 564 | +<td align=center><b>1</b></td> | |
| 565 | +<td> </td> | |
| 566 | +</tr> | |
| 567 | +<tr> | |
| 568 | +<td><b>B</b> <i>baseline</i></td> | |
| 569 | +<td align=center><b>0-1*</b></td> | |
| 570 | +<td> </td> | |
| 571 | +<td> </td> | |
| 572 | +<td> </td> | |
| 573 | +<td> </td> | |
| 574 | +<td> </td> | |
| 575 | +<td> </td> | |
| 576 | +</tr> | |
| 577 | +<tr><td> </td><td colspan='7'>* = Required for delta manifests</td></tr> | |
| 578 | +<tr> | |
| 579 | +<td><b>C</b> <i>comment-text</i></td> | |
| 580 | +<td align=center><b>1</b></td> | |
| 581 | +<td> </td> | |
| 582 | +<td> </td> | |
| 583 | +<td> </td> | |
| 584 | +<td> </td> | |
| 585 | +<td align=center><b>0-1</b></td> | |
| 586 | +<td align=center><b>1</b></td> | |
| 587 | +</tr> | |
| 588 | +<tr> | |
| 589 | +<td><b>D</b> <i>date-time-stamp</i></td> | |
| 590 | +<td align=center><b>1</b></td> | |
| 591 | +<td align=center> </td> | |
| 592 | +<td align=center><b>1</b></td> | |
| 593 | +<td align=center><b>1</b></td> | |
| 594 | +<td align=center><b>1</b></td> | |
| 595 | +<td align=center><b>1</b></td> | |
| 596 | +<td align=center><b>1</b></td> | |
| 592 | 597 | </tr> |
| 593 | 598 | <tr> |
| 594 | 599 | <td><b>E</b> <i>event-time event-id</i></td> |
| 595 | 600 | <td align=center> </td> |
| 596 | 601 | <td align=center> </td> |
| 597 | 602 | <td align=center> </td> |
| 598 | 603 | <td align=center> </td> |
| 599 | 604 | <td align=center> </td> |
| 600 | 605 | <td align=center> </td> |
| 601 | -<td align=center><b>X</b></td> | |
| 606 | +<td align=center><b>1</b></td> | |
| 602 | 607 | </tr> |
| 603 | 608 | <tr> |
| 604 | -<td><b>F</b> <i>filename uuid permissions oldname</i></td> | |
| 605 | -<td align=center><b>X</b></td> | |
| 609 | +<td><b>F</b> <i>filename</i> ?<i>uuid</i>? ?<i>permissions</i>? ?<i>oldname</i>?</td> | |
| 610 | +<td align=center><b>0+</b></td> | |
| 606 | 611 | <td align=center> </td> |
| 607 | 612 | <td align=center> </td> |
| 608 | 613 | <td align=center> </td> |
| 609 | 614 | <td align=center> </td> |
| 610 | 615 | <td align=center> </td> |
| 611 | 616 | <td align=center> </td> |
| 612 | 617 | </tr> |
| 613 | 618 | <tr> |
| 614 | -<td><b>J</b> <i>name value</i></td> | |
| 619 | +<td><b>J</b> <i>name</i> ?<i>value</i>?</td> | |
| 615 | 620 | <td align=center> </td> |
| 616 | 621 | <td align=center> </td> |
| 617 | 622 | <td align=center> </td> |
| 618 | 623 | <td align=center> </td> |
| 619 | -<td align=center><b>X</b></td> | |
| 624 | +<td align=center><b>1+</b></td> | |
| 620 | 625 | <td align=center> </td> |
| 621 | 626 | <td align=center> </td> |
| 622 | 627 | </tr> |
| 623 | 628 | <tr> |
| 624 | 629 | <td><b>K</b> <i>ticket-uuid</i></td> |
| 625 | 630 | <td align=center> </td> |
| 626 | 631 | <td align=center> </td> |
| 627 | 632 | <td align=center> </td> |
| 628 | 633 | <td align=center> </td> |
| 629 | -<td align=center><b>X</b></td> | |
| 634 | +<td align=center><b>1</b></td> | |
| 630 | 635 | <td align=center> </td> |
| 631 | 636 | <td align=center> </td> |
| 632 | 637 | </tr> |
| 633 | 638 | <tr> |
| 634 | 639 | <td><b>L</b> <i>wiki-title</i></td> |
| 635 | 640 | <td align=center> </td> |
| 636 | 641 | <td align=center> </td> |
| 637 | 642 | <td align=center> </td> |
| 638 | -<td align=center><b>X</b></td> | |
| 643 | +<td align=center><b>1</b></td> | |
| 639 | 644 | <td align=center> </td> |
| 640 | 645 | <td align=center> </td> |
| 641 | 646 | <td align=center> </td> |
| 642 | 647 | </tr> |
| 643 | 648 | <tr> |
| 644 | 649 | <td><b>M</b> <i>uuid</i></td> |
| 645 | 650 | <td align=center> </td> |
| 646 | -<td align=center><b>X</b></td> | |
| 651 | +<td align=center><b>1+</b></td> | |
| 647 | 652 | <td align=center> </td> |
| 648 | 653 | <td align=center> </td> |
| 649 | 654 | <td align=center> </td> |
| 650 | 655 | <td align=center> </td> |
| 651 | 656 | <td align=center> </td> |
| 652 | 657 | </tr> |
| 653 | 658 | <tr> |
| 654 | 659 | <td><b>N</b> <i>mimetype</i></td> |
| 655 | -<td align=center><b>X</b></td> | |
| 660 | +<td align=center><b>0-1</b></td> | |
| 656 | 661 | <td align=center> </td> |
| 657 | 662 | <td align=center> </td> |
| 658 | -<td align=center><b>X</b></td> | |
| 663 | +<td align=center><b>0-1</b></td> | |
| 659 | 664 | <td align=center> </td> |
| 660 | -<td align=center><b>X</b></td> | |
| 661 | -<td align=center><b>X</b></td> | |
| 665 | +<td align=center><b>0-1</b></td> | |
| 666 | +<td align=center><b>0-1</b></td> | |
| 662 | 667 | </tr> |
| 663 | 668 | <tr> |
| 664 | 669 | <td><b>P</b> <i>uuid ...</i></td> |
| 665 | -<td align=center><b>X</b></td> | |
| 670 | +<td align=center><b>0-1</b></td> | |
| 666 | 671 | <td align=center> </td> |
| 667 | 672 | <td align=center> </td> |
| 668 | -<td align=center><b>X</b></td> | |
| 673 | +<td align=center><b>0-1</b></td> | |
| 669 | 674 | <td align=center> </td> |
| 670 | 675 | <td align=center> </td> |
| 671 | -<td align=center> </td> | |
| 676 | +<td align=center><b>0-1</b></td> | |
| 672 | 677 | </tr> |
| 673 | 678 | <tr> |
| 674 | -<td><b>Q</b> (<b>+</b>|<b>-</b>)<i>uuid uuid</i></td> | |
| 675 | -<td align=center><b>X</b></td> | |
| 679 | +<td><b>Q</b> (<b>+</b>|<b>-</b>)<i>uuid</i> ?<i>uuid</i>?</td> | |
| 680 | +<td align=center><b>0+</b></td> | |
| 676 | 681 | <td align=center> </td> |
| 677 | 682 | <td align=center> </td> |
| 678 | 683 | <td align=center> </td> |
| 679 | 684 | <td align=center> </td> |
| 680 | 685 | <td align=center> </td> |
| 681 | 686 | <td align=center> </td> |
| 682 | 687 | </tr> |
| 683 | 688 | <tr> |
| 684 | 689 | <td><b>R</b> <i>md5sum</i></td> |
| 685 | -<td align=center><b>X</b></td> | |
| 690 | +<td align=center><b>0-1*</b></td> | |
| 686 | 691 | <td align=center> </td> |
| 687 | 692 | <td align=center> </td> |
| 688 | 693 | <td align=center> </td> |
| 689 | 694 | <td align=center> </td> |
| 690 | 695 | <td align=center> </td> |
| 691 | 696 | <td align=center> </td> |
| 697 | +<tr><td> </td><td colspan='7'>* = Required when using F cards</td></tr> | |
| 692 | 698 | <tr> |
| 693 | -<td><b>T</b> (<b>+</b>|<b>*</b>|<b>-</b>)<i>tagname uuid value</i></td> | |
| 694 | -<td align=center><b>X</b></td> | |
| 699 | +<td><b>T</b> (<b>+</b>|<b>*</b>|<b>-</b>)<i>tagname</i> <i>uuid</i> ?<i>value</i>?</td> | |
| 700 | +<td align=center><b>0+</b></td> | |
| 695 | 701 | <td align=center> </td> |
| 696 | -<td align=center><b>X</b></td> | |
| 702 | +<td align=center><b>1+</b></td> | |
| 697 | 703 | <td align=center> </td> |
| 698 | 704 | <td align=center> </td> |
| 699 | 705 | <td align=center> </td> |
| 700 | -<td align=center><b>X</b></td> | |
| 706 | +<td align=center><b>0+</b></td> | |
| 701 | 707 | </tr> |
| 702 | 708 | <tr> |
| 703 | 709 | <td><b>U</b> <i>username</i></td> |
| 704 | -<td align=center><b>X</b></td> | |
| 710 | +<td align=center><b>1</b></td> | |
| 705 | 711 | <td align=center> </td> |
| 706 | -<td align=center><b>X</b></td> | |
| 707 | -<td align=center><b>X</b></td> | |
| 708 | -<td align=center><b>X</b></td> | |
| 709 | -<td align=center><b>X</b></td> | |
| 710 | -<td align=center><b>X</b></td> | |
| 712 | +<td align=center><b>1</b></td> | |
| 713 | +<td align=center><b>1</b></td> | |
| 714 | +<td align=center><b>1</b></td> | |
| 715 | +<td align=center><b>0-1</b></td> | |
| 716 | +<td align=center><b>0-1</b></td> | |
| 711 | 717 | </tr> |
| 712 | 718 | <tr> |
| 713 | 719 | <td><b>W</b> <i>size</i></td> |
| 714 | 720 | <td align=center> </td> |
| 715 | 721 | <td align=center> </td> |
| 716 | 722 | <td align=center> </td> |
| 717 | -<td align=center><b>X</b></td> | |
| 723 | +<td align=center><b>1</b></td> | |
| 718 | 724 | <td align=center> </td> |
| 719 | 725 | <td align=center> </td> |
| 720 | -<td align=center><b>X</b></td> | |
| 726 | +<td align=center><b>1</b></td> | |
| 721 | 727 | </tr> |
| 722 | 728 | <tr> |
| 723 | 729 | <td><b>Z</b> <i>md5sum</i></td> |
| 724 | -<td align=center><b>X</b></td> | |
| 725 | -<td align=center><b>X</b></td> | |
| 726 | -<td align=center><b>X</b></td> | |
| 727 | -<td align=center><b>X</b></td> | |
| 728 | -<td align=center><b>X</b></td> | |
| 729 | -<td align=center><b>X</b></td> | |
| 730 | -<td align=center><b>X</b></td> | |
| 730 | +<td align=center><b>1</b></td> | |
| 731 | +<td align=center><b>1</b></td> | |
| 732 | +<td align=center><b>1</b></td> | |
| 733 | +<td align=center><b>1</b></td> | |
| 734 | +<td align=center><b>1</b></td> | |
| 735 | +<td align=center><b>1</b></td> | |
| 736 | +<td align=center><b>1</b></td> | |
| 731 | 737 | </tr> |
| 732 | 738 | </table> |
| 739 | + | |
| 740 | + | |
| 741 | +<a name="addenda"></a> | |
| 742 | +<h2>9.0 Addenda</h2> | |
| 743 | + | |
| 744 | +This section contains additional information which may be useful when | |
| 745 | +implementing algorithms described above. | |
| 746 | + | |
| 747 | +<h3>R Card Hash Calculation</h3> | |
| 748 | + | |
| 749 | +Given a manifest file named <tt>MF</tt>, the following Bash shell code | |
| 750 | +demonstrates how to compute the value of the R card in that manifest. | |
| 751 | +This example uses manifest [28987096ac]. Lines starting with <tt>#</tt> are | |
| 752 | +shell input and other lines are output. This demonstration assumes that the | |
| 753 | +file versions represented by the input manifest are checked out | |
| 754 | +under the current directory. | |
| 755 | + | |
| 756 | +<nowiki><pre> | |
| 757 | +# head MF | |
| 758 | +-----BEGIN PGP SIGNED MESSAGE----- | |
| 759 | +Hash: SHA1 | |
| 760 | + | |
| 761 | +C Make\sthe\s"clearsign"\sPGP\ssigning\sdefault\sto\soff. | |
| 762 | +D 2010-02-23T15:33:14 | |
| 763 | +F BUILD.txt 4f7988767e4e48b29f7eddd0e2cdea4555b9161c | |
| 764 | +F COPYRIGHT-GPL2.txt 06877624ea5c77efe3b7e39b0f909eda6e25a4ec | |
| 765 | +... | |
| 766 | + | |
| 767 | +# grep '^R ' MF | |
| 768 | +R c0788982781981c96a0d81465fec7192 | |
| 769 | + | |
| 770 | +# for i in $(awk '/^F /{print $2}' MF); do \ | |
| 771 | + echo $i $(stat -c '%s' $i); \ | |
| 772 | + cat $i; \ | |
| 773 | +done | md5sum | |
| 774 | +c0788982781981c96a0d81465fec7192 - | |
| 775 | +</pre></nowiki> | |
| 733 | 776 | |
| 734 | 777 | ADDED www/hacker-howto.wiki |
| --- www/fileformat.wiki | |
| +++ www/fileformat.wiki | |
| @@ -46,11 +46,11 @@ | |
| 46 | <li> [#tktchng | Ticket Changes] </li> |
| 47 | <li> [#attachment | Attachments] </li> |
| 48 | <li> [#event | Events] </li> |
| 49 | </ul> |
| 50 | |
| 51 | These seven artifact types are described in the sequel. |
| 52 | |
| 53 | In the current implementation (as of 2009-01-25) the artifacts that |
| 54 | make up a fossil repository are stored in in as delta- and zlib-compressed |
| 55 | blobs in an <a href="http://www.sqlite.org/">SQLite</a> database. This |
| 56 | is an implementation detail and might change in a future release. For |
| @@ -98,16 +98,16 @@ | |
| 98 | |
| 99 | <blockquote> |
| 100 | <b>B</b> <i>baseline-manifest</i><br> |
| 101 | <b>C</b> <i>checkin-comment</i><br> |
| 102 | <b>D</b> <i>time-and-date-stamp</i><br> |
| 103 | <b>F</b> <i>filename</i> <i>SHA1-hash</i> <i>permissions</i> <i>old-name</i><br> |
| 104 | <b>N</b> <i>mimetype</i><br> |
| 105 | <b>P</b> <i>SHA1-hash</i>+<br> |
| 106 | <b>Q</b> (<b>+</b>|<b>-</b>)<i>SHA1-hash ?SHA1-hash?</i><br> |
| 107 | <b>R</b> <i>repository-checksum</i><br> |
| 108 | <b>T</b> (<b>+</b>|<b>-</b>|<b>*</b>)<i>tag-name <b>*</b> ?value?</i><br> |
| 109 | <b>U</b> <i>user-login</i><br> |
| 110 | <b>Z</b> <i>manifest-checksum</i> |
| 111 | </blockquote> |
| 112 | |
| 113 | A manifest may optionally have a single B-card. The B-card specifies |
| @@ -291,11 +291,11 @@ | |
| 291 | |
| 292 | Allowed cards in a control artifact are as follows: |
| 293 | |
| 294 | <blockquote> |
| 295 | <b>D</b> <i>time-and-date-stamp</i><br /> |
| 296 | <b>T</b> (<b>+</b>|<b>-</b>|<b>*</b>)<i>tag-name artifact-id ?value?</i><br /> |
| 297 | <b>U</b> <i>user-name</i><br /> |
| 298 | <b>Z</b> <i>checksum</i><br /> |
| 299 | </blockquote> |
| 300 | |
| 301 | A control artifact must have one D card, one U card, one Z card and |
| @@ -475,11 +475,11 @@ | |
| 475 | <b>C</b> <i>comment</i><br> |
| 476 | <b>D</b> <i>time-and-date-stamp</i><br /> |
| 477 | <b>E</b> <i>event-time</i> <i>event-id</i><br /> |
| 478 | <b>N</b> <i>mimetype</i><br /> |
| 479 | <b>P</b> <i>parent-artifact-id</i>+<br /> |
| 480 | <b>T</b> <b>+</b><i>tag-name</i> <b>*</b> <i>value</i><br /> |
| 481 | <b>U</b> <i>user-name</i><br /> |
| 482 | <b>W</b> <i>size</i> <b>\n</b> <i>text</i> <b>\n</b><br /> |
| 483 | <b>Z</b> <i>checksum</i> |
| 484 | </blockquote> |
| 485 | |
| @@ -499,11 +499,11 @@ | |
| 499 | The optional N card specifies the mimetype of the text of the event |
| 500 | that is contained in the W card. If the N card is omitted, then the |
| 501 | W card text mimetype is assumed to be text/x-fossil, which is the |
| 502 | Fossil wiki format. |
| 503 | |
| 504 | The option P card specifies a prior event with the same event-id from |
| 505 | which the current event is an edit. The P card is a hint to the system |
| 506 | that it might be space efficient to store one event as a delta of the |
| 507 | other. |
| 508 | |
| 509 | An event might contain one or more T-cards used to set |
| @@ -531,12 +531,16 @@ | |
| 531 | |
| 532 | |
| 533 | <a name="summary"></a> |
| 534 | <h2>8.0 Card Summary</h2> |
| 535 | |
| 536 | The following table summaries the various kinds of cards that |
| 537 | appear on Fossil artifacts: |
| 538 | |
| 539 | <table border=1 width="100%"> |
| 540 | <tr> |
| 541 | <th rowspan=2 valign=bottom>Card Format</th> |
| 542 | <th colspan=7>Used By</th> |
| @@ -549,184 +553,223 @@ | |
| 549 | <th>Ticket</th> |
| 550 | <th>Attachment</th> |
| 551 | <th>Event</th> |
| 552 | </tr> |
| 553 | <tr> |
| 554 | <td><b>A</b> <i>filename target source</i></td> |
| 555 | <td> </td> |
| 556 | <td> </td> |
| 557 | <td> </td> |
| 558 | <td> </td> |
| 559 | <td> </td> |
| 560 | <td align=center><b>X</b></td> |
| 561 | <td> </td> |
| 562 | </tr> |
| 563 | <tr> |
| 564 | <td><b>B</b> <i>baseline</i></td> |
| 565 | <td align=center><b>X</b></td> |
| 566 | <td> </td> |
| 567 | <td> </td> |
| 568 | <td> </td> |
| 569 | <td> </td> |
| 570 | <td> </td> |
| 571 | <td> </td> |
| 572 | </tr> |
| 573 | <tr> |
| 574 | <td><b>C</b> <i>comment-text</i></td> |
| 575 | <td align=center><b>X</b></td> |
| 576 | <td> </td> |
| 577 | <td> </td> |
| 578 | <td> </td> |
| 579 | <td> </td> |
| 580 | <td align=center><b>X</b></td> |
| 581 | <td align=center><b>X</b></td> |
| 582 | </tr> |
| 583 | <tr> |
| 584 | <td><b>D</b> <i>date-time-stamp</i></td> |
| 585 | <td align=center><b>X</b></td> |
| 586 | <td align=center> </td> |
| 587 | <td align=center><b>X</b></td> |
| 588 | <td align=center><b>X</b></td> |
| 589 | <td align=center><b>X</b></td> |
| 590 | <td align=center><b>X</b></td> |
| 591 | <td align=center><b>X</b></td> |
| 592 | </tr> |
| 593 | <tr> |
| 594 | <td><b>E</b> <i>event-time event-id</i></td> |
| 595 | <td align=center> </td> |
| 596 | <td align=center> </td> |
| 597 | <td align=center> </td> |
| 598 | <td align=center> </td> |
| 599 | <td align=center> </td> |
| 600 | <td align=center> </td> |
| 601 | <td align=center><b>X</b></td> |
| 602 | </tr> |
| 603 | <tr> |
| 604 | <td><b>F</b> <i>filename uuid permissions oldname</i></td> |
| 605 | <td align=center><b>X</b></td> |
| 606 | <td align=center> </td> |
| 607 | <td align=center> </td> |
| 608 | <td align=center> </td> |
| 609 | <td align=center> </td> |
| 610 | <td align=center> </td> |
| 611 | <td align=center> </td> |
| 612 | </tr> |
| 613 | <tr> |
| 614 | <td><b>J</b> <i>name value</i></td> |
| 615 | <td align=center> </td> |
| 616 | <td align=center> </td> |
| 617 | <td align=center> </td> |
| 618 | <td align=center> </td> |
| 619 | <td align=center><b>X</b></td> |
| 620 | <td align=center> </td> |
| 621 | <td align=center> </td> |
| 622 | </tr> |
| 623 | <tr> |
| 624 | <td><b>K</b> <i>ticket-uuid</i></td> |
| 625 | <td align=center> </td> |
| 626 | <td align=center> </td> |
| 627 | <td align=center> </td> |
| 628 | <td align=center> </td> |
| 629 | <td align=center><b>X</b></td> |
| 630 | <td align=center> </td> |
| 631 | <td align=center> </td> |
| 632 | </tr> |
| 633 | <tr> |
| 634 | <td><b>L</b> <i>wiki-title</i></td> |
| 635 | <td align=center> </td> |
| 636 | <td align=center> </td> |
| 637 | <td align=center> </td> |
| 638 | <td align=center><b>X</b></td> |
| 639 | <td align=center> </td> |
| 640 | <td align=center> </td> |
| 641 | <td align=center> </td> |
| 642 | </tr> |
| 643 | <tr> |
| 644 | <td><b>M</b> <i>uuid</i></td> |
| 645 | <td align=center> </td> |
| 646 | <td align=center><b>X</b></td> |
| 647 | <td align=center> </td> |
| 648 | <td align=center> </td> |
| 649 | <td align=center> </td> |
| 650 | <td align=center> </td> |
| 651 | <td align=center> </td> |
| 652 | </tr> |
| 653 | <tr> |
| 654 | <td><b>N</b> <i>mimetype</i></td> |
| 655 | <td align=center><b>X</b></td> |
| 656 | <td align=center> </td> |
| 657 | <td align=center> </td> |
| 658 | <td align=center><b>X</b></td> |
| 659 | <td align=center> </td> |
| 660 | <td align=center><b>X</b></td> |
| 661 | <td align=center><b>X</b></td> |
| 662 | </tr> |
| 663 | <tr> |
| 664 | <td><b>P</b> <i>uuid ...</i></td> |
| 665 | <td align=center><b>X</b></td> |
| 666 | <td align=center> </td> |
| 667 | <td align=center> </td> |
| 668 | <td align=center><b>X</b></td> |
| 669 | <td align=center> </td> |
| 670 | <td align=center> </td> |
| 671 | <td align=center> </td> |
| 672 | </tr> |
| 673 | <tr> |
| 674 | <td><b>Q</b> (<b>+</b>|<b>-</b>)<i>uuid uuid</i></td> |
| 675 | <td align=center><b>X</b></td> |
| 676 | <td align=center> </td> |
| 677 | <td align=center> </td> |
| 678 | <td align=center> </td> |
| 679 | <td align=center> </td> |
| 680 | <td align=center> </td> |
| 681 | <td align=center> </td> |
| 682 | </tr> |
| 683 | <tr> |
| 684 | <td><b>R</b> <i>md5sum</i></td> |
| 685 | <td align=center><b>X</b></td> |
| 686 | <td align=center> </td> |
| 687 | <td align=center> </td> |
| 688 | <td align=center> </td> |
| 689 | <td align=center> </td> |
| 690 | <td align=center> </td> |
| 691 | <td align=center> </td> |
| 692 | <tr> |
| 693 | <td><b>T</b> (<b>+</b>|<b>*</b>|<b>-</b>)<i>tagname uuid value</i></td> |
| 694 | <td align=center><b>X</b></td> |
| 695 | <td align=center> </td> |
| 696 | <td align=center><b>X</b></td> |
| 697 | <td align=center> </td> |
| 698 | <td align=center> </td> |
| 699 | <td align=center> </td> |
| 700 | <td align=center><b>X</b></td> |
| 701 | </tr> |
| 702 | <tr> |
| 703 | <td><b>U</b> <i>username</i></td> |
| 704 | <td align=center><b>X</b></td> |
| 705 | <td align=center> </td> |
| 706 | <td align=center><b>X</b></td> |
| 707 | <td align=center><b>X</b></td> |
| 708 | <td align=center><b>X</b></td> |
| 709 | <td align=center><b>X</b></td> |
| 710 | <td align=center><b>X</b></td> |
| 711 | </tr> |
| 712 | <tr> |
| 713 | <td><b>W</b> <i>size</i></td> |
| 714 | <td align=center> </td> |
| 715 | <td align=center> </td> |
| 716 | <td align=center> </td> |
| 717 | <td align=center><b>X</b></td> |
| 718 | <td align=center> </td> |
| 719 | <td align=center> </td> |
| 720 | <td align=center><b>X</b></td> |
| 721 | </tr> |
| 722 | <tr> |
| 723 | <td><b>Z</b> <i>md5sum</i></td> |
| 724 | <td align=center><b>X</b></td> |
| 725 | <td align=center><b>X</b></td> |
| 726 | <td align=center><b>X</b></td> |
| 727 | <td align=center><b>X</b></td> |
| 728 | <td align=center><b>X</b></td> |
| 729 | <td align=center><b>X</b></td> |
| 730 | <td align=center><b>X</b></td> |
| 731 | </tr> |
| 732 | </table> |
| 733 | |
| 734 | DDED www/hacker-howto.wiki |
| --- www/fileformat.wiki | |
| +++ www/fileformat.wiki | |
| @@ -46,11 +46,11 @@ | |
| 46 | <li> [#tktchng | Ticket Changes] </li> |
| 47 | <li> [#attachment | Attachments] </li> |
| 48 | <li> [#event | Events] </li> |
| 49 | </ul> |
| 50 | |
| 51 | These seven artifact types are described in the following sections. |
| 52 | |
| 53 | In the current implementation (as of 2009-01-25) the artifacts that |
| 54 | make up a fossil repository are stored in in as delta- and zlib-compressed |
| 55 | blobs in an <a href="http://www.sqlite.org/">SQLite</a> database. This |
| 56 | is an implementation detail and might change in a future release. For |
| @@ -98,16 +98,16 @@ | |
| 98 | |
| 99 | <blockquote> |
| 100 | <b>B</b> <i>baseline-manifest</i><br> |
| 101 | <b>C</b> <i>checkin-comment</i><br> |
| 102 | <b>D</b> <i>time-and-date-stamp</i><br> |
| 103 | <b>F</b> <i>filename</i> ?<i>SHA1-hash</i>? ?<i>permissions</i>? ?<i>old-name</i>?<br> |
| 104 | <b>N</b> <i>mimetype</i><br> |
| 105 | <b>P</b> <i>SHA1-hash</i>+<br> |
| 106 | <b>Q</b> (<b>+</b>|<b>-</b>)<i>SHA1-hash</i> ?<i>SHA1-hash</i>?<br> |
| 107 | <b>R</b> <i>repository-checksum</i><br> |
| 108 | <b>T</b> (<b>+</b>|<b>-</b>|<b>*</b>)<i>tag-name</i> <b>*</b> ?<i>value</i>?<br> |
| 109 | <b>U</b> <i>user-login</i><br> |
| 110 | <b>Z</b> <i>manifest-checksum</i> |
| 111 | </blockquote> |
| 112 | |
| 113 | A manifest may optionally have a single B-card. The B-card specifies |
| @@ -291,11 +291,11 @@ | |
| 291 | |
| 292 | Allowed cards in a control artifact are as follows: |
| 293 | |
| 294 | <blockquote> |
| 295 | <b>D</b> <i>time-and-date-stamp</i><br /> |
| 296 | <b>T</b> (<b>+</b>|<b>-</b>|<b>*</b>)<i>tag-name</i> <i>artifact-id</i> ?<i>value</i>?<br /> |
| 297 | <b>U</b> <i>user-name</i><br /> |
| 298 | <b>Z</b> <i>checksum</i><br /> |
| 299 | </blockquote> |
| 300 | |
| 301 | A control artifact must have one D card, one U card, one Z card and |
| @@ -475,11 +475,11 @@ | |
| 475 | <b>C</b> <i>comment</i><br> |
| 476 | <b>D</b> <i>time-and-date-stamp</i><br /> |
| 477 | <b>E</b> <i>event-time</i> <i>event-id</i><br /> |
| 478 | <b>N</b> <i>mimetype</i><br /> |
| 479 | <b>P</b> <i>parent-artifact-id</i>+<br /> |
| 480 | <b>T</b> <b>+</b><i>tag-name</i> <b>*</b> ?<i>value</i>?<br /> |
| 481 | <b>U</b> <i>user-name</i><br /> |
| 482 | <b>W</b> <i>size</i> <b>\n</b> <i>text</i> <b>\n</b><br /> |
| 483 | <b>Z</b> <i>checksum</i> |
| 484 | </blockquote> |
| 485 | |
| @@ -499,11 +499,11 @@ | |
| 499 | The optional N card specifies the mimetype of the text of the event |
| 500 | that is contained in the W card. If the N card is omitted, then the |
| 501 | W card text mimetype is assumed to be text/x-fossil, which is the |
| 502 | Fossil wiki format. |
| 503 | |
| 504 | The optional P card specifies a prior event with the same event-id from |
| 505 | which the current event is an edit. The P card is a hint to the system |
| 506 | that it might be space efficient to store one event as a delta of the |
| 507 | other. |
| 508 | |
| 509 | An event might contain one or more T-cards used to set |
| @@ -531,12 +531,16 @@ | |
| 531 | |
| 532 | |
| 533 | <a name="summary"></a> |
| 534 | <h2>8.0 Card Summary</h2> |
| 535 | |
| 536 | The following table summarizes the various kinds of cards that appear |
| 537 | on Fossil artifacts. A blank entry means that combination of card and |
| 538 | artifact is not legal. A number or range of numbers indicates the number |
| 539 | of times a card may (or must) appear in the corresponding artifact type. |
| 540 | e.g. a value of 1 indicates a required unique card and 1+ indicates that one |
| 541 | or more such cards are required. |
| 542 | |
| 543 | <table border=1 width="100%"> |
| 544 | <tr> |
| 545 | <th rowspan=2 valign=bottom>Card Format</th> |
| 546 | <th colspan=7>Used By</th> |
| @@ -549,184 +553,223 @@ | |
| 553 | <th>Ticket</th> |
| 554 | <th>Attachment</th> |
| 555 | <th>Event</th> |
| 556 | </tr> |
| 557 | <tr> |
| 558 | <td><b>A</b> <i>filename</i> <i>target</i> ?<i>source</i>?</td> |
| 559 | <td> </td> |
| 560 | <td> </td> |
| 561 | <td> </td> |
| 562 | <td> </td> |
| 563 | <td> </td> |
| 564 | <td align=center><b>1</b></td> |
| 565 | <td> </td> |
| 566 | </tr> |
| 567 | <tr> |
| 568 | <td><b>B</b> <i>baseline</i></td> |
| 569 | <td align=center><b>0-1*</b></td> |
| 570 | <td> </td> |
| 571 | <td> </td> |
| 572 | <td> </td> |
| 573 | <td> </td> |
| 574 | <td> </td> |
| 575 | <td> </td> |
| 576 | </tr> |
| 577 | <tr><td> </td><td colspan='7'>* = Required for delta manifests</td></tr> |
| 578 | <tr> |
| 579 | <td><b>C</b> <i>comment-text</i></td> |
| 580 | <td align=center><b>1</b></td> |
| 581 | <td> </td> |
| 582 | <td> </td> |
| 583 | <td> </td> |
| 584 | <td> </td> |
| 585 | <td align=center><b>0-1</b></td> |
| 586 | <td align=center><b>1</b></td> |
| 587 | </tr> |
| 588 | <tr> |
| 589 | <td><b>D</b> <i>date-time-stamp</i></td> |
| 590 | <td align=center><b>1</b></td> |
| 591 | <td align=center> </td> |
| 592 | <td align=center><b>1</b></td> |
| 593 | <td align=center><b>1</b></td> |
| 594 | <td align=center><b>1</b></td> |
| 595 | <td align=center><b>1</b></td> |
| 596 | <td align=center><b>1</b></td> |
| 597 | </tr> |
| 598 | <tr> |
| 599 | <td><b>E</b> <i>event-time event-id</i></td> |
| 600 | <td align=center> </td> |
| 601 | <td align=center> </td> |
| 602 | <td align=center> </td> |
| 603 | <td align=center> </td> |
| 604 | <td align=center> </td> |
| 605 | <td align=center> </td> |
| 606 | <td align=center><b>1</b></td> |
| 607 | </tr> |
| 608 | <tr> |
| 609 | <td><b>F</b> <i>filename</i> ?<i>uuid</i>? ?<i>permissions</i>? ?<i>oldname</i>?</td> |
| 610 | <td align=center><b>0+</b></td> |
| 611 | <td align=center> </td> |
| 612 | <td align=center> </td> |
| 613 | <td align=center> </td> |
| 614 | <td align=center> </td> |
| 615 | <td align=center> </td> |
| 616 | <td align=center> </td> |
| 617 | </tr> |
| 618 | <tr> |
| 619 | <td><b>J</b> <i>name</i> ?<i>value</i>?</td> |
| 620 | <td align=center> </td> |
| 621 | <td align=center> </td> |
| 622 | <td align=center> </td> |
| 623 | <td align=center> </td> |
| 624 | <td align=center><b>1+</b></td> |
| 625 | <td align=center> </td> |
| 626 | <td align=center> </td> |
| 627 | </tr> |
| 628 | <tr> |
| 629 | <td><b>K</b> <i>ticket-uuid</i></td> |
| 630 | <td align=center> </td> |
| 631 | <td align=center> </td> |
| 632 | <td align=center> </td> |
| 633 | <td align=center> </td> |
| 634 | <td align=center><b>1</b></td> |
| 635 | <td align=center> </td> |
| 636 | <td align=center> </td> |
| 637 | </tr> |
| 638 | <tr> |
| 639 | <td><b>L</b> <i>wiki-title</i></td> |
| 640 | <td align=center> </td> |
| 641 | <td align=center> </td> |
| 642 | <td align=center> </td> |
| 643 | <td align=center><b>1</b></td> |
| 644 | <td align=center> </td> |
| 645 | <td align=center> </td> |
| 646 | <td align=center> </td> |
| 647 | </tr> |
| 648 | <tr> |
| 649 | <td><b>M</b> <i>uuid</i></td> |
| 650 | <td align=center> </td> |
| 651 | <td align=center><b>1+</b></td> |
| 652 | <td align=center> </td> |
| 653 | <td align=center> </td> |
| 654 | <td align=center> </td> |
| 655 | <td align=center> </td> |
| 656 | <td align=center> </td> |
| 657 | </tr> |
| 658 | <tr> |
| 659 | <td><b>N</b> <i>mimetype</i></td> |
| 660 | <td align=center><b>0-1</b></td> |
| 661 | <td align=center> </td> |
| 662 | <td align=center> </td> |
| 663 | <td align=center><b>0-1</b></td> |
| 664 | <td align=center> </td> |
| 665 | <td align=center><b>0-1</b></td> |
| 666 | <td align=center><b>0-1</b></td> |
| 667 | </tr> |
| 668 | <tr> |
| 669 | <td><b>P</b> <i>uuid ...</i></td> |
| 670 | <td align=center><b>0-1</b></td> |
| 671 | <td align=center> </td> |
| 672 | <td align=center> </td> |
| 673 | <td align=center><b>0-1</b></td> |
| 674 | <td align=center> </td> |
| 675 | <td align=center> </td> |
| 676 | <td align=center><b>0-1</b></td> |
| 677 | </tr> |
| 678 | <tr> |
| 679 | <td><b>Q</b> (<b>+</b>|<b>-</b>)<i>uuid</i> ?<i>uuid</i>?</td> |
| 680 | <td align=center><b>0+</b></td> |
| 681 | <td align=center> </td> |
| 682 | <td align=center> </td> |
| 683 | <td align=center> </td> |
| 684 | <td align=center> </td> |
| 685 | <td align=center> </td> |
| 686 | <td align=center> </td> |
| 687 | </tr> |
| 688 | <tr> |
| 689 | <td><b>R</b> <i>md5sum</i></td> |
| 690 | <td align=center><b>0-1*</b></td> |
| 691 | <td align=center> </td> |
| 692 | <td align=center> </td> |
| 693 | <td align=center> </td> |
| 694 | <td align=center> </td> |
| 695 | <td align=center> </td> |
| 696 | <td align=center> </td> |
| 697 | <tr><td> </td><td colspan='7'>* = Required when using F cards</td></tr> |
| 698 | <tr> |
| 699 | <td><b>T</b> (<b>+</b>|<b>*</b>|<b>-</b>)<i>tagname</i> <i>uuid</i> ?<i>value</i>?</td> |
| 700 | <td align=center><b>0+</b></td> |
| 701 | <td align=center> </td> |
| 702 | <td align=center><b>1+</b></td> |
| 703 | <td align=center> </td> |
| 704 | <td align=center> </td> |
| 705 | <td align=center> </td> |
| 706 | <td align=center><b>0+</b></td> |
| 707 | </tr> |
| 708 | <tr> |
| 709 | <td><b>U</b> <i>username</i></td> |
| 710 | <td align=center><b>1</b></td> |
| 711 | <td align=center> </td> |
| 712 | <td align=center><b>1</b></td> |
| 713 | <td align=center><b>1</b></td> |
| 714 | <td align=center><b>1</b></td> |
| 715 | <td align=center><b>0-1</b></td> |
| 716 | <td align=center><b>0-1</b></td> |
| 717 | </tr> |
| 718 | <tr> |
| 719 | <td><b>W</b> <i>size</i></td> |
| 720 | <td align=center> </td> |
| 721 | <td align=center> </td> |
| 722 | <td align=center> </td> |
| 723 | <td align=center><b>1</b></td> |
| 724 | <td align=center> </td> |
| 725 | <td align=center> </td> |
| 726 | <td align=center><b>1</b></td> |
| 727 | </tr> |
| 728 | <tr> |
| 729 | <td><b>Z</b> <i>md5sum</i></td> |
| 730 | <td align=center><b>1</b></td> |
| 731 | <td align=center><b>1</b></td> |
| 732 | <td align=center><b>1</b></td> |
| 733 | <td align=center><b>1</b></td> |
| 734 | <td align=center><b>1</b></td> |
| 735 | <td align=center><b>1</b></td> |
| 736 | <td align=center><b>1</b></td> |
| 737 | </tr> |
| 738 | </table> |
| 739 | |
| 740 | |
| 741 | <a name="addenda"></a> |
| 742 | <h2>9.0 Addenda</h2> |
| 743 | |
| 744 | This section contains additional information which may be useful when |
| 745 | implementing algorithms described above. |
| 746 | |
| 747 | <h3>R Card Hash Calculation</h3> |
| 748 | |
| 749 | Given a manifest file named <tt>MF</tt>, the following Bash shell code |
| 750 | demonstrates how to compute the value of the R card in that manifest. |
| 751 | This example uses manifest [28987096ac]. Lines starting with <tt>#</tt> are |
| 752 | shell input and other lines are output. This demonstration assumes that the |
| 753 | file versions represented by the input manifest are checked out |
| 754 | under the current directory. |
| 755 | |
| 756 | <nowiki><pre> |
| 757 | # head MF |
| 758 | -----BEGIN PGP SIGNED MESSAGE----- |
| 759 | Hash: SHA1 |
| 760 | |
| 761 | C Make\sthe\s"clearsign"\sPGP\ssigning\sdefault\sto\soff. |
| 762 | D 2010-02-23T15:33:14 |
| 763 | F BUILD.txt 4f7988767e4e48b29f7eddd0e2cdea4555b9161c |
| 764 | F COPYRIGHT-GPL2.txt 06877624ea5c77efe3b7e39b0f909eda6e25a4ec |
| 765 | ... |
| 766 | |
| 767 | # grep '^R ' MF |
| 768 | R c0788982781981c96a0d81465fec7192 |
| 769 | |
| 770 | # for i in $(awk '/^F /{print $2}' MF); do \ |
| 771 | echo $i $(stat -c '%s' $i); \ |
| 772 | cat $i; \ |
| 773 | done | md5sum |
| 774 | c0788982781981c96a0d81465fec7192 - |
| 775 | </pre></nowiki> |
| 776 | |
| 777 | DDED www/hacker-howto.wiki |
+13
| --- a/www/hacker-howto.wiki | ||
| +++ b/www/hacker-howto.wiki | ||
| @@ -0,0 +1,13 @@ | ||
| 1 | +<title>Fossil Hackers How-To</title> | |
| 2 | + | |
| 3 | +The following links are of interest to programmers who want . Ordinary users can safely ignore this information. | |
| 4 | + | |
| 5 | + * [./build.wiki | How To Compile And Install Fossil] | |
| 6 | + * [./d_in_ide.md | Using] | |
| 7 | + * [./tech_overview.wiki | ts To The Fossil Project] | |
| 8 | + * [./fileformat.wiki|Fossil Artifact File Format] | |
| 9 | + * [./sync.wiki|The Sync Protocol] | |
| 10 | + * [./style.wiki | Coding Style Guidelines] | |
| 11 | + * [./checkin.wiki | Pre-checkinlease Checklist] | |
| 12 | + * [./backoffice.md | Tadding_cod Fossil] | |
| 13 | + * [./contribute.wiki|Contributing Code Or Enhancements To The Fossil Project] |
| --- a/www/hacker-howto.wiki | |
| +++ b/www/hacker-howto.wiki | |
| @@ -0,0 +1,13 @@ | |
| --- a/www/hacker-howto.wiki | |
| +++ b/www/hacker-howto.wiki | |
| @@ -0,0 +1,13 @@ | |
| 1 | <title>Fossil Hackers How-To</title> |
| 2 | |
| 3 | The following links are of interest to programmers who want . Ordinary users can safely ignore this information. |
| 4 | |
| 5 | * [./build.wiki | How To Compile And Install Fossil] |
| 6 | * [./d_in_ide.md | Using] |
| 7 | * [./tech_overview.wiki | ts To The Fossil Project] |
| 8 | * [./fileformat.wiki|Fossil Artifact File Format] |
| 9 | * [./sync.wiki|The Sync Protocol] |
| 10 | * [./style.wiki | Coding Style Guidelines] |
| 11 | * [./checkin.wiki | Pre-checkinlease Checklist] |
| 12 | * [./backoffice.md | Tadding_cod Fossil] |
| 13 | * [./contribute.wiki|Contributing Code Or Enhancements To The Fossil Project] |
+6
-6
| --- www/index.wiki | ||
| +++ www/index.wiki | ||
| @@ -20,11 +20,11 @@ | ||
| 20 | 20 | <li> [./quickstart.wiki | Quick Start] |
| 21 | 21 | <li> [./build.wiki | Install] |
| 22 | 22 | <li> [../COPYRIGHT-BSD2.txt | License] |
| 23 | 23 | <li> [/timeline | Recent changes] |
| 24 | 24 | <li> [./faq.wiki | FAQ] |
| 25 | -<li> [./contribute.wiki | Contributing] | |
| 25 | +<li> [./hacker-howto.wiki | Hacker How-To] | |
| 26 | 26 | <li> [./changes.wiki | Change Log] |
| 27 | 27 | <li> [./hints.wiki | Tip & Hints] |
| 28 | 28 | <li> [./permutedindex.wiki#pindex | Documentation Index] |
| 29 | 29 | <li> [http://www.fossil-scm.org/schimpf-book/home | Jim Schimpf's book] |
| 30 | 30 | <li> Mailing list |
| @@ -81,33 +81,33 @@ | ||
| 81 | 81 | Installation is trivial: simply download a |
| 82 | 82 | <a href="http://www.fossil-scm.org/download.html">precompiled binary</a> |
| 83 | 83 | for Linux, Mac, or Windows and put it on your $PATH. |
| 84 | 84 | [./build.wiki | Easy-to-compile source code] is available for |
| 85 | 85 | users on other platforms. Fossil sources are also mostly self-contained, |
| 86 | - requiring only the "zlib" library and the standard C library to build. | |
| 86 | + requiring only the standard C library to build. | |
| 87 | 87 | |
| 88 | 88 | 5. <b>Simple Networking</b> - |
| 89 | 89 | Fossil uses plain old HTTP (with |
| 90 | 90 | [./quickstart.wiki#proxy | proxy support]) |
| 91 | 91 | for all network communications, meaning that it works fine from behind |
| 92 | 92 | restrictive firewalls. The protocol is |
| 93 | 93 | [./stats.wiki | bandwidth efficient] to the point that Fossil can be |
| 94 | 94 | used comfortably over a dial-up internet connection. |
| 95 | 95 | |
| 96 | - 6. <b>CGI Enabled</b> - | |
| 96 | + 6. <b>CGI/SCGI Enabled</b> - | |
| 97 | 97 | No server is required to use fossil. But a |
| 98 | - server does make collaboration easier. Fossil supports three different | |
| 99 | - yet simple [./quickstart.wiki#serversetup | server configurations]. | |
| 98 | + server does make collaboration easier. Fossil supports four different | |
| 99 | + yet simple [./server.wiki | server configurations]. | |
| 100 | 100 | The most popular is a 2-line CGI script. This is the approach |
| 101 | 101 | used by the [./selfhost.wiki | self-hosting fossil repositories]. |
| 102 | 102 | |
| 103 | 103 | 7. <b>Robust & Reliable</b> - |
| 104 | 104 | Fossil stores content using an [./fileformat.wiki | enduring file format] |
| 105 | 105 | in an SQLite database so that transactions are |
| 106 | 106 | atomic even if interrupted by a power loss or system crash. Furthermore, |
| 107 | 107 | automatic [./selfcheck.wiki | self-checks] verify that all aspects of |
| 108 | - the repository are consistent prior to each commit. In over three years | |
| 108 | + the repository are consistent prior to each commit. In over six years | |
| 109 | 109 | of operation, no work has ever been lost after having been committed to |
| 110 | 110 | a Fossil repository. |
| 111 | 111 | |
| 112 | 112 | <hr> |
| 113 | 113 | <h3>Links For Fossil Users:</h3> |
| 114 | 114 |
| --- www/index.wiki | |
| +++ www/index.wiki | |
| @@ -20,11 +20,11 @@ | |
| 20 | <li> [./quickstart.wiki | Quick Start] |
| 21 | <li> [./build.wiki | Install] |
| 22 | <li> [../COPYRIGHT-BSD2.txt | License] |
| 23 | <li> [/timeline | Recent changes] |
| 24 | <li> [./faq.wiki | FAQ] |
| 25 | <li> [./contribute.wiki | Contributing] |
| 26 | <li> [./changes.wiki | Change Log] |
| 27 | <li> [./hints.wiki | Tip & Hints] |
| 28 | <li> [./permutedindex.wiki#pindex | Documentation Index] |
| 29 | <li> [http://www.fossil-scm.org/schimpf-book/home | Jim Schimpf's book] |
| 30 | <li> Mailing list |
| @@ -81,33 +81,33 @@ | |
| 81 | Installation is trivial: simply download a |
| 82 | <a href="http://www.fossil-scm.org/download.html">precompiled binary</a> |
| 83 | for Linux, Mac, or Windows and put it on your $PATH. |
| 84 | [./build.wiki | Easy-to-compile source code] is available for |
| 85 | users on other platforms. Fossil sources are also mostly self-contained, |
| 86 | requiring only the "zlib" library and the standard C library to build. |
| 87 | |
| 88 | 5. <b>Simple Networking</b> - |
| 89 | Fossil uses plain old HTTP (with |
| 90 | [./quickstart.wiki#proxy | proxy support]) |
| 91 | for all network communications, meaning that it works fine from behind |
| 92 | restrictive firewalls. The protocol is |
| 93 | [./stats.wiki | bandwidth efficient] to the point that Fossil can be |
| 94 | used comfortably over a dial-up internet connection. |
| 95 | |
| 96 | 6. <b>CGI Enabled</b> - |
| 97 | No server is required to use fossil. But a |
| 98 | server does make collaboration easier. Fossil supports three different |
| 99 | yet simple [./quickstart.wiki#serversetup | server configurations]. |
| 100 | The most popular is a 2-line CGI script. This is the approach |
| 101 | used by the [./selfhost.wiki | self-hosting fossil repositories]. |
| 102 | |
| 103 | 7. <b>Robust & Reliable</b> - |
| 104 | Fossil stores content using an [./fileformat.wiki | enduring file format] |
| 105 | in an SQLite database so that transactions are |
| 106 | atomic even if interrupted by a power loss or system crash. Furthermore, |
| 107 | automatic [./selfcheck.wiki | self-checks] verify that all aspects of |
| 108 | the repository are consistent prior to each commit. In over three years |
| 109 | of operation, no work has ever been lost after having been committed to |
| 110 | a Fossil repository. |
| 111 | |
| 112 | <hr> |
| 113 | <h3>Links For Fossil Users:</h3> |
| 114 |
| --- www/index.wiki | |
| +++ www/index.wiki | |
| @@ -20,11 +20,11 @@ | |
| 20 | <li> [./quickstart.wiki | Quick Start] |
| 21 | <li> [./build.wiki | Install] |
| 22 | <li> [../COPYRIGHT-BSD2.txt | License] |
| 23 | <li> [/timeline | Recent changes] |
| 24 | <li> [./faq.wiki | FAQ] |
| 25 | <li> [./hacker-howto.wiki | Hacker How-To] |
| 26 | <li> [./changes.wiki | Change Log] |
| 27 | <li> [./hints.wiki | Tip & Hints] |
| 28 | <li> [./permutedindex.wiki#pindex | Documentation Index] |
| 29 | <li> [http://www.fossil-scm.org/schimpf-book/home | Jim Schimpf's book] |
| 30 | <li> Mailing list |
| @@ -81,33 +81,33 @@ | |
| 81 | Installation is trivial: simply download a |
| 82 | <a href="http://www.fossil-scm.org/download.html">precompiled binary</a> |
| 83 | for Linux, Mac, or Windows and put it on your $PATH. |
| 84 | [./build.wiki | Easy-to-compile source code] is available for |
| 85 | users on other platforms. Fossil sources are also mostly self-contained, |
| 86 | requiring only the standard C library to build. |
| 87 | |
| 88 | 5. <b>Simple Networking</b> - |
| 89 | Fossil uses plain old HTTP (with |
| 90 | [./quickstart.wiki#proxy | proxy support]) |
| 91 | for all network communications, meaning that it works fine from behind |
| 92 | restrictive firewalls. The protocol is |
| 93 | [./stats.wiki | bandwidth efficient] to the point that Fossil can be |
| 94 | used comfortably over a dial-up internet connection. |
| 95 | |
| 96 | 6. <b>CGI/SCGI Enabled</b> - |
| 97 | No server is required to use fossil. But a |
| 98 | server does make collaboration easier. Fossil supports four different |
| 99 | yet simple [./server.wiki | server configurations]. |
| 100 | The most popular is a 2-line CGI script. This is the approach |
| 101 | used by the [./selfhost.wiki | self-hosting fossil repositories]. |
| 102 | |
| 103 | 7. <b>Robust & Reliable</b> - |
| 104 | Fossil stores content using an [./fileformat.wiki | enduring file format] |
| 105 | in an SQLite database so that transactions are |
| 106 | atomic even if interrupted by a power loss or system crash. Furthermore, |
| 107 | automatic [./selfcheck.wiki | self-checks] verify that all aspects of |
| 108 | the repository are consistent prior to each commit. In over six years |
| 109 | of operation, no work has ever been lost after having been committed to |
| 110 | a Fossil repository. |
| 111 | |
| 112 | <hr> |
| 113 | <h3>Links For Fossil Users:</h3> |
| 114 |
+1
| --- www/mkindex.tcl | ||
| +++ www/mkindex.tcl | ||
| @@ -28,10 +28,11 @@ | ||
| 28 | 28 | fileformat.wiki {Fossil File Format} |
| 29 | 29 | fiveminutes.wiki {Update and Running in 5 Minutes as a Single User} |
| 30 | 30 | foss-cklist.wiki {Checklist For Successful Open-Source Projects} |
| 31 | 31 | fossil-from-msvc.wiki {Integrating Fossil in the Microsoft Express 2010 IDE} |
| 32 | 32 | fossil-v-git.wiki {Fossil Versus Git} |
| 33 | + hacker-howto.wiki {Hacker How-To} | |
| 33 | 34 | hints.wiki {Fossil Tips And Usage Hints} |
| 34 | 35 | index.wiki {Home Page} |
| 35 | 36 | inout.wiki {Import And Export To And From Git} |
| 36 | 37 | makefile.wiki {The Fossil Build Process} |
| 37 | 38 | newrepo.wiki {How To Create A New Fossil Repository} |
| 38 | 39 |
| --- www/mkindex.tcl | |
| +++ www/mkindex.tcl | |
| @@ -28,10 +28,11 @@ | |
| 28 | fileformat.wiki {Fossil File Format} |
| 29 | fiveminutes.wiki {Update and Running in 5 Minutes as a Single User} |
| 30 | foss-cklist.wiki {Checklist For Successful Open-Source Projects} |
| 31 | fossil-from-msvc.wiki {Integrating Fossil in the Microsoft Express 2010 IDE} |
| 32 | fossil-v-git.wiki {Fossil Versus Git} |
| 33 | hints.wiki {Fossil Tips And Usage Hints} |
| 34 | index.wiki {Home Page} |
| 35 | inout.wiki {Import And Export To And From Git} |
| 36 | makefile.wiki {The Fossil Build Process} |
| 37 | newrepo.wiki {How To Create A New Fossil Repository} |
| 38 |
| --- www/mkindex.tcl | |
| +++ www/mkindex.tcl | |
| @@ -28,10 +28,11 @@ | |
| 28 | fileformat.wiki {Fossil File Format} |
| 29 | fiveminutes.wiki {Update and Running in 5 Minutes as a Single User} |
| 30 | foss-cklist.wiki {Checklist For Successful Open-Source Projects} |
| 31 | fossil-from-msvc.wiki {Integrating Fossil in the Microsoft Express 2010 IDE} |
| 32 | fossil-v-git.wiki {Fossil Versus Git} |
| 33 | hacker-howto.wiki {Hacker How-To} |
| 34 | hints.wiki {Fossil Tips And Usage Hints} |
| 35 | index.wiki {Home Page} |
| 36 | inout.wiki {Import And Export To And From Git} |
| 37 | makefile.wiki {The Fossil Build Process} |
| 38 | newrepo.wiki {How To Create A New Fossil Repository} |
| 39 |
| --- www/permutedindex.wiki | ||
| +++ www/permutedindex.wiki | ||
| @@ -88,16 +88,18 @@ | ||
| 88 | 88 | <li><a href="fossil-v-git.wiki">Git — Fossil Versus</a></li> |
| 89 | 89 | <li><a href="inout.wiki">Git — Import And Export To And From</a></li> |
| 90 | 90 | <li><a href="quotes.wiki">Git, and DVCSes in General — Quotes: What People Are Saying About Fossil,</a></li> |
| 91 | 91 | <li><a href="quickstart.wiki">Guide — Fossil Quick Start</a></li> |
| 92 | 92 | <li><a href="style.wiki">Guidelines — Source Code Style</a></li> |
| 93 | +<li><a href="hacker-howto.wiki">Hacker How-To</a></li> | |
| 93 | 94 | <li><a href="adding_code.wiki">Hacking Fossil</a></li> |
| 94 | 95 | <li><a href="hints.wiki">Hints — Fossil Tips And Usage</a></li> |
| 95 | 96 | <li><a href="index.wiki">Home Page</a></li> |
| 96 | 97 | <li><a href="selfhost.wiki">Hosting Repositories — Fossil Self</a></li> |
| 97 | 98 | <li><a href="server.wiki">How To Configure A Fossil Server</a></li> |
| 98 | 99 | <li><a href="newrepo.wiki">How To Create A New Fossil Repository</a></li> |
| 100 | +<li><a href="hacker-howto.wiki">How-To — Hacker</a></li> | |
| 99 | 101 | <li><a href="fossil-from-msvc.wiki">IDE — Integrating Fossil in the Microsoft Express 2010</a></li> |
| 100 | 102 | <li><a href="tech_overview.wiki">Implementation Of Fossil — A Technical Overview Of The Design And</a></li> |
| 101 | 103 | <li><a href="inout.wiki">Import And Export To And From Git</a></li> |
| 102 | 104 | <li><a href="build.wiki">Installing Fossil — Compiling and</a></li> |
| 103 | 105 | <li><a href="fossil-from-msvc.wiki">Integrating Fossil in the Microsoft Express 2010 IDE</a></li> |
| 104 | 106 |
| --- www/permutedindex.wiki | |
| +++ www/permutedindex.wiki | |
| @@ -88,16 +88,18 @@ | |
| 88 | <li><a href="fossil-v-git.wiki">Git — Fossil Versus</a></li> |
| 89 | <li><a href="inout.wiki">Git — Import And Export To And From</a></li> |
| 90 | <li><a href="quotes.wiki">Git, and DVCSes in General — Quotes: What People Are Saying About Fossil,</a></li> |
| 91 | <li><a href="quickstart.wiki">Guide — Fossil Quick Start</a></li> |
| 92 | <li><a href="style.wiki">Guidelines — Source Code Style</a></li> |
| 93 | <li><a href="adding_code.wiki">Hacking Fossil</a></li> |
| 94 | <li><a href="hints.wiki">Hints — Fossil Tips And Usage</a></li> |
| 95 | <li><a href="index.wiki">Home Page</a></li> |
| 96 | <li><a href="selfhost.wiki">Hosting Repositories — Fossil Self</a></li> |
| 97 | <li><a href="server.wiki">How To Configure A Fossil Server</a></li> |
| 98 | <li><a href="newrepo.wiki">How To Create A New Fossil Repository</a></li> |
| 99 | <li><a href="fossil-from-msvc.wiki">IDE — Integrating Fossil in the Microsoft Express 2010</a></li> |
| 100 | <li><a href="tech_overview.wiki">Implementation Of Fossil — A Technical Overview Of The Design And</a></li> |
| 101 | <li><a href="inout.wiki">Import And Export To And From Git</a></li> |
| 102 | <li><a href="build.wiki">Installing Fossil — Compiling and</a></li> |
| 103 | <li><a href="fossil-from-msvc.wiki">Integrating Fossil in the Microsoft Express 2010 IDE</a></li> |
| 104 |
| --- www/permutedindex.wiki | |
| +++ www/permutedindex.wiki | |
| @@ -88,16 +88,18 @@ | |
| 88 | <li><a href="fossil-v-git.wiki">Git — Fossil Versus</a></li> |
| 89 | <li><a href="inout.wiki">Git — Import And Export To And From</a></li> |
| 90 | <li><a href="quotes.wiki">Git, and DVCSes in General — Quotes: What People Are Saying About Fossil,</a></li> |
| 91 | <li><a href="quickstart.wiki">Guide — Fossil Quick Start</a></li> |
| 92 | <li><a href="style.wiki">Guidelines — Source Code Style</a></li> |
| 93 | <li><a href="hacker-howto.wiki">Hacker How-To</a></li> |
| 94 | <li><a href="adding_code.wiki">Hacking Fossil</a></li> |
| 95 | <li><a href="hints.wiki">Hints — Fossil Tips And Usage</a></li> |
| 96 | <li><a href="index.wiki">Home Page</a></li> |
| 97 | <li><a href="selfhost.wiki">Hosting Repositories — Fossil Self</a></li> |
| 98 | <li><a href="server.wiki">How To Configure A Fossil Server</a></li> |
| 99 | <li><a href="newrepo.wiki">How To Create A New Fossil Repository</a></li> |
| 100 | <li><a href="hacker-howto.wiki">How-To — Hacker</a></li> |
| 101 | <li><a href="fossil-from-msvc.wiki">IDE — Integrating Fossil in the Microsoft Express 2010</a></li> |
| 102 | <li><a href="tech_overview.wiki">Implementation Of Fossil — A Technical Overview Of The Design And</a></li> |
| 103 | <li><a href="inout.wiki">Import And Export To And From Git</a></li> |
| 104 | <li><a href="build.wiki">Installing Fossil — Compiling and</a></li> |
| 105 | <li><a href="fossil-from-msvc.wiki">Integrating Fossil in the Microsoft Express 2010 IDE</a></li> |
| 106 |
+18
-47
| --- www/quickstart.wiki | ||
| +++ www/quickstart.wiki | ||
| @@ -285,69 +285,40 @@ | ||
| 285 | 285 | mistake. Undo and redo only work for changes that have |
| 286 | 286 | not yet been checked in using commit and there is only a single |
| 287 | 287 | level of undo/redo.</p> |
| 288 | 288 | |
| 289 | 289 | |
| 290 | -<a name="serversetup"></a> | |
| 291 | 290 | <h2>Setting Up A Server</h2> |
| 292 | 291 | |
| 293 | - <p>The easiest way to set up a server is:</p> | |
| 292 | + <p>Fossil can act as a stand-alone web server using one of these | |
| 293 | + commands:</p> | |
| 294 | 294 | |
| 295 | 295 | <blockquote> |
| 296 | - <b>[/help/server | fossil server]</b> <i>repository-filename</i> | |
| 296 | + <b>[/help/server | fossil server]</b> <i>repository-filename</i><br> | |
| 297 | + <b>[/help/ui | fossil ui]</b> <i>repository-filename</i> | |
| 297 | 298 | </blockquote> |
| 298 | 299 | |
| 299 | - <p>Or</b> | |
| 300 | - | |
| 301 | - <blockquote> | |
| 302 | - <b>[/help/ui | fossil ui]</b> <i>repository-filename</i> | |
| 303 | - </blockquote> | |
| 300 | + <p>The <i>repository-filename</i> can be omitted when these commands | |
| 301 | + are run from within an open check-out, which a particularly useful | |
| 302 | + shortcut for the <b>fossil ui</b> command. | |
| 304 | 303 | |
| 305 | 304 | <p>The <b>ui</b> command is intended for accessing the web interface |
| 306 | 305 | from a local desktop. The <b>ui</b> command binds to the loopback IP |
| 307 | 306 | address only (and thus makes the web interface visible only on the |
| 308 | 307 | local machine) and it automatically start your web browser pointing at the |
| 309 | 308 | server. For cross-machine collaboration, use the <b>server</b> command, |
| 310 | - which binds on all IP addresses and does not try to start a web browser. | |
| 311 | - You can omit the <i>repository-filename</i> if you are within | |
| 312 | - a checked-out local tree. The <b>server</b> uses port 8080 by default | |
| 313 | - but you can specify a different port using the <b>-port</b> option.</p> | |
| 314 | - | |
| 315 | - <p>Command-line servers like this are useful when two people want | |
| 316 | - to share a repository on temporary or ad-hoc basis. For a more | |
| 317 | - permanent installation, you should use either the CGI server or the | |
| 318 | - inetd server. | |
| 319 | - <a name="cgiserver"></a> | |
| 320 | - To use the CGI server, create a CGI script that | |
| 321 | - looks something like this:</p> | |
| 322 | - | |
| 323 | - <blockquote><b> | |
| 324 | - #!/usr/local/bin/fossil<br> | |
| 325 | - repository: /home/proj1/repos1.fossil | |
| 326 | - </b></blockquote> | |
| 327 | - | |
| 328 | - <p>Adjust the paths in this CGI script to match your installation | |
| 329 | - and make sure both repository file and the directory that contains it | |
| 330 | - are readable and writable by the user that CGI scripts run as. | |
| 331 | - Then point clients at the CGI script. That's all there is to it!</p> | |
| 332 | - | |
| 333 | - <a name="inetdserver"></a> | |
| 334 | - <p>You can also run fossil from inetd or xinetd. For an inetd | |
| 335 | - installation, make an entry in /etc/inetd.conf that looks something | |
| 336 | - like this:</p> | |
| 337 | - | |
| 338 | - <blockquote><b> | |
| 339 | - 80 stream tcp nowait.1000 root /usr/bin/fossil \<br> | |
| 340 | - /usr/bin/fossil http /home/proj1/repos1.fossil | |
| 341 | - </b></blockquote> | |
| 342 | - | |
| 343 | - <p>Adjust the paths to suit your installation, of course. Notice that | |
| 344 | - fossil runs as root. This is not required - you can run it as an | |
| 345 | - unprivileged user. But it is more secure to run fossil as root. | |
| 346 | - When you do run fossil as root, it automatically puts itself in a | |
| 347 | - chroot jail in the same directory as the repository, then drops | |
| 348 | - root privileges prior to reading any information from the socket.</p> | |
| 309 | + which binds on all IP addresses and does not try to start a web browser.</p> | |
| 310 | + | |
| 311 | + <p>Servers are also easily configured as: | |
| 312 | + <ul> | |
| 313 | + <li>[./server.wiki#inetd|inetd/xinetd] | |
| 314 | + <li>[./server.wiki#cgi|CGI] | |
| 315 | + <li>[./server.wiki#scgi|SCGI] | |
| 316 | + </ul> | |
| 317 | + | |
| 318 | + <p>The the [./selfhost.wiki | self-hosting fossil repositories] use | |
| 319 | + CGI. | |
| 349 | 320 | |
| 350 | 321 | <a name="proxy"></a> |
| 351 | 322 | <h2>HTTP Proxies</h2> |
| 352 | 323 | |
| 353 | 324 | <p>If you are behind a restrictive firewall that requires you to use |
| 354 | 325 | |
| 355 | 326 | ADDED www/scgi.wiki |
| --- www/quickstart.wiki | |
| +++ www/quickstart.wiki | |
| @@ -285,69 +285,40 @@ | |
| 285 | mistake. Undo and redo only work for changes that have |
| 286 | not yet been checked in using commit and there is only a single |
| 287 | level of undo/redo.</p> |
| 288 | |
| 289 | |
| 290 | <a name="serversetup"></a> |
| 291 | <h2>Setting Up A Server</h2> |
| 292 | |
| 293 | <p>The easiest way to set up a server is:</p> |
| 294 | |
| 295 | <blockquote> |
| 296 | <b>[/help/server | fossil server]</b> <i>repository-filename</i> |
| 297 | </blockquote> |
| 298 | |
| 299 | <p>Or</b> |
| 300 | |
| 301 | <blockquote> |
| 302 | <b>[/help/ui | fossil ui]</b> <i>repository-filename</i> |
| 303 | </blockquote> |
| 304 | |
| 305 | <p>The <b>ui</b> command is intended for accessing the web interface |
| 306 | from a local desktop. The <b>ui</b> command binds to the loopback IP |
| 307 | address only (and thus makes the web interface visible only on the |
| 308 | local machine) and it automatically start your web browser pointing at the |
| 309 | server. For cross-machine collaboration, use the <b>server</b> command, |
| 310 | which binds on all IP addresses and does not try to start a web browser. |
| 311 | You can omit the <i>repository-filename</i> if you are within |
| 312 | a checked-out local tree. The <b>server</b> uses port 8080 by default |
| 313 | but you can specify a different port using the <b>-port</b> option.</p> |
| 314 | |
| 315 | <p>Command-line servers like this are useful when two people want |
| 316 | to share a repository on temporary or ad-hoc basis. For a more |
| 317 | permanent installation, you should use either the CGI server or the |
| 318 | inetd server. |
| 319 | <a name="cgiserver"></a> |
| 320 | To use the CGI server, create a CGI script that |
| 321 | looks something like this:</p> |
| 322 | |
| 323 | <blockquote><b> |
| 324 | #!/usr/local/bin/fossil<br> |
| 325 | repository: /home/proj1/repos1.fossil |
| 326 | </b></blockquote> |
| 327 | |
| 328 | <p>Adjust the paths in this CGI script to match your installation |
| 329 | and make sure both repository file and the directory that contains it |
| 330 | are readable and writable by the user that CGI scripts run as. |
| 331 | Then point clients at the CGI script. That's all there is to it!</p> |
| 332 | |
| 333 | <a name="inetdserver"></a> |
| 334 | <p>You can also run fossil from inetd or xinetd. For an inetd |
| 335 | installation, make an entry in /etc/inetd.conf that looks something |
| 336 | like this:</p> |
| 337 | |
| 338 | <blockquote><b> |
| 339 | 80 stream tcp nowait.1000 root /usr/bin/fossil \<br> |
| 340 | /usr/bin/fossil http /home/proj1/repos1.fossil |
| 341 | </b></blockquote> |
| 342 | |
| 343 | <p>Adjust the paths to suit your installation, of course. Notice that |
| 344 | fossil runs as root. This is not required - you can run it as an |
| 345 | unprivileged user. But it is more secure to run fossil as root. |
| 346 | When you do run fossil as root, it automatically puts itself in a |
| 347 | chroot jail in the same directory as the repository, then drops |
| 348 | root privileges prior to reading any information from the socket.</p> |
| 349 | |
| 350 | <a name="proxy"></a> |
| 351 | <h2>HTTP Proxies</h2> |
| 352 | |
| 353 | <p>If you are behind a restrictive firewall that requires you to use |
| 354 | |
| 355 | DDED www/scgi.wiki |
| --- www/quickstart.wiki | |
| +++ www/quickstart.wiki | |
| @@ -285,69 +285,40 @@ | |
| 285 | mistake. Undo and redo only work for changes that have |
| 286 | not yet been checked in using commit and there is only a single |
| 287 | level of undo/redo.</p> |
| 288 | |
| 289 | |
| 290 | <h2>Setting Up A Server</h2> |
| 291 | |
| 292 | <p>Fossil can act as a stand-alone web server using one of these |
| 293 | commands:</p> |
| 294 | |
| 295 | <blockquote> |
| 296 | <b>[/help/server | fossil server]</b> <i>repository-filename</i><br> |
| 297 | <b>[/help/ui | fossil ui]</b> <i>repository-filename</i> |
| 298 | </blockquote> |
| 299 | |
| 300 | <p>The <i>repository-filename</i> can be omitted when these commands |
| 301 | are run from within an open check-out, which a particularly useful |
| 302 | shortcut for the <b>fossil ui</b> command. |
| 303 | |
| 304 | <p>The <b>ui</b> command is intended for accessing the web interface |
| 305 | from a local desktop. The <b>ui</b> command binds to the loopback IP |
| 306 | address only (and thus makes the web interface visible only on the |
| 307 | local machine) and it automatically start your web browser pointing at the |
| 308 | server. For cross-machine collaboration, use the <b>server</b> command, |
| 309 | which binds on all IP addresses and does not try to start a web browser.</p> |
| 310 | |
| 311 | <p>Servers are also easily configured as: |
| 312 | <ul> |
| 313 | <li>[./server.wiki#inetd|inetd/xinetd] |
| 314 | <li>[./server.wiki#cgi|CGI] |
| 315 | <li>[./server.wiki#scgi|SCGI] |
| 316 | </ul> |
| 317 | |
| 318 | <p>The the [./selfhost.wiki | self-hosting fossil repositories] use |
| 319 | CGI. |
| 320 | |
| 321 | <a name="proxy"></a> |
| 322 | <h2>HTTP Proxies</h2> |
| 323 | |
| 324 | <p>If you are behind a restrictive firewall that requires you to use |
| 325 | |
| 326 | DDED www/scgi.wiki |
+22
| --- a/www/scgi.wiki | ||
| +++ b/www/scgi.wiki | ||
| @@ -0,0 +1,22 @@ | ||
| 1 | +<title>Fossil SCGI</title> | |
| 2 | + | |
| 3 | +To run Fossil using SCGI, start the [/help/server|fossil server] command | |
| 4 | +with the --scgi command-line option. You will probably also want to | |
| 5 | +specific an alternative TCP/IP port usiblockquote>ing --port. For example: | |
| 6 | + | |
| 7 | +<pr</blockquote> | |
| 8 | + | |
| 9 | +Then configure your SCGI-aware web-server to send SCGI requests to port | |
| 10 | +9000 on the machine where Fossil is running. A typical configuratiblockquote><pre> | |
| 11 | +location ~ ^/demo_project/ { | |
| 12 | + include scgi_params; | |
| 13 | + scgi_pass localhost:9000; | |
| 14 | + scgi_param SCRIPT_NAME "/demo_project"; | |
| 15 | + scgi</blockquotgi_param HTTPS "on"; | |
| 16 | +} | |
| 17 | +</pre> | |
| 18 | + | |
| 19 | +NoteO or SCRIPT_NAME | |
| 20 | +variables via SCGI, but Fossil needs one or the other. So the configuration | |
| 21 | +above needs to add SCRIPT_N an | |
| 22 | +error. |
| --- a/www/scgi.wiki | |
| +++ b/www/scgi.wiki | |
| @@ -0,0 +1,22 @@ | |
| --- a/www/scgi.wiki | |
| +++ b/www/scgi.wiki | |
| @@ -0,0 +1,22 @@ | |
| 1 | <title>Fossil SCGI</title> |
| 2 | |
| 3 | To run Fossil using SCGI, start the [/help/server|fossil server] command |
| 4 | with the --scgi command-line option. You will probably also want to |
| 5 | specific an alternative TCP/IP port usiblockquote>ing --port. For example: |
| 6 | |
| 7 | <pr</blockquote> |
| 8 | |
| 9 | Then configure your SCGI-aware web-server to send SCGI requests to port |
| 10 | 9000 on the machine where Fossil is running. A typical configuratiblockquote><pre> |
| 11 | location ~ ^/demo_project/ { |
| 12 | include scgi_params; |
| 13 | scgi_pass localhost:9000; |
| 14 | scgi_param SCRIPT_NAME "/demo_project"; |
| 15 | scgi</blockquotgi_param HTTPS "on"; |
| 16 | } |
| 17 | </pre> |
| 18 | |
| 19 | NoteO or SCRIPT_NAME |
| 20 | variables via SCGI, but Fossil needs one or the other. So the configuration |
| 21 | above needs to add SCRIPT_N an |
| 22 | error. |
+4
-3
| --- www/selfhost.wiki | ||
| +++ www/selfhost.wiki | ||
| @@ -21,12 +21,12 @@ | ||
| 21 | 21 | |
| 22 | 22 | Server (1) runs as a CGI script on a |
| 23 | 23 | <a href="http://www.linode.com/">Linode 1024</a> located in Dallas, TX |
| 24 | 24 | - on the same virtual machine that |
| 25 | 25 | hosts <a href="http://www.sqlite.org/">SQLite</a> and over a |
| 26 | -dozen other smaller projects. This demonstrates that Fossil does not | |
| 27 | -require much server power. | |
| 26 | +dozen other smaller projects. This demonstrates that Fossil can run on | |
| 27 | +a low-power host processor. | |
| 28 | 28 | Multiple fossil-based projects can easily be hosted on the same machine, |
| 29 | 29 | even if that machine is itself one of several dozen virtual machines on |
| 30 | 30 | single physical box. The CGI script that runs the canonical Fossil |
| 31 | 31 | self-hosting repository is as follows: |
| 32 | 32 | |
| @@ -38,11 +38,12 @@ | ||
| 38 | 38 | Server (3) runs as a CGI script on a shared hosting account at |
| 39 | 39 | <a href="http://www.he.net/">Hurricane Electric</a> in Fremont, CA. |
| 40 | 40 | This server demonstrates the ability of |
| 41 | 41 | Fossil to run on an economical shared-host web account with no |
| 42 | 42 | privileges beyond port 80 HTTP access and CGI. It is not necessary |
| 43 | -to have a dedicated server to run Fossil. As far as we are aware, | |
| 43 | +to have a dedicated computer with administrator to run Fossil. | |
| 44 | +As far as we are aware, | |
| 44 | 45 | Fossil is the only full-featured configuration management system |
| 45 | 46 | that can run in |
| 46 | 47 | such a restricted environment. The CGI script that runs on the |
| 47 | 48 | Hurricane Electric server is the same as the CGI script shown above, |
| 48 | 49 | except that the pathnames are modified to suit the environment: |
| 49 | 50 |
| --- www/selfhost.wiki | |
| +++ www/selfhost.wiki | |
| @@ -21,12 +21,12 @@ | |
| 21 | |
| 22 | Server (1) runs as a CGI script on a |
| 23 | <a href="http://www.linode.com/">Linode 1024</a> located in Dallas, TX |
| 24 | - on the same virtual machine that |
| 25 | hosts <a href="http://www.sqlite.org/">SQLite</a> and over a |
| 26 | dozen other smaller projects. This demonstrates that Fossil does not |
| 27 | require much server power. |
| 28 | Multiple fossil-based projects can easily be hosted on the same machine, |
| 29 | even if that machine is itself one of several dozen virtual machines on |
| 30 | single physical box. The CGI script that runs the canonical Fossil |
| 31 | self-hosting repository is as follows: |
| 32 | |
| @@ -38,11 +38,12 @@ | |
| 38 | Server (3) runs as a CGI script on a shared hosting account at |
| 39 | <a href="http://www.he.net/">Hurricane Electric</a> in Fremont, CA. |
| 40 | This server demonstrates the ability of |
| 41 | Fossil to run on an economical shared-host web account with no |
| 42 | privileges beyond port 80 HTTP access and CGI. It is not necessary |
| 43 | to have a dedicated server to run Fossil. As far as we are aware, |
| 44 | Fossil is the only full-featured configuration management system |
| 45 | that can run in |
| 46 | such a restricted environment. The CGI script that runs on the |
| 47 | Hurricane Electric server is the same as the CGI script shown above, |
| 48 | except that the pathnames are modified to suit the environment: |
| 49 |
| --- www/selfhost.wiki | |
| +++ www/selfhost.wiki | |
| @@ -21,12 +21,12 @@ | |
| 21 | |
| 22 | Server (1) runs as a CGI script on a |
| 23 | <a href="http://www.linode.com/">Linode 1024</a> located in Dallas, TX |
| 24 | - on the same virtual machine that |
| 25 | hosts <a href="http://www.sqlite.org/">SQLite</a> and over a |
| 26 | dozen other smaller projects. This demonstrates that Fossil can run on |
| 27 | a low-power host processor. |
| 28 | Multiple fossil-based projects can easily be hosted on the same machine, |
| 29 | even if that machine is itself one of several dozen virtual machines on |
| 30 | single physical box. The CGI script that runs the canonical Fossil |
| 31 | self-hosting repository is as follows: |
| 32 | |
| @@ -38,11 +38,12 @@ | |
| 38 | Server (3) runs as a CGI script on a shared hosting account at |
| 39 | <a href="http://www.he.net/">Hurricane Electric</a> in Fremont, CA. |
| 40 | This server demonstrates the ability of |
| 41 | Fossil to run on an economical shared-host web account with no |
| 42 | privileges beyond port 80 HTTP access and CGI. It is not necessary |
| 43 | to have a dedicated computer with administrator to run Fossil. |
| 44 | As far as we are aware, |
| 45 | Fossil is the only full-featured configuration management system |
| 46 | that can run in |
| 47 | such a restricted environment. The CGI script that runs on the |
| 48 | Hurricane Electric server is the same as the CGI script shown above, |
| 49 | except that the pathnames are modified to suit the environment: |
| 50 |
+214
-89
| --- www/server.wiki | ||
| +++ www/server.wiki | ||
| @@ -1,124 +1,249 @@ | ||
| 1 | 1 | <title>How To Configure A Fossil Server</title> |
| 2 | -<nowiki> | |
| 3 | -<p>This guide is intended to help guide you in setting up a Fossil server.</p> | |
| 4 | - | |
| 2 | +<h2>Introduction</h2><blockquote> | |
| 3 | +<p>A server is not necessary to use Fossil, but a server does help in collaborating with | |
| 4 | +peers. A Fossil server also works well as a complete website for a project. | |
| 5 | +For example, the complete <b>http://www.fossil-scm.org/</b> website, including the | |
| 6 | +page you are now reading (but excepting the download page), | |
| 7 | +is just a Fossil server displaying the content of the | |
| 8 | +self-hosting repository for Fossil.</p> | |
| 9 | +<p>This article is a guide for setting up your own Fossil server.</p></blockquote> | |
| 10 | +<h2>Overview</h2><blockquote> | |
| 11 | +There are basically four ways to set up a Fossil server: | |
| 12 | +<ol> | |
| 13 | +<li>A stand-alone server | |
| 14 | +<li>Using inetd or xinetd or stunnel | |
| 15 | +<li>CGI | |
| 16 | +<li>SCGI (a.k.a. SimpleCGI) | |
| 17 | +</ol> | |
| 18 | +Each of these can serve either a single repository, or a directory hierarchy | |
| 19 | +containing many repositories with names ending in ".fossil". | |
| 20 | +</blockquote> | |
| 21 | +<a name="standalone"></a> | |
| 5 | 22 | <h2>Standalone server</h2><blockquote> |
| 6 | -The easiest way to set up a Fossil server is to use the <tt>server</tt> or | |
| 7 | -<tt>ui</tt> command. Assuming the repository you are interested in serving is | |
| 8 | -in the file "<tt>repo.fossil</tt>", you can use either of these commands to | |
| 9 | -start Fossil as a server: | |
| 23 | +The easiest way to set up a Fossil server is to use either the | |
| 24 | +[/help/server|server] or the [/help/ui|ui] commands: | |
| 10 | 25 | <ul> |
| 11 | -<li><tt>fossil server repo.fossil</tt> | |
| 12 | -<li><tt>fossil ui repo.fossil</tt> | |
| 26 | +<li><b>fossil server</b> <i>REPOSITORY</i> | |
| 27 | +<li><b>fossil ui</b> <i>REPOSITORY</i> | |
| 13 | 28 | </ul> |
| 14 | - | |
| 15 | 29 | <p> |
| 16 | -Both of these commands start a Fossil server on port 8080 on the local machine, | |
| 17 | -which can be accessed with the URL: <tt>http://localhost:8080/</tt> using any | |
| 18 | -handy web browser. The difference between the two commands is that "ui", in | |
| 19 | -addition to starting the Fossil server, also starts a web browser and points it | |
| 20 | -to the URL mentioned above. On the other hand, the "ui" command binds to | |
| 21 | -the loopback IP address only (127.0.0.1) so that the "ui" command cannot be | |
| 30 | +The <i>REPOSITORY</i> argument is either the name of the repository file, or | |
| 31 | +a directory containing many repositories. | |
| 32 | +Both of these commands start a Fossil server, usually on TCP port 8080, though | |
| 33 | +a higher numbered port might also be used if 8080 is already occupied. You can | |
| 34 | +access these using URLs of the form <b>http://localhost:8080/</b>, or if | |
| 35 | +<i>REPOSITORY</i> is a directory, URLs of the form | |
| 36 | +<b>http://localhost:8080/</b><i>repo</i><b>/</b> where <i>repo</i> is the base | |
| 37 | +name of the repository file without the ".fossil" suffix. | |
| 38 | +The difference between "ui" and "server" is that "ui" will | |
| 39 | +also start a web browser and points it | |
| 40 | +to the URL mentioned above, and "ui" command binds to | |
| 41 | +the loopback IP address (127.0.0.1) only so that the "ui" command cannot be | |
| 22 | 42 | used to serve content to a different machine. |
| 23 | 43 | </p> |
| 24 | 44 | <p> |
| 25 | -NOTES: | |
| 26 | -<ol> | |
| 27 | -<li>The option "--port NNN" will start the server on port "NNN" instead of 8080. | |
| 28 | -<li>If port 8080 is already being used (perhaps by another Fossil server), then | |
| 29 | -Fossil will use the next available port number. | |
| 30 | -<li>Starting either command from within an "open" Fossil checkout will start a | |
| 31 | -server using the repository corresponding to the checkout. | |
| 32 | -<li>This is an excellent option for quickly sharing with coworkers on a small network. | |
| 33 | -<li>A huge advantage to this deployment scenario is that no special "root" or "administrator" access is required in order to share the repository. | |
| 34 | -</ol> | |
| 35 | -</p> | |
| 36 | -</blockquote> | |
| 37 | - | |
| 38 | -<h2>Fossil as an ''inetd'' service</h2><blockquote> | |
| 39 | -<p> | |
| 40 | -Modify your <tt>/etc/inetd.conf</tt> (on Linux, modify as appropriate for your platform) so it contains a line like this: | |
| 41 | -<blockquote> | |
| 42 | -<tt> | |
| 43 | -12345 stream tcp nowait.1000 root /path-to/fossil /path-to/fossil http /other-path-to/repository | |
| 44 | -</tt> | |
| 45 | -</blockquote> | |
| 46 | -In this example, you are telling "inetd" that when an incoming connection on port "12345" happens, it should launch the binary "/path-to/fossil". Obviously you will need to modify the "path-to" parts as appropriate for your particular setup. | |
| 47 | -</p> | |
| 48 | -<p> | |
| 49 | -This is a more complex setup than the "standalone" server, but it has the advantage of only using system resources when an actual connection is attempted. If no-one ever connects to that port, a Fossil server will not (automatically) run. It has the disadvantage of requiring "root" access and therefore may not normally be available to lower-priced "shared" servers on the internet. | |
| 50 | -</p> | |
| 51 | -</blockquote> | |
| 52 | - | |
| 53 | -<h2>Fossil as a ''CGI script''</h2><blockquote> | |
| 54 | -<p> | |
| 55 | -This is the most flexible and most likely to be widely usable of these deployment scenarios. In order for it to work, you must have the ability to install "CGI scripts" on the server you are interested in using. | |
| 56 | -</p> | |
| 57 | -</blockquote> | |
| 58 | - | |
| 59 | -<h3>One script per repository</h3><blockquote> | |
| 60 | -<p> | |
| 61 | -Create a script (let's call it 'repo') in your CGI directory which has content like this: | |
| 62 | -<blockquote><tt> | |
| 63 | -#!/path-to/fossil<br> | |
| 64 | -repository: /path-to-repo/repository | |
| 65 | -</tt></blockquote> | |
| 66 | -</p> | |
| 67 | -<p> | |
| 68 | -It may be necessary to set permissions properly, or to modify an ".htaccess" file or other server-specific things like that. Consult with your server provider if you need that sort of assistance. | |
| 69 | -</p> | |
| 70 | -<p> | |
| 71 | -Once the script is set up correctly, and assuming your server is also set correctly, you should be able to access your repository with a URL like: <tt>http://mydomain.org/cgi-bin/repo</tt> (assuming the "repo" script is accessible under "cgi-bin", which would be a typical deployment on Apache for instance). | |
| 72 | -</p> | |
| 73 | -</blockquote> | |
| 74 | - | |
| 75 | -<h3>Serving multiple repositories with one script</h3><blockquote> | |
| 76 | -<p> | |
| 77 | -This scenario is almost identical to the previous one. However, here we will assume you have multiple repositories, in one directory. | |
| 78 | -(Call the directory 'fossils'). All repositories served, in this case, must | |
| 79 | -use the ".fossil" filename suffix. | |
| 80 | -As before, create a script (again, 'repo'): | |
| 81 | -<blockquote><tt> | |
| 82 | -#!/path-to/fossil<br> | |
| 83 | -directory: /path-to-repo/fossils<br> | |
| 84 | -notfound: http://url-to-go-to-if-repo-not-found/ | |
| 85 | -</tt></blockquote> | |
| 86 | -</p> | |
| 87 | -<p> | |
| 88 | -Once deployed, a URL like: <tt>http://mydomain.org/cgi-bin/repo/XYZ</tt> will serve up the repository "fossils/XYX.fossil" (if it exists). This makes serving multiple projects on one server pretty painless. | |
| 45 | +If one of the commands above is run from within an option checkout, | |
| 46 | +then the <i>REPOSITORY</i> argument can be omitted and the checkout is used as | |
| 47 | +the repository. | |
| 48 | +</p> | |
| 49 | +<p> | |
| 50 | +Both commands have additional command-line options that can be used to refine | |
| 51 | +their behavior. See the [/help/server|online documentation] for an overview. | |
| 52 | +</p> | |
| 53 | +</blockquote> | |
| 54 | +<a name="inetd"></a> | |
| 55 | +<h2>Fossil as an inetd/xinetd or stunnel service</h2><blockquote> | |
| 56 | +<p> | |
| 57 | +A Fossil server can be launched on-demand by inetd or xinetd using | |
| 58 | +the [/help/http|fossil http] command. To launch Fossil from inetd, modify | |
| 59 | +your inetd configuration file (typically "/etc/inetd.conf") to contain a | |
| 60 | +line something like this: | |
| 61 | +<blockquote> | |
| 62 | +<pre> | |
| 63 | +12345 stream tcp nowait.1000 root /usr/bin/fossil /usr/bin/fossil http /home/fossil/repo.fossil | |
| 64 | +</pre> | |
| 65 | +</blockquote> | |
| 66 | +In this example, you are telling "inetd" that when an incoming connection | |
| 67 | +appears on port "12345", that it should launch the binary "/usr/bin/fossil" | |
| 68 | +program with the arguments shown. | |
| 69 | +Obviously you will | |
| 70 | +need to modify the pathnames parts as appropriate for your particular setup. | |
| 71 | +The final argument is either the name of the fossil repository to be served, | |
| 72 | +or a directory containing multiple repositories. | |
| 73 | +</p> | |
| 74 | +<p> | |
| 75 | +If you system is running xinetd, then the configuration is likely to be | |
| 76 | +in the file "/etc/xinetd.conf" or in a subfile of "/etc/xinetd.d". | |
| 77 | +An xinetd configuration file will appear like this:</p> | |
| 78 | +<blockquote> | |
| 79 | +<pre> | |
| 80 | +service http-alt | |
| 81 | +{ | |
| 82 | + port = 591 | |
| 83 | + socket_type = stream | |
| 84 | + wait = no | |
| 85 | + user = root | |
| 86 | + server = /usr/bin/fossil | |
| 87 | + server_args = http /home/fossil/repos/ | |
| 88 | +} | |
| 89 | +</pre> | |
| 90 | +</blockquote> | |
| 91 | +<p> | |
| 92 | +The xinetd example above has Fossil configured to serve multiple | |
| 93 | +repositories, contained under the "/home/fossil/repos/" directory. | |
| 94 | +</p> | |
| 95 | +<p> | |
| 96 | +In both cases notice that Fossil was launched as root. This is not required, | |
| 97 | +but if it is done, then Fossil will automatically put itself into a chroot | |
| 98 | +jail for the user who owns the fossil repository before reading any information | |
| 99 | +off of the wire. | |
| 100 | +</p> | |
| 101 | +<p> | |
| 102 | +[http://www.stunnel.org/ | Stunnel version 4] is an inetd-like process that | |
| 103 | +accepts and decodes SSL-encrypted connections. Fossil can be run directly from | |
| 104 | +stunnel in a mannar similar to inetd and xinetd. This can be used to provide | |
| 105 | +a secure link to a Fossil project. The configuration needed to get stunnel4 | |
| 106 | +to invoke Fossil is very similar to the inetd and xinetd examples shown above. | |
| 107 | +See the stunnel4 documentation for details. | |
| 108 | +<p> | |
| 109 | +Using inetd or xinetd or stunnel is a more complex setup | |
| 110 | +than the "standalone" server, but it has the | |
| 111 | +advantage of only using system resources when an actual connection is | |
| 112 | +attempted. If no-one ever connects to that port, a Fossil server will | |
| 113 | +not (automatically) run. It has the disadvantage of requiring "root" access | |
| 114 | +and therefore may not normally be available to lower-priced "shared" servers | |
| 115 | +on the internet. | |
| 116 | +</p> | |
| 117 | +</blockquote> | |
| 118 | +<a name="cgi"></a> | |
| 119 | +<h2>Fossil as CGI</h2><blockquote> | |
| 120 | +<p> | |
| 121 | +A Fossil server can also be run from an ordinary web server as a CGI program. | |
| 122 | +This feature allows Fossil to be seamlessly integrated into a larger website. | |
| 123 | +CGI is how the [./selfhost.wiki | self-hosting fossil repositories] are | |
| 124 | +implemented. | |
| 125 | +</p> | |
| 126 | +<p> | |
| 127 | +To run Fossil as CGI, create a CGI script (here called "repo") in the CGI directory | |
| 128 | +of your web server and having content like this: | |
| 129 | +<blockquote><pre> | |
| 130 | +#!/usr/bin/fossil | |
| 131 | +repository: /home/fossil/repo.fossil | |
| 132 | +</pre></blockquote> | |
| 133 | +</p> | |
| 134 | +<p> | |
| 135 | +As always, adjust your paths appropriately. | |
| 136 | +It may be necessary to set permissions properly, or to modify an ".htaccess" | |
| 137 | +file or other server-specific things like that. Consult the documentation | |
| 138 | +for your particular server. | |
| 139 | +</p> | |
| 140 | +<p> | |
| 141 | +Once the script is set up correctly, and assuming your server is also set | |
| 142 | +correctly, you should be able to access your repository with a URL like: | |
| 143 | +<b>http://mydomain.org/cgi-bin/repo</b> (assuming the "repo" script is | |
| 144 | +accessible under "cgi-bin", which would be a typical deployment on Apache | |
| 145 | +for instance). | |
| 146 | +</p> | |
| 147 | +<p> | |
| 148 | +To server multiple repositories from a directory using CGI, use the "directory:" | |
| 149 | +tag in the CGI script rather than "repository:". You might also want to add | |
| 150 | +a "notfound:" tag to tell where to redirect if the particular repository requested | |
| 151 | +by the URL is not found: | |
| 152 | +<blockquote><pre> | |
| 153 | +#!/usr/bin/fossil | |
| 154 | +directory: /home/fossil/repos | |
| 155 | +notfound: http://url-to-go-to-if-repo-not-found/ | |
| 156 | +</pre></blockquote> | |
| 157 | +</p> | |
| 158 | +<p> | |
| 159 | +Once deployed, a URL like: <b>http://mydomain.org/cgi-bin/repo/XYZ</b> | |
| 160 | +will serve up the repository "/home/fossil/repos/XYX.fossil" (if it exists). | |
| 161 | +</p> | |
| 162 | +</blockquote> | |
| 163 | + | |
| 164 | +<a name="scgi"></a> | |
| 165 | +<h2>Fossil as SCGI</h2><blockquote> | |
| 166 | + | |
| 167 | +<p> | |
| 168 | +The [/help/server|fossil server] command, described above as a way of | |
| 169 | +starting a stand-alone web server, can also be used for SCGI. Simply add | |
| 170 | +the --scgi command-line option and the stand-alone server will interpret | |
| 171 | +and respond to the SimpleCGI or SCGI protocol rather than raw HTTP. This can | |
| 172 | +be used in combination with a webserver (such as [http://nginx.org|Nginx]) | |
| 173 | +that does not support CGI. A typical Nginx configuration to support SCGI | |
| 174 | +with Fossil would look something like this: | |
| 175 | +<blockquote><pre> | |
| 176 | +location ~ ^/demo_project/ { | |
| 177 | + include scgi_params; | |
| 178 | + scgi_pass localhost:9000; | |
| 179 | + scgi_param SCRIPT_NAME "/demo_project"; | |
| 180 | +} | |
| 181 | +</pre></blockquote> | |
| 182 | +<p> | |
| 183 | +Note that Fossil requires the SCRIPT_NAME variable | |
| 184 | +in order to function properly, but Nginx does not provide this | |
| 185 | +variable by default. | |
| 186 | +So it is necessary to provide the SCRIPT_NAME parameter in the configuration. | |
| 187 | +Failure to do this will cause Fossil to return an error. | |
| 188 | +</p> | |
| 189 | +<p> | |
| 190 | +All of the features of the stand-alone server mode described above, | |
| 191 | +such as the ability to server a directory full of Fossil repositories | |
| 192 | +rather than just a single repository, work the same way in SCGI mode. | |
| 89 | 193 | </p> |
| 90 | 194 | </blockquote> |
| 91 | 195 | |
| 92 | 196 | <h2>Securing a repository with SSL</h2><blockquote> |
| 93 | 197 | <p> |
| 94 | -Using either of the CGI script approaches, it is trivial to use SSL to secure the server. Simply set up the Fossil CGI scripts etc. as above, but modify the Apache (or IIS, etc.) server to require SSL (that is, a URL with "https://") in order to access the CGI script directory. This may also be accomplished (on Apache, at least) using appropriate ".htaccess" rules. | |
| 198 | +Using either CGI or SCGI, it is trivial to use SSL to | |
| 199 | +secure the server. Simply set up the Fossil CGI scripts etc. as above, | |
| 200 | +but modify the Apache (or IIS, etc.) server to require SSL (that is, a | |
| 201 | +URL with "https://") in order to access the CGI script directory. This | |
| 202 | +may also be accomplished (on Apache, at least) using appropriate | |
| 203 | +".htaccess" rules. | |
| 95 | 204 | </p> |
| 96 | 205 | <p> |
| 97 | -If you are using "inetd" to serve your repository, then you simply need to add "/usr/bin/stunnel" (perhaps on a different path, depending on your setup) before the command line to launch Fossil. | |
| 206 | +If you are using "inetd" to serve your repository, then you simply need | |
| 207 | +to add "/usr/bin/stunnel" (perhaps on a different path, depending on your | |
| 208 | +setup) before the command line to launch Fossil. | |
| 98 | 209 | </p> |
| 99 | 210 | <p> |
| 100 | -At this stage, the standalone server (e.g. "fossil server") does not support SSL. | |
| 211 | +At this stage, the standalone server (e.g. "fossil server") does not | |
| 212 | +support SSL. | |
| 101 | 213 | </p> |
| 102 | 214 | <p> |
| 103 | 215 | For more information, see <a href="./ssl.wiki">Using SSL with Fossil</a>. |
| 104 | 216 | </p> |
| 105 | 217 | </blockquote> |
| 106 | 218 | |
| 107 | 219 | <h2>Various security concerns with hosted repositories</h2><blockquote> |
| 108 | 220 | <p> |
| 109 | -There are two main concerns relating to usage of Fossil for sharing sensitive information (source or any other data): | |
| 221 | +There are two main concerns relating to usage of Fossil for sharing | |
| 222 | +sensitive information (source or any other data): | |
| 110 | 223 | <ul> |
| 111 | -<li>Interception of the Fossil synchronization stream, thereby capturing data, and | |
| 224 | +<li>Interception of the Fossil synchronization stream, thereby capturing | |
| 225 | +data, and | |
| 112 | 226 | <li>Direct access to the Fossil repository on the server |
| 113 | 227 | </ul> |
| 114 | 228 | </p> |
| 115 | 229 | <p> |
| 116 | -Regarding the first, it is adequate to secure the server using SSL, and disallowing any non-SSL access. The data stream will be encrypted by the HTTPS protocol, rendering the data reasonably secure. The truly paranoid may wish to deploy <i>ssh</i> encrypted tunnels, but that is quite a bit more difficult and cumbersome to set up (particularly for a larger number of users). | |
| 230 | +Regarding the first, it is adequate to secure the server using SSL, and | |
| 231 | +disallowing any non-SSL access. The data stream will be encrypted by | |
| 232 | +the HTTPS protocol, rendering the data reasonably secure. The truly | |
| 233 | +paranoid may wish to deploy <i>ssh</i> encrypted tunnels, but that is | |
| 234 | +quite a bit more difficult and cumbersome to set up (particularly for | |
| 235 | +a larger number of users). | |
| 117 | 236 | </p> |
| 118 | 237 | <p> |
| 119 | -As far as direct access to the repository, the same steps must be taken as for any other internet-facing data-store. Access passwords to any disk-accessing accounts should be strong (and preferably changed from time to time). However, the data in the repository itself are <i>not</i> encrypted (unless you save encrypted data yourself), and so the system administrators of your server will be able to access your data (as with any hosting service setup). The only workaround in this case is to host the server yourself, in which case you will need to allocate resources to deal with administration issues. | |
| 238 | +As far as direct access to the repository, the same steps must be taken | |
| 239 | +as for any other internet-facing data-store. Access passwords to any | |
| 240 | +disk-accessing accounts should be strong (and preferably changed from | |
| 241 | +time to time). However, the data in the repository itself are <i>not</i> | |
| 242 | +encrypted (unless you save encrypted data yourself), and so the system | |
| 243 | +administrators of your server will be able to access your data (as with | |
| 244 | +any hosting service setup). The only workaround in this case is to | |
| 245 | +host the server yourself, in which case you will need to allocate | |
| 246 | +resources to deal with administration issues. | |
| 120 | 247 | </p> |
| 121 | 248 | |
| 122 | 249 | </blockquote> |
| 123 | - | |
| 124 | -</nowiki> | |
| 125 | 250 |
| --- www/server.wiki | |
| +++ www/server.wiki | |
| @@ -1,124 +1,249 @@ | |
| 1 | <title>How To Configure A Fossil Server</title> |
| 2 | <nowiki> |
| 3 | <p>This guide is intended to help guide you in setting up a Fossil server.</p> |
| 4 | |
| 5 | <h2>Standalone server</h2><blockquote> |
| 6 | The easiest way to set up a Fossil server is to use the <tt>server</tt> or |
| 7 | <tt>ui</tt> command. Assuming the repository you are interested in serving is |
| 8 | in the file "<tt>repo.fossil</tt>", you can use either of these commands to |
| 9 | start Fossil as a server: |
| 10 | <ul> |
| 11 | <li><tt>fossil server repo.fossil</tt> |
| 12 | <li><tt>fossil ui repo.fossil</tt> |
| 13 | </ul> |
| 14 | |
| 15 | <p> |
| 16 | Both of these commands start a Fossil server on port 8080 on the local machine, |
| 17 | which can be accessed with the URL: <tt>http://localhost:8080/</tt> using any |
| 18 | handy web browser. The difference between the two commands is that "ui", in |
| 19 | addition to starting the Fossil server, also starts a web browser and points it |
| 20 | to the URL mentioned above. On the other hand, the "ui" command binds to |
| 21 | the loopback IP address only (127.0.0.1) so that the "ui" command cannot be |
| 22 | used to serve content to a different machine. |
| 23 | </p> |
| 24 | <p> |
| 25 | NOTES: |
| 26 | <ol> |
| 27 | <li>The option "--port NNN" will start the server on port "NNN" instead of 8080. |
| 28 | <li>If port 8080 is already being used (perhaps by another Fossil server), then |
| 29 | Fossil will use the next available port number. |
| 30 | <li>Starting either command from within an "open" Fossil checkout will start a |
| 31 | server using the repository corresponding to the checkout. |
| 32 | <li>This is an excellent option for quickly sharing with coworkers on a small network. |
| 33 | <li>A huge advantage to this deployment scenario is that no special "root" or "administrator" access is required in order to share the repository. |
| 34 | </ol> |
| 35 | </p> |
| 36 | </blockquote> |
| 37 | |
| 38 | <h2>Fossil as an ''inetd'' service</h2><blockquote> |
| 39 | <p> |
| 40 | Modify your <tt>/etc/inetd.conf</tt> (on Linux, modify as appropriate for your platform) so it contains a line like this: |
| 41 | <blockquote> |
| 42 | <tt> |
| 43 | 12345 stream tcp nowait.1000 root /path-to/fossil /path-to/fossil http /other-path-to/repository |
| 44 | </tt> |
| 45 | </blockquote> |
| 46 | In this example, you are telling "inetd" that when an incoming connection on port "12345" happens, it should launch the binary "/path-to/fossil". Obviously you will need to modify the "path-to" parts as appropriate for your particular setup. |
| 47 | </p> |
| 48 | <p> |
| 49 | This is a more complex setup than the "standalone" server, but it has the advantage of only using system resources when an actual connection is attempted. If no-one ever connects to that port, a Fossil server will not (automatically) run. It has the disadvantage of requiring "root" access and therefore may not normally be available to lower-priced "shared" servers on the internet. |
| 50 | </p> |
| 51 | </blockquote> |
| 52 | |
| 53 | <h2>Fossil as a ''CGI script''</h2><blockquote> |
| 54 | <p> |
| 55 | This is the most flexible and most likely to be widely usable of these deployment scenarios. In order for it to work, you must have the ability to install "CGI scripts" on the server you are interested in using. |
| 56 | </p> |
| 57 | </blockquote> |
| 58 | |
| 59 | <h3>One script per repository</h3><blockquote> |
| 60 | <p> |
| 61 | Create a script (let's call it 'repo') in your CGI directory which has content like this: |
| 62 | <blockquote><tt> |
| 63 | #!/path-to/fossil<br> |
| 64 | repository: /path-to-repo/repository |
| 65 | </tt></blockquote> |
| 66 | </p> |
| 67 | <p> |
| 68 | It may be necessary to set permissions properly, or to modify an ".htaccess" file or other server-specific things like that. Consult with your server provider if you need that sort of assistance. |
| 69 | </p> |
| 70 | <p> |
| 71 | Once the script is set up correctly, and assuming your server is also set correctly, you should be able to access your repository with a URL like: <tt>http://mydomain.org/cgi-bin/repo</tt> (assuming the "repo" script is accessible under "cgi-bin", which would be a typical deployment on Apache for instance). |
| 72 | </p> |
| 73 | </blockquote> |
| 74 | |
| 75 | <h3>Serving multiple repositories with one script</h3><blockquote> |
| 76 | <p> |
| 77 | This scenario is almost identical to the previous one. However, here we will assume you have multiple repositories, in one directory. |
| 78 | (Call the directory 'fossils'). All repositories served, in this case, must |
| 79 | use the ".fossil" filename suffix. |
| 80 | As before, create a script (again, 'repo'): |
| 81 | <blockquote><tt> |
| 82 | #!/path-to/fossil<br> |
| 83 | directory: /path-to-repo/fossils<br> |
| 84 | notfound: http://url-to-go-to-if-repo-not-found/ |
| 85 | </tt></blockquote> |
| 86 | </p> |
| 87 | <p> |
| 88 | Once deployed, a URL like: <tt>http://mydomain.org/cgi-bin/repo/XYZ</tt> will serve up the repository "fossils/XYX.fossil" (if it exists). This makes serving multiple projects on one server pretty painless. |
| 89 | </p> |
| 90 | </blockquote> |
| 91 | |
| 92 | <h2>Securing a repository with SSL</h2><blockquote> |
| 93 | <p> |
| 94 | Using either of the CGI script approaches, it is trivial to use SSL to secure the server. Simply set up the Fossil CGI scripts etc. as above, but modify the Apache (or IIS, etc.) server to require SSL (that is, a URL with "https://") in order to access the CGI script directory. This may also be accomplished (on Apache, at least) using appropriate ".htaccess" rules. |
| 95 | </p> |
| 96 | <p> |
| 97 | If you are using "inetd" to serve your repository, then you simply need to add "/usr/bin/stunnel" (perhaps on a different path, depending on your setup) before the command line to launch Fossil. |
| 98 | </p> |
| 99 | <p> |
| 100 | At this stage, the standalone server (e.g. "fossil server") does not support SSL. |
| 101 | </p> |
| 102 | <p> |
| 103 | For more information, see <a href="./ssl.wiki">Using SSL with Fossil</a>. |
| 104 | </p> |
| 105 | </blockquote> |
| 106 | |
| 107 | <h2>Various security concerns with hosted repositories</h2><blockquote> |
| 108 | <p> |
| 109 | There are two main concerns relating to usage of Fossil for sharing sensitive information (source or any other data): |
| 110 | <ul> |
| 111 | <li>Interception of the Fossil synchronization stream, thereby capturing data, and |
| 112 | <li>Direct access to the Fossil repository on the server |
| 113 | </ul> |
| 114 | </p> |
| 115 | <p> |
| 116 | Regarding the first, it is adequate to secure the server using SSL, and disallowing any non-SSL access. The data stream will be encrypted by the HTTPS protocol, rendering the data reasonably secure. The truly paranoid may wish to deploy <i>ssh</i> encrypted tunnels, but that is quite a bit more difficult and cumbersome to set up (particularly for a larger number of users). |
| 117 | </p> |
| 118 | <p> |
| 119 | As far as direct access to the repository, the same steps must be taken as for any other internet-facing data-store. Access passwords to any disk-accessing accounts should be strong (and preferably changed from time to time). However, the data in the repository itself are <i>not</i> encrypted (unless you save encrypted data yourself), and so the system administrators of your server will be able to access your data (as with any hosting service setup). The only workaround in this case is to host the server yourself, in which case you will need to allocate resources to deal with administration issues. |
| 120 | </p> |
| 121 | |
| 122 | </blockquote> |
| 123 | |
| 124 | </nowiki> |
| 125 |
| --- www/server.wiki | |
| +++ www/server.wiki | |
| @@ -1,124 +1,249 @@ | |
| 1 | <title>How To Configure A Fossil Server</title> |
| 2 | <h2>Introduction</h2><blockquote> |
| 3 | <p>A server is not necessary to use Fossil, but a server does help in collaborating with |
| 4 | peers. A Fossil server also works well as a complete website for a project. |
| 5 | For example, the complete <b>http://www.fossil-scm.org/</b> website, including the |
| 6 | page you are now reading (but excepting the download page), |
| 7 | is just a Fossil server displaying the content of the |
| 8 | self-hosting repository for Fossil.</p> |
| 9 | <p>This article is a guide for setting up your own Fossil server.</p></blockquote> |
| 10 | <h2>Overview</h2><blockquote> |
| 11 | There are basically four ways to set up a Fossil server: |
| 12 | <ol> |
| 13 | <li>A stand-alone server |
| 14 | <li>Using inetd or xinetd or stunnel |
| 15 | <li>CGI |
| 16 | <li>SCGI (a.k.a. SimpleCGI) |
| 17 | </ol> |
| 18 | Each of these can serve either a single repository, or a directory hierarchy |
| 19 | containing many repositories with names ending in ".fossil". |
| 20 | </blockquote> |
| 21 | <a name="standalone"></a> |
| 22 | <h2>Standalone server</h2><blockquote> |
| 23 | The easiest way to set up a Fossil server is to use either the |
| 24 | [/help/server|server] or the [/help/ui|ui] commands: |
| 25 | <ul> |
| 26 | <li><b>fossil server</b> <i>REPOSITORY</i> |
| 27 | <li><b>fossil ui</b> <i>REPOSITORY</i> |
| 28 | </ul> |
| 29 | <p> |
| 30 | The <i>REPOSITORY</i> argument is either the name of the repository file, or |
| 31 | a directory containing many repositories. |
| 32 | Both of these commands start a Fossil server, usually on TCP port 8080, though |
| 33 | a higher numbered port might also be used if 8080 is already occupied. You can |
| 34 | access these using URLs of the form <b>http://localhost:8080/</b>, or if |
| 35 | <i>REPOSITORY</i> is a directory, URLs of the form |
| 36 | <b>http://localhost:8080/</b><i>repo</i><b>/</b> where <i>repo</i> is the base |
| 37 | name of the repository file without the ".fossil" suffix. |
| 38 | The difference between "ui" and "server" is that "ui" will |
| 39 | also start a web browser and points it |
| 40 | to the URL mentioned above, and "ui" command binds to |
| 41 | the loopback IP address (127.0.0.1) only so that the "ui" command cannot be |
| 42 | used to serve content to a different machine. |
| 43 | </p> |
| 44 | <p> |
| 45 | If one of the commands above is run from within an option checkout, |
| 46 | then the <i>REPOSITORY</i> argument can be omitted and the checkout is used as |
| 47 | the repository. |
| 48 | </p> |
| 49 | <p> |
| 50 | Both commands have additional command-line options that can be used to refine |
| 51 | their behavior. See the [/help/server|online documentation] for an overview. |
| 52 | </p> |
| 53 | </blockquote> |
| 54 | <a name="inetd"></a> |
| 55 | <h2>Fossil as an inetd/xinetd or stunnel service</h2><blockquote> |
| 56 | <p> |
| 57 | A Fossil server can be launched on-demand by inetd or xinetd using |
| 58 | the [/help/http|fossil http] command. To launch Fossil from inetd, modify |
| 59 | your inetd configuration file (typically "/etc/inetd.conf") to contain a |
| 60 | line something like this: |
| 61 | <blockquote> |
| 62 | <pre> |
| 63 | 12345 stream tcp nowait.1000 root /usr/bin/fossil /usr/bin/fossil http /home/fossil/repo.fossil |
| 64 | </pre> |
| 65 | </blockquote> |
| 66 | In this example, you are telling "inetd" that when an incoming connection |
| 67 | appears on port "12345", that it should launch the binary "/usr/bin/fossil" |
| 68 | program with the arguments shown. |
| 69 | Obviously you will |
| 70 | need to modify the pathnames parts as appropriate for your particular setup. |
| 71 | The final argument is either the name of the fossil repository to be served, |
| 72 | or a directory containing multiple repositories. |
| 73 | </p> |
| 74 | <p> |
| 75 | If you system is running xinetd, then the configuration is likely to be |
| 76 | in the file "/etc/xinetd.conf" or in a subfile of "/etc/xinetd.d". |
| 77 | An xinetd configuration file will appear like this:</p> |
| 78 | <blockquote> |
| 79 | <pre> |
| 80 | service http-alt |
| 81 | { |
| 82 | port = 591 |
| 83 | socket_type = stream |
| 84 | wait = no |
| 85 | user = root |
| 86 | server = /usr/bin/fossil |
| 87 | server_args = http /home/fossil/repos/ |
| 88 | } |
| 89 | </pre> |
| 90 | </blockquote> |
| 91 | <p> |
| 92 | The xinetd example above has Fossil configured to serve multiple |
| 93 | repositories, contained under the "/home/fossil/repos/" directory. |
| 94 | </p> |
| 95 | <p> |
| 96 | In both cases notice that Fossil was launched as root. This is not required, |
| 97 | but if it is done, then Fossil will automatically put itself into a chroot |
| 98 | jail for the user who owns the fossil repository before reading any information |
| 99 | off of the wire. |
| 100 | </p> |
| 101 | <p> |
| 102 | [http://www.stunnel.org/ | Stunnel version 4] is an inetd-like process that |
| 103 | accepts and decodes SSL-encrypted connections. Fossil can be run directly from |
| 104 | stunnel in a mannar similar to inetd and xinetd. This can be used to provide |
| 105 | a secure link to a Fossil project. The configuration needed to get stunnel4 |
| 106 | to invoke Fossil is very similar to the inetd and xinetd examples shown above. |
| 107 | See the stunnel4 documentation for details. |
| 108 | <p> |
| 109 | Using inetd or xinetd or stunnel is a more complex setup |
| 110 | than the "standalone" server, but it has the |
| 111 | advantage of only using system resources when an actual connection is |
| 112 | attempted. If no-one ever connects to that port, a Fossil server will |
| 113 | not (automatically) run. It has the disadvantage of requiring "root" access |
| 114 | and therefore may not normally be available to lower-priced "shared" servers |
| 115 | on the internet. |
| 116 | </p> |
| 117 | </blockquote> |
| 118 | <a name="cgi"></a> |
| 119 | <h2>Fossil as CGI</h2><blockquote> |
| 120 | <p> |
| 121 | A Fossil server can also be run from an ordinary web server as a CGI program. |
| 122 | This feature allows Fossil to be seamlessly integrated into a larger website. |
| 123 | CGI is how the [./selfhost.wiki | self-hosting fossil repositories] are |
| 124 | implemented. |
| 125 | </p> |
| 126 | <p> |
| 127 | To run Fossil as CGI, create a CGI script (here called "repo") in the CGI directory |
| 128 | of your web server and having content like this: |
| 129 | <blockquote><pre> |
| 130 | #!/usr/bin/fossil |
| 131 | repository: /home/fossil/repo.fossil |
| 132 | </pre></blockquote> |
| 133 | </p> |
| 134 | <p> |
| 135 | As always, adjust your paths appropriately. |
| 136 | It may be necessary to set permissions properly, or to modify an ".htaccess" |
| 137 | file or other server-specific things like that. Consult the documentation |
| 138 | for your particular server. |
| 139 | </p> |
| 140 | <p> |
| 141 | Once the script is set up correctly, and assuming your server is also set |
| 142 | correctly, you should be able to access your repository with a URL like: |
| 143 | <b>http://mydomain.org/cgi-bin/repo</b> (assuming the "repo" script is |
| 144 | accessible under "cgi-bin", which would be a typical deployment on Apache |
| 145 | for instance). |
| 146 | </p> |
| 147 | <p> |
| 148 | To server multiple repositories from a directory using CGI, use the "directory:" |
| 149 | tag in the CGI script rather than "repository:". You might also want to add |
| 150 | a "notfound:" tag to tell where to redirect if the particular repository requested |
| 151 | by the URL is not found: |
| 152 | <blockquote><pre> |
| 153 | #!/usr/bin/fossil |
| 154 | directory: /home/fossil/repos |
| 155 | notfound: http://url-to-go-to-if-repo-not-found/ |
| 156 | </pre></blockquote> |
| 157 | </p> |
| 158 | <p> |
| 159 | Once deployed, a URL like: <b>http://mydomain.org/cgi-bin/repo/XYZ</b> |
| 160 | will serve up the repository "/home/fossil/repos/XYX.fossil" (if it exists). |
| 161 | </p> |
| 162 | </blockquote> |
| 163 | |
| 164 | <a name="scgi"></a> |
| 165 | <h2>Fossil as SCGI</h2><blockquote> |
| 166 | |
| 167 | <p> |
| 168 | The [/help/server|fossil server] command, described above as a way of |
| 169 | starting a stand-alone web server, can also be used for SCGI. Simply add |
| 170 | the --scgi command-line option and the stand-alone server will interpret |
| 171 | and respond to the SimpleCGI or SCGI protocol rather than raw HTTP. This can |
| 172 | be used in combination with a webserver (such as [http://nginx.org|Nginx]) |
| 173 | that does not support CGI. A typical Nginx configuration to support SCGI |
| 174 | with Fossil would look something like this: |
| 175 | <blockquote><pre> |
| 176 | location ~ ^/demo_project/ { |
| 177 | include scgi_params; |
| 178 | scgi_pass localhost:9000; |
| 179 | scgi_param SCRIPT_NAME "/demo_project"; |
| 180 | } |
| 181 | </pre></blockquote> |
| 182 | <p> |
| 183 | Note that Fossil requires the SCRIPT_NAME variable |
| 184 | in order to function properly, but Nginx does not provide this |
| 185 | variable by default. |
| 186 | So it is necessary to provide the SCRIPT_NAME parameter in the configuration. |
| 187 | Failure to do this will cause Fossil to return an error. |
| 188 | </p> |
| 189 | <p> |
| 190 | All of the features of the stand-alone server mode described above, |
| 191 | such as the ability to server a directory full of Fossil repositories |
| 192 | rather than just a single repository, work the same way in SCGI mode. |
| 193 | </p> |
| 194 | </blockquote> |
| 195 | |
| 196 | <h2>Securing a repository with SSL</h2><blockquote> |
| 197 | <p> |
| 198 | Using either CGI or SCGI, it is trivial to use SSL to |
| 199 | secure the server. Simply set up the Fossil CGI scripts etc. as above, |
| 200 | but modify the Apache (or IIS, etc.) server to require SSL (that is, a |
| 201 | URL with "https://") in order to access the CGI script directory. This |
| 202 | may also be accomplished (on Apache, at least) using appropriate |
| 203 | ".htaccess" rules. |
| 204 | </p> |
| 205 | <p> |
| 206 | If you are using "inetd" to serve your repository, then you simply need |
| 207 | to add "/usr/bin/stunnel" (perhaps on a different path, depending on your |
| 208 | setup) before the command line to launch Fossil. |
| 209 | </p> |
| 210 | <p> |
| 211 | At this stage, the standalone server (e.g. "fossil server") does not |
| 212 | support SSL. |
| 213 | </p> |
| 214 | <p> |
| 215 | For more information, see <a href="./ssl.wiki">Using SSL with Fossil</a>. |
| 216 | </p> |
| 217 | </blockquote> |
| 218 | |
| 219 | <h2>Various security concerns with hosted repositories</h2><blockquote> |
| 220 | <p> |
| 221 | There are two main concerns relating to usage of Fossil for sharing |
| 222 | sensitive information (source or any other data): |
| 223 | <ul> |
| 224 | <li>Interception of the Fossil synchronization stream, thereby capturing |
| 225 | data, and |
| 226 | <li>Direct access to the Fossil repository on the server |
| 227 | </ul> |
| 228 | </p> |
| 229 | <p> |
| 230 | Regarding the first, it is adequate to secure the server using SSL, and |
| 231 | disallowing any non-SSL access. The data stream will be encrypted by |
| 232 | the HTTPS protocol, rendering the data reasonably secure. The truly |
| 233 | paranoid may wish to deploy <i>ssh</i> encrypted tunnels, but that is |
| 234 | quite a bit more difficult and cumbersome to set up (particularly for |
| 235 | a larger number of users). |
| 236 | </p> |
| 237 | <p> |
| 238 | As far as direct access to the repository, the same steps must be taken |
| 239 | as for any other internet-facing data-store. Access passwords to any |
| 240 | disk-accessing accounts should be strong (and preferably changed from |
| 241 | time to time). However, the data in the repository itself are <i>not</i> |
| 242 | encrypted (unless you save encrypted data yourself), and so the system |
| 243 | administrators of your server will be able to access your data (as with |
| 244 | any hosting service setup). The only workaround in this case is to |
| 245 | host the server yourself, in which case you will need to allocate |
| 246 | resources to deal with administration issues. |
| 247 | </p> |
| 248 | |
| 249 | </blockquote> |
| 250 |
+9
-3
| --- www/webui.wiki | ||
| +++ www/webui.wiki | ||
| @@ -67,11 +67,12 @@ | ||
| 67 | 67 | URL. So there is never any fumbling around trying to find an open |
| 68 | 68 | port or to type arcane strings into your browser URL entry box. |
| 69 | 69 | The interface just pops right up, ready to run. |
| 70 | 70 | |
| 71 | 71 | The Fossil web interface is also very easy to setup and run on a |
| 72 | -network server, as either a CGI program or from inetd. Details on how | |
| 72 | +network server, as either a CGI program or from inetd, or as an | |
| 73 | +SCGI server. Details on how | |
| 73 | 74 | to do that are described further below. |
| 74 | 75 | |
| 75 | 76 | <h2>Things To Do Using The Web Interface</h2> |
| 76 | 77 | |
| 77 | 78 | You can view <b>timelines</b> of changes to the project. The default |
| @@ -135,11 +136,12 @@ | ||
| 135 | 136 | <h2>Installing On A Network Server</h2> |
| 136 | 137 | |
| 137 | 138 | When you create a new Fossil project and after you have configured it |
| 138 | 139 | like you want it using the web interface, you can make the project |
| 139 | 140 | available to a distributed team by simply copying the single |
| 140 | -repository file up to a web server that supports CGI. Just put the | |
| 141 | +repository file up to a web server that supports CGI or SCGI. To | |
| 142 | +run Fossil as CGI, just put the | |
| 141 | 143 | <b>sample-project.fossil</b> file in a directory where CGI scripts |
| 142 | 144 | have both read and write permission on the file and the directory that |
| 143 | 145 | contains the file, then add a CGI script that looks something like this: |
| 144 | 146 | |
| 145 | 147 | <verbatim> |
| @@ -151,11 +153,15 @@ | ||
| 151 | 153 | of course, and also make sure the Fossil binary is installed on the server. |
| 152 | 154 | But that is <u>all</u> you have to do. You now have everything you need to host |
| 153 | 155 | a distributed software development project in less than five minutes using a |
| 154 | 156 | two-line CGI script. |
| 155 | 157 | |
| 156 | -You don't have a CGI-capable web server running on your server machine? | |
| 158 | +Instructions for setting up an SCGI server are | |
| 159 | +[./scgi.wiki | available separately]. | |
| 160 | + | |
| 161 | +You don't have a CGI- or SCGI-capable web server running on your | |
| 162 | +server machine? | |
| 157 | 163 | Not a problem. The Fossil interface can also be launched via inetd or |
| 158 | 164 | xinetd. An inetd configuration line sufficient to launch the Fossil |
| 159 | 165 | web interface looks like this: |
| 160 | 166 | |
| 161 | 167 | <verbatim> |
| 162 | 168 |
| --- www/webui.wiki | |
| +++ www/webui.wiki | |
| @@ -67,11 +67,12 @@ | |
| 67 | URL. So there is never any fumbling around trying to find an open |
| 68 | port or to type arcane strings into your browser URL entry box. |
| 69 | The interface just pops right up, ready to run. |
| 70 | |
| 71 | The Fossil web interface is also very easy to setup and run on a |
| 72 | network server, as either a CGI program or from inetd. Details on how |
| 73 | to do that are described further below. |
| 74 | |
| 75 | <h2>Things To Do Using The Web Interface</h2> |
| 76 | |
| 77 | You can view <b>timelines</b> of changes to the project. The default |
| @@ -135,11 +136,12 @@ | |
| 135 | <h2>Installing On A Network Server</h2> |
| 136 | |
| 137 | When you create a new Fossil project and after you have configured it |
| 138 | like you want it using the web interface, you can make the project |
| 139 | available to a distributed team by simply copying the single |
| 140 | repository file up to a web server that supports CGI. Just put the |
| 141 | <b>sample-project.fossil</b> file in a directory where CGI scripts |
| 142 | have both read and write permission on the file and the directory that |
| 143 | contains the file, then add a CGI script that looks something like this: |
| 144 | |
| 145 | <verbatim> |
| @@ -151,11 +153,15 @@ | |
| 151 | of course, and also make sure the Fossil binary is installed on the server. |
| 152 | But that is <u>all</u> you have to do. You now have everything you need to host |
| 153 | a distributed software development project in less than five minutes using a |
| 154 | two-line CGI script. |
| 155 | |
| 156 | You don't have a CGI-capable web server running on your server machine? |
| 157 | Not a problem. The Fossil interface can also be launched via inetd or |
| 158 | xinetd. An inetd configuration line sufficient to launch the Fossil |
| 159 | web interface looks like this: |
| 160 | |
| 161 | <verbatim> |
| 162 |
| --- www/webui.wiki | |
| +++ www/webui.wiki | |
| @@ -67,11 +67,12 @@ | |
| 67 | URL. So there is never any fumbling around trying to find an open |
| 68 | port or to type arcane strings into your browser URL entry box. |
| 69 | The interface just pops right up, ready to run. |
| 70 | |
| 71 | The Fossil web interface is also very easy to setup and run on a |
| 72 | network server, as either a CGI program or from inetd, or as an |
| 73 | SCGI server. Details on how |
| 74 | to do that are described further below. |
| 75 | |
| 76 | <h2>Things To Do Using The Web Interface</h2> |
| 77 | |
| 78 | You can view <b>timelines</b> of changes to the project. The default |
| @@ -135,11 +136,12 @@ | |
| 136 | <h2>Installing On A Network Server</h2> |
| 137 | |
| 138 | When you create a new Fossil project and after you have configured it |
| 139 | like you want it using the web interface, you can make the project |
| 140 | available to a distributed team by simply copying the single |
| 141 | repository file up to a web server that supports CGI or SCGI. To |
| 142 | run Fossil as CGI, just put the |
| 143 | <b>sample-project.fossil</b> file in a directory where CGI scripts |
| 144 | have both read and write permission on the file and the directory that |
| 145 | contains the file, then add a CGI script that looks something like this: |
| 146 | |
| 147 | <verbatim> |
| @@ -151,11 +153,15 @@ | |
| 153 | of course, and also make sure the Fossil binary is installed on the server. |
| 154 | But that is <u>all</u> you have to do. You now have everything you need to host |
| 155 | a distributed software development project in less than five minutes using a |
| 156 | two-line CGI script. |
| 157 | |
| 158 | Instructions for setting up an SCGI server are |
| 159 | [./scgi.wiki | available separately]. |
| 160 | |
| 161 | You don't have a CGI- or SCGI-capable web server running on your |
| 162 | server machine? |
| 163 | Not a problem. The Fossil interface can also be launched via inetd or |
| 164 | xinetd. An inetd configuration line sufficient to launch the Fossil |
| 165 | web interface looks like this: |
| 166 | |
| 167 | <verbatim> |
| 168 |