Fossil SCM
style.css now checks for a builtin file named after the first path component of the referer (sic), rather than PD("name"), however, we still have to emit style.css/NAME in order to pick up the the name-specific CSS, otherwise /style.css?id=... is the same for all pages and a page with its own style may pick up a cached copy without its own styles, or with the styles from another page.
Commit
5abc0f6e79dfbfc5bd9b57377002805227b02e2d4a01f24c294b2181a1f202c2
Parent
eddb5ac5528bf09…
3 files changed
+36
+2
-2
+20
-1
+36
| --- src/cgi.c | ||
| +++ src/cgi.c | ||
| @@ -447,10 +447,46 @@ | ||
| 447 | 447 | zRef = P("HTTP_REFERER"); |
| 448 | 448 | if( zRef==0 ) zRef = zDefault; |
| 449 | 449 | } |
| 450 | 450 | return zRef; |
| 451 | 451 | } |
| 452 | + | |
| 453 | +/* | |
| 454 | +** If cgi_referer(0) returns a non-0 AND the referrer is from the same | |
| 455 | +** fossil app path (i.e. the referrer's path starts with g.zTop), this | |
| 456 | +** function returns the first path element of the referring page, up | |
| 457 | +** to, but not including, the first slash. Thus if he refer[r]er is | |
| 458 | +** https://foo.com/fossil.cgi/foo/bar, this returns "foo". The | |
| 459 | +** returned memory is malloc'd and needs to be freed by the caller. | |
| 460 | +*/ | |
| 461 | +char * cgi_referer_fossil_page_name(){ | |
| 462 | + UrlData url; | |
| 463 | + char * zPage = 0; | |
| 464 | + const char * zRef = cgi_referer(0); | |
| 465 | + | |
| 466 | + if(zRef==0) return 0; | |
| 467 | + memset(&url, 0, sizeof(url)); | |
| 468 | + url_parse_local(zRef, 0, &url); | |
| 469 | + if(url.path==strstr(url.path, g.zTop)){ | |
| 470 | + /* g.zTop is, e.g., /cgi-bin/fossil.cgi, | |
| 471 | + url.path is, e.g., /cgi-bin/fossil.cgi/page/... */ | |
| 472 | + char * zSlash = 0; | |
| 473 | + zPage = url.path + strlen(g.zTop); | |
| 474 | + if('/' == zPage[0]){ | |
| 475 | + *zPage++ = 0; | |
| 476 | + if((zSlash = strstr(zPage,"/"))!=0){ | |
| 477 | + *zSlash = 0; | |
| 478 | + } | |
| 479 | + zPage = mprintf("%s", zPage); | |
| 480 | + }else{ /*unexpected result*/ | |
| 481 | + zPage = 0; | |
| 482 | + } | |
| 483 | + } | |
| 484 | + url_cleanup(&url); | |
| 485 | + return zPage; | |
| 486 | +} | |
| 487 | + | |
| 452 | 488 | |
| 453 | 489 | /* |
| 454 | 490 | ** Return true if the current request appears to be safe from a |
| 455 | 491 | ** Cross-Site Request Forgery (CSRF) attack. Conditions that must |
| 456 | 492 | ** be met: |
| 457 | 493 |
| --- src/cgi.c | |
| +++ src/cgi.c | |
| @@ -447,10 +447,46 @@ | |
| 447 | zRef = P("HTTP_REFERER"); |
| 448 | if( zRef==0 ) zRef = zDefault; |
| 449 | } |
| 450 | return zRef; |
| 451 | } |
| 452 | |
| 453 | /* |
| 454 | ** Return true if the current request appears to be safe from a |
| 455 | ** Cross-Site Request Forgery (CSRF) attack. Conditions that must |
| 456 | ** be met: |
| 457 |
| --- src/cgi.c | |
| +++ src/cgi.c | |
| @@ -447,10 +447,46 @@ | |
| 447 | zRef = P("HTTP_REFERER"); |
| 448 | if( zRef==0 ) zRef = zDefault; |
| 449 | } |
| 450 | return zRef; |
| 451 | } |
| 452 | |
| 453 | /* |
| 454 | ** If cgi_referer(0) returns a non-0 AND the referrer is from the same |
| 455 | ** fossil app path (i.e. the referrer's path starts with g.zTop), this |
| 456 | ** function returns the first path element of the referring page, up |
| 457 | ** to, but not including, the first slash. Thus if he refer[r]er is |
| 458 | ** https://foo.com/fossil.cgi/foo/bar, this returns "foo". The |
| 459 | ** returned memory is malloc'd and needs to be freed by the caller. |
| 460 | */ |
| 461 | char * cgi_referer_fossil_page_name(){ |
| 462 | UrlData url; |
| 463 | char * zPage = 0; |
| 464 | const char * zRef = cgi_referer(0); |
| 465 | |
| 466 | if(zRef==0) return 0; |
| 467 | memset(&url, 0, sizeof(url)); |
| 468 | url_parse_local(zRef, 0, &url); |
| 469 | if(url.path==strstr(url.path, g.zTop)){ |
| 470 | /* g.zTop is, e.g., /cgi-bin/fossil.cgi, |
| 471 | url.path is, e.g., /cgi-bin/fossil.cgi/page/... */ |
| 472 | char * zSlash = 0; |
| 473 | zPage = url.path + strlen(g.zTop); |
| 474 | if('/' == zPage[0]){ |
| 475 | *zPage++ = 0; |
| 476 | if((zSlash = strstr(zPage,"/"))!=0){ |
| 477 | *zSlash = 0; |
| 478 | } |
| 479 | zPage = mprintf("%s", zPage); |
| 480 | }else{ /*unexpected result*/ |
| 481 | zPage = 0; |
| 482 | } |
| 483 | } |
| 484 | url_cleanup(&url); |
| 485 | return zPage; |
| 486 | } |
| 487 | |
| 488 | |
| 489 | /* |
| 490 | ** Return true if the current request appears to be safe from a |
| 491 | ** Cross-Site Request Forgery (CSRF) attack. Conditions that must |
| 492 | ** be met: |
| 493 |
+2
-2
| --- src/style.c | ||
| +++ src/style.c | ||
| @@ -1071,20 +1071,19 @@ | ||
| 1071 | 1071 | } |
| 1072 | 1072 | style_init_th1_vars(0); |
| 1073 | 1073 | Th_Render(zScript?zScript:""); |
| 1074 | 1074 | } |
| 1075 | 1075 | |
| 1076 | - | |
| 1077 | 1076 | /* |
| 1078 | 1077 | ** WEBPAGE: style.css |
| 1079 | 1078 | ** |
| 1080 | 1079 | ** Return the style sheet. |
| 1081 | 1080 | */ |
| 1082 | 1081 | void page_style_css(void){ |
| 1083 | 1082 | Blob css = empty_blob; |
| 1084 | 1083 | int i; |
| 1085 | - const char *zPage = P("name"); | |
| 1084 | + char *zPage = cgi_referer_fossil_page_name(); | |
| 1086 | 1085 | |
| 1087 | 1086 | cgi_set_content_type("text/css"); |
| 1088 | 1087 | /* Emit all default rules... */ |
| 1089 | 1088 | for(i=1; cssDefaultList[i].elementClass; i++){ |
| 1090 | 1089 | char *z = blob_str(&css); |
| @@ -1118,10 +1117,11 @@ | ||
| 1118 | 1117 | "***********************************************************/\n", |
| 1119 | 1118 | zPage); |
| 1120 | 1119 | } |
| 1121 | 1120 | fossil_free(zFile); |
| 1122 | 1121 | } |
| 1122 | + fossil_free(zPage); | |
| 1123 | 1123 | blob_append(&css, |
| 1124 | 1124 | "\n/***********************************************************\n" |
| 1125 | 1125 | "** All CSS which follows is supplied by the repository \"skin\".\n" |
| 1126 | 1126 | "***********************************************************/\n", |
| 1127 | 1127 | -1); |
| 1128 | 1128 |
| --- src/style.c | |
| +++ src/style.c | |
| @@ -1071,20 +1071,19 @@ | |
| 1071 | } |
| 1072 | style_init_th1_vars(0); |
| 1073 | Th_Render(zScript?zScript:""); |
| 1074 | } |
| 1075 | |
| 1076 | |
| 1077 | /* |
| 1078 | ** WEBPAGE: style.css |
| 1079 | ** |
| 1080 | ** Return the style sheet. |
| 1081 | */ |
| 1082 | void page_style_css(void){ |
| 1083 | Blob css = empty_blob; |
| 1084 | int i; |
| 1085 | const char *zPage = P("name"); |
| 1086 | |
| 1087 | cgi_set_content_type("text/css"); |
| 1088 | /* Emit all default rules... */ |
| 1089 | for(i=1; cssDefaultList[i].elementClass; i++){ |
| 1090 | char *z = blob_str(&css); |
| @@ -1118,10 +1117,11 @@ | |
| 1118 | "***********************************************************/\n", |
| 1119 | zPage); |
| 1120 | } |
| 1121 | fossil_free(zFile); |
| 1122 | } |
| 1123 | blob_append(&css, |
| 1124 | "\n/***********************************************************\n" |
| 1125 | "** All CSS which follows is supplied by the repository \"skin\".\n" |
| 1126 | "***********************************************************/\n", |
| 1127 | -1); |
| 1128 |
| --- src/style.c | |
| +++ src/style.c | |
| @@ -1071,20 +1071,19 @@ | |
| 1071 | } |
| 1072 | style_init_th1_vars(0); |
| 1073 | Th_Render(zScript?zScript:""); |
| 1074 | } |
| 1075 | |
| 1076 | /* |
| 1077 | ** WEBPAGE: style.css |
| 1078 | ** |
| 1079 | ** Return the style sheet. |
| 1080 | */ |
| 1081 | void page_style_css(void){ |
| 1082 | Blob css = empty_blob; |
| 1083 | int i; |
| 1084 | char *zPage = cgi_referer_fossil_page_name(); |
| 1085 | |
| 1086 | cgi_set_content_type("text/css"); |
| 1087 | /* Emit all default rules... */ |
| 1088 | for(i=1; cssDefaultList[i].elementClass; i++){ |
| 1089 | char *z = blob_str(&css); |
| @@ -1118,10 +1117,11 @@ | |
| 1117 | "***********************************************************/\n", |
| 1118 | zPage); |
| 1119 | } |
| 1120 | fossil_free(zFile); |
| 1121 | } |
| 1122 | fossil_free(zPage); |
| 1123 | blob_append(&css, |
| 1124 | "\n/***********************************************************\n" |
| 1125 | "** All CSS which follows is supplied by the repository \"skin\".\n" |
| 1126 | "***********************************************************/\n", |
| 1127 | -1); |
| 1128 |
+20
-1
| --- src/url.c | ||
| +++ src/url.c | ||
| @@ -49,11 +49,11 @@ | ||
| 49 | 49 | int isFile; /* True if a "file:" url */ |
| 50 | 50 | int isHttps; /* True if a "https:" url */ |
| 51 | 51 | int isSsh; /* True if an "ssh:" url */ |
| 52 | 52 | char *name; /* Hostname for http: or filename for file: */ |
| 53 | 53 | char *hostname; /* The HOST: parameter on http headers */ |
| 54 | - char *protocol; /* "http" or "https" */ | |
| 54 | + const char *protocol; /* "http" or "https" or "ssh" */ | |
| 55 | 55 | int port; /* TCP port number for http: or https: */ |
| 56 | 56 | int dfltPort; /* The default port for the given protocol */ |
| 57 | 57 | char *path; /* Pathname for http: */ |
| 58 | 58 | char *user; /* User id for http: */ |
| 59 | 59 | char *passwd; /* Password for http: */ |
| @@ -65,10 +65,29 @@ | ||
| 65 | 65 | char *proxyUrlPath; |
| 66 | 66 | int proxyOrigPort; /* Tunneled port number for https through proxy */ |
| 67 | 67 | }; |
| 68 | 68 | #endif /* INTERFACE */ |
| 69 | 69 | |
| 70 | +/* | |
| 71 | +** Frees (almost) all (char*) members of pUrlData and zeroes out | |
| 72 | +** pUrlData. Results are undefined if pUrlData passed an uninitialized | |
| 73 | +** object. | |
| 74 | +*/ | |
| 75 | +void url_cleanup(UrlData *pUrlData){ | |
| 76 | + fossil_free(pUrlData->user); | |
| 77 | + fossil_free(pUrlData->passwd); | |
| 78 | + if(pUrlData->hostname != pUrlData->name){ | |
| 79 | + fossil_free(pUrlData->name); | |
| 80 | + } | |
| 81 | + fossil_free(pUrlData->hostname); | |
| 82 | + fossil_free(pUrlData->path); | |
| 83 | + fossil_free(pUrlData->canonical); | |
| 84 | + /* ??? fossil_free(pUrlData->proxyAuth); */ | |
| 85 | + /* ??? fossil_free(pUrlData->fossil); */ | |
| 86 | + /* ??? fossil_free(pUrlData->proxyUrlPath); */ | |
| 87 | + memset(pUrlData, 0, sizeof(*pUrlData)); | |
| 88 | +} | |
| 70 | 89 | |
| 71 | 90 | /* |
| 72 | 91 | ** Parse the given URL. Populate members of the provided UrlData structure |
| 73 | 92 | ** as follows: |
| 74 | 93 | ** |
| 75 | 94 |
| --- src/url.c | |
| +++ src/url.c | |
| @@ -49,11 +49,11 @@ | |
| 49 | int isFile; /* True if a "file:" url */ |
| 50 | int isHttps; /* True if a "https:" url */ |
| 51 | int isSsh; /* True if an "ssh:" url */ |
| 52 | char *name; /* Hostname for http: or filename for file: */ |
| 53 | char *hostname; /* The HOST: parameter on http headers */ |
| 54 | char *protocol; /* "http" or "https" */ |
| 55 | int port; /* TCP port number for http: or https: */ |
| 56 | int dfltPort; /* The default port for the given protocol */ |
| 57 | char *path; /* Pathname for http: */ |
| 58 | char *user; /* User id for http: */ |
| 59 | char *passwd; /* Password for http: */ |
| @@ -65,10 +65,29 @@ | |
| 65 | char *proxyUrlPath; |
| 66 | int proxyOrigPort; /* Tunneled port number for https through proxy */ |
| 67 | }; |
| 68 | #endif /* INTERFACE */ |
| 69 | |
| 70 | |
| 71 | /* |
| 72 | ** Parse the given URL. Populate members of the provided UrlData structure |
| 73 | ** as follows: |
| 74 | ** |
| 75 |
| --- src/url.c | |
| +++ src/url.c | |
| @@ -49,11 +49,11 @@ | |
| 49 | int isFile; /* True if a "file:" url */ |
| 50 | int isHttps; /* True if a "https:" url */ |
| 51 | int isSsh; /* True if an "ssh:" url */ |
| 52 | char *name; /* Hostname for http: or filename for file: */ |
| 53 | char *hostname; /* The HOST: parameter on http headers */ |
| 54 | const char *protocol; /* "http" or "https" or "ssh" */ |
| 55 | int port; /* TCP port number for http: or https: */ |
| 56 | int dfltPort; /* The default port for the given protocol */ |
| 57 | char *path; /* Pathname for http: */ |
| 58 | char *user; /* User id for http: */ |
| 59 | char *passwd; /* Password for http: */ |
| @@ -65,10 +65,29 @@ | |
| 65 | char *proxyUrlPath; |
| 66 | int proxyOrigPort; /* Tunneled port number for https through proxy */ |
| 67 | }; |
| 68 | #endif /* INTERFACE */ |
| 69 | |
| 70 | /* |
| 71 | ** Frees (almost) all (char*) members of pUrlData and zeroes out |
| 72 | ** pUrlData. Results are undefined if pUrlData passed an uninitialized |
| 73 | ** object. |
| 74 | */ |
| 75 | void url_cleanup(UrlData *pUrlData){ |
| 76 | fossil_free(pUrlData->user); |
| 77 | fossil_free(pUrlData->passwd); |
| 78 | if(pUrlData->hostname != pUrlData->name){ |
| 79 | fossil_free(pUrlData->name); |
| 80 | } |
| 81 | fossil_free(pUrlData->hostname); |
| 82 | fossil_free(pUrlData->path); |
| 83 | fossil_free(pUrlData->canonical); |
| 84 | /* ??? fossil_free(pUrlData->proxyAuth); */ |
| 85 | /* ??? fossil_free(pUrlData->fossil); */ |
| 86 | /* ??? fossil_free(pUrlData->proxyUrlPath); */ |
| 87 | memset(pUrlData, 0, sizeof(*pUrlData)); |
| 88 | } |
| 89 | |
| 90 | /* |
| 91 | ** Parse the given URL. Populate members of the provided UrlData structure |
| 92 | ** as follows: |
| 93 | ** |
| 94 |