Fossil SCM
Enable diff expansion with the exbase= query parameter on /ckout.
Commit
4ba97a66aaad0f64695c4dd06c5d5f8d01cfe219772dddc67d36220388ea6d5a
Parent
700b5031fdb85ea…
1 file changed
+62
-21
+62
-21
| --- src/info.c | ||
| +++ src/info.c | ||
| @@ -778,16 +778,29 @@ | ||
| 778 | 778 | } |
| 779 | 779 | blob_read_from_file(&rhs, zRhs, ExtFILE); |
| 780 | 780 | if( blob_size(&lhs)!=blob_size(&rhs) |
| 781 | 781 | || memcmp(blob_buffer(&lhs), blob_buffer(&rhs), blob_size(&lhs))!=0 |
| 782 | 782 | ){ |
| 783 | + char *zFullFN; | |
| 784 | + char *zHexFN; | |
| 785 | + int nFullFN; | |
| 786 | + zFullFN = file_canonical_name_dup(zLhs); | |
| 787 | + nFullFN = (int)strlen(zFullFN); | |
| 788 | + zHexFN = fossil_malloc( nFullFN*2 + 5 ); | |
| 789 | + zHexFN[0] = 'x'; | |
| 790 | + encode16((const u8*)zFullFN, (u8*)(zHexFN+1), nFullFN); | |
| 791 | + zHexFN[1+nFullFN*2] = 0; | |
| 792 | + fossil_free(zFullFN); | |
| 793 | + pCfg->zLeftHash = zHexFN; | |
| 783 | 794 | @ <div class='file-change-line'><span> |
| 784 | 795 | @ Changes to %h(zFile) |
| 785 | 796 | @ </span></div> |
| 786 | 797 | if( pCfg ){ |
| 787 | 798 | text_diff(&lhs, &rhs, cgi_output_blob(), pCfg); |
| 788 | 799 | } |
| 800 | + pCfg->zLeftHash = 0; | |
| 801 | + fossil_free(zHexFN); | |
| 789 | 802 | } |
| 790 | 803 | blob_reset(&lhs); |
| 791 | 804 | blob_reset(&rhs); |
| 792 | 805 | fossil_free(zLhs); |
| 793 | 806 | fossil_free(zRhs); |
| @@ -2165,10 +2178,16 @@ | ||
| 2165 | 2178 | ** WEBPAGE: jchunk hidden |
| 2166 | 2179 | ** URL: /jchunk/HASH?from=N&to=M |
| 2167 | 2180 | ** |
| 2168 | 2181 | ** Return lines of text from a file as a JSON array - one entry in the |
| 2169 | 2182 | ** array for each line of text. |
| 2183 | +** | |
| 2184 | +** The HASH is normally a sha1 or sha3 hash that identifies an artifact | |
| 2185 | +** in the BLOB table of the database. However, if HASH starts with an "x" | |
| 2186 | +** and is followed by valid hexadecimal, and if we are running in a | |
| 2187 | +** "fossil ui" situation (locally and with privilege), then decode the hex | |
| 2188 | +** into a filename and read the file content from that name. | |
| 2170 | 2189 | ** |
| 2171 | 2190 | ** **Warning:** This is an internal-use-only interface that is subject to |
| 2172 | 2191 | ** change at any moment. External application should not use this interface |
| 2173 | 2192 | ** since the application will break when this interface changes, and this |
| 2174 | 2193 | ** interface will undoubtedly change. |
| @@ -2180,10 +2199,11 @@ | ||
| 2180 | 2199 | ** ajax_route_error(). |
| 2181 | 2200 | */ |
| 2182 | 2201 | void jchunk_page(void){ |
| 2183 | 2202 | int rid = 0; |
| 2184 | 2203 | const char *zName = PD("name", ""); |
| 2204 | + int nName = (int)(strlen(zName)&0x7fffffff); | |
| 2185 | 2205 | int iFrom = atoi(PD("from","0")); |
| 2186 | 2206 | int iTo = atoi(PD("to","0")); |
| 2187 | 2207 | int ln; |
| 2188 | 2208 | int go = 1; |
| 2189 | 2209 | const char *zSep; |
| @@ -2200,36 +2220,57 @@ | ||
| 2200 | 2220 | cgi_check_for_malice(); |
| 2201 | 2221 | if( !g.perm.Read ){ |
| 2202 | 2222 | ajax_route_error(403, "Access requires Read permissions."); |
| 2203 | 2223 | return; |
| 2204 | 2224 | } |
| 2205 | -#if 1 | |
| 2206 | - /* Re-enable this block once this code is integrated somewhere into | |
| 2207 | - the UI. */ | |
| 2208 | - rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", zName); | |
| 2209 | - if( rid==0 ){ | |
| 2210 | - ajax_route_error(404, "Unknown artifact: %h", zName); | |
| 2211 | - return; | |
| 2212 | - } | |
| 2213 | -#else | |
| 2214 | - /* This impl is only to simplify "manual" testing via the JS | |
| 2215 | - console. */ | |
| 2216 | - rid = symbolic_name_to_rid(zName, "*"); | |
| 2217 | - if( rid==0 ){ | |
| 2218 | - ajax_route_error(404, "Unknown artifact: %h", zName); | |
| 2219 | - return; | |
| 2220 | - }else if( rid<0 ){ | |
| 2221 | - ajax_route_error(418, "Ambiguous artifact name: %h", zName); | |
| 2222 | - return; | |
| 2223 | - } | |
| 2224 | -#endif | |
| 2225 | 2225 | if( iFrom<1 || iTo<iFrom ){ |
| 2226 | 2226 | ajax_route_error(500, "Invalid line range from=%d, to=%d.", |
| 2227 | 2227 | iFrom, iTo); |
| 2228 | 2228 | return; |
| 2229 | 2229 | } |
| 2230 | - content_get(rid, &content); | |
| 2230 | + if( zName[0]=='x' | |
| 2231 | + && ((nName-1)&1)==0 | |
| 2232 | + && validate16(&zName[1],nName-1) | |
| 2233 | + && g.perm.Admin | |
| 2234 | + && db_open_local(0) | |
| 2235 | + && cgi_is_loopback(g.zIpAddr) | |
| 2236 | + ){ | |
| 2237 | + /* Treat the HASH as a hex-encoded filename */ | |
| 2238 | + int n = (nName-1)/2; | |
| 2239 | + char *zFN = fossil_malloc(n+1); | |
| 2240 | + decode16((const u8*)&zName[1], (u8*)zFN, nName-1); | |
| 2241 | + zFN[n] = 0; | |
| 2242 | + if( file_size(zFN, ExtFILE)<0 ){ | |
| 2243 | + blob_zero(&content); | |
| 2244 | + }else{ | |
| 2245 | + blob_read_from_file(&content, zFN, ExtFILE); | |
| 2246 | + } | |
| 2247 | + fossil_free(zFN); | |
| 2248 | + }else{ | |
| 2249 | + /* Treat the HASH as an artifact hash matching BLOB.UUID */ | |
| 2250 | +#if 1 | |
| 2251 | + /* Re-enable this block once this code is integrated somewhere into | |
| 2252 | + the UI. */ | |
| 2253 | + rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", zName); | |
| 2254 | + if( rid==0 ){ | |
| 2255 | + ajax_route_error(404, "Unknown artifact: %h", zName); | |
| 2256 | + return; | |
| 2257 | + } | |
| 2258 | +#else | |
| 2259 | + /* This impl is only to simplify "manual" testing via the JS | |
| 2260 | + console. */ | |
| 2261 | + rid = symbolic_name_to_rid(zName, "*"); | |
| 2262 | + if( rid==0 ){ | |
| 2263 | + ajax_route_error(404, "Unknown artifact: %h", zName); | |
| 2264 | + return; | |
| 2265 | + }else if( rid<0 ){ | |
| 2266 | + ajax_route_error(418, "Ambiguous artifact name: %h", zName); | |
| 2267 | + return; | |
| 2268 | + } | |
| 2269 | +#endif | |
| 2270 | + content_get(rid, &content); | |
| 2271 | + } | |
| 2231 | 2272 | g.isConst = 1; |
| 2232 | 2273 | cgi_set_content_type("application/json"); |
| 2233 | 2274 | ln = 0; |
| 2234 | 2275 | while( go && ln<iFrom ){ |
| 2235 | 2276 | go = blob_line(&content, &line); |
| 2236 | 2277 |
| --- src/info.c | |
| +++ src/info.c | |
| @@ -778,16 +778,29 @@ | |
| 778 | } |
| 779 | blob_read_from_file(&rhs, zRhs, ExtFILE); |
| 780 | if( blob_size(&lhs)!=blob_size(&rhs) |
| 781 | || memcmp(blob_buffer(&lhs), blob_buffer(&rhs), blob_size(&lhs))!=0 |
| 782 | ){ |
| 783 | @ <div class='file-change-line'><span> |
| 784 | @ Changes to %h(zFile) |
| 785 | @ </span></div> |
| 786 | if( pCfg ){ |
| 787 | text_diff(&lhs, &rhs, cgi_output_blob(), pCfg); |
| 788 | } |
| 789 | } |
| 790 | blob_reset(&lhs); |
| 791 | blob_reset(&rhs); |
| 792 | fossil_free(zLhs); |
| 793 | fossil_free(zRhs); |
| @@ -2165,10 +2178,16 @@ | |
| 2165 | ** WEBPAGE: jchunk hidden |
| 2166 | ** URL: /jchunk/HASH?from=N&to=M |
| 2167 | ** |
| 2168 | ** Return lines of text from a file as a JSON array - one entry in the |
| 2169 | ** array for each line of text. |
| 2170 | ** |
| 2171 | ** **Warning:** This is an internal-use-only interface that is subject to |
| 2172 | ** change at any moment. External application should not use this interface |
| 2173 | ** since the application will break when this interface changes, and this |
| 2174 | ** interface will undoubtedly change. |
| @@ -2180,10 +2199,11 @@ | |
| 2180 | ** ajax_route_error(). |
| 2181 | */ |
| 2182 | void jchunk_page(void){ |
| 2183 | int rid = 0; |
| 2184 | const char *zName = PD("name", ""); |
| 2185 | int iFrom = atoi(PD("from","0")); |
| 2186 | int iTo = atoi(PD("to","0")); |
| 2187 | int ln; |
| 2188 | int go = 1; |
| 2189 | const char *zSep; |
| @@ -2200,36 +2220,57 @@ | |
| 2200 | cgi_check_for_malice(); |
| 2201 | if( !g.perm.Read ){ |
| 2202 | ajax_route_error(403, "Access requires Read permissions."); |
| 2203 | return; |
| 2204 | } |
| 2205 | #if 1 |
| 2206 | /* Re-enable this block once this code is integrated somewhere into |
| 2207 | the UI. */ |
| 2208 | rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", zName); |
| 2209 | if( rid==0 ){ |
| 2210 | ajax_route_error(404, "Unknown artifact: %h", zName); |
| 2211 | return; |
| 2212 | } |
| 2213 | #else |
| 2214 | /* This impl is only to simplify "manual" testing via the JS |
| 2215 | console. */ |
| 2216 | rid = symbolic_name_to_rid(zName, "*"); |
| 2217 | if( rid==0 ){ |
| 2218 | ajax_route_error(404, "Unknown artifact: %h", zName); |
| 2219 | return; |
| 2220 | }else if( rid<0 ){ |
| 2221 | ajax_route_error(418, "Ambiguous artifact name: %h", zName); |
| 2222 | return; |
| 2223 | } |
| 2224 | #endif |
| 2225 | if( iFrom<1 || iTo<iFrom ){ |
| 2226 | ajax_route_error(500, "Invalid line range from=%d, to=%d.", |
| 2227 | iFrom, iTo); |
| 2228 | return; |
| 2229 | } |
| 2230 | content_get(rid, &content); |
| 2231 | g.isConst = 1; |
| 2232 | cgi_set_content_type("application/json"); |
| 2233 | ln = 0; |
| 2234 | while( go && ln<iFrom ){ |
| 2235 | go = blob_line(&content, &line); |
| 2236 |
| --- src/info.c | |
| +++ src/info.c | |
| @@ -778,16 +778,29 @@ | |
| 778 | } |
| 779 | blob_read_from_file(&rhs, zRhs, ExtFILE); |
| 780 | if( blob_size(&lhs)!=blob_size(&rhs) |
| 781 | || memcmp(blob_buffer(&lhs), blob_buffer(&rhs), blob_size(&lhs))!=0 |
| 782 | ){ |
| 783 | char *zFullFN; |
| 784 | char *zHexFN; |
| 785 | int nFullFN; |
| 786 | zFullFN = file_canonical_name_dup(zLhs); |
| 787 | nFullFN = (int)strlen(zFullFN); |
| 788 | zHexFN = fossil_malloc( nFullFN*2 + 5 ); |
| 789 | zHexFN[0] = 'x'; |
| 790 | encode16((const u8*)zFullFN, (u8*)(zHexFN+1), nFullFN); |
| 791 | zHexFN[1+nFullFN*2] = 0; |
| 792 | fossil_free(zFullFN); |
| 793 | pCfg->zLeftHash = zHexFN; |
| 794 | @ <div class='file-change-line'><span> |
| 795 | @ Changes to %h(zFile) |
| 796 | @ </span></div> |
| 797 | if( pCfg ){ |
| 798 | text_diff(&lhs, &rhs, cgi_output_blob(), pCfg); |
| 799 | } |
| 800 | pCfg->zLeftHash = 0; |
| 801 | fossil_free(zHexFN); |
| 802 | } |
| 803 | blob_reset(&lhs); |
| 804 | blob_reset(&rhs); |
| 805 | fossil_free(zLhs); |
| 806 | fossil_free(zRhs); |
| @@ -2165,10 +2178,16 @@ | |
| 2178 | ** WEBPAGE: jchunk hidden |
| 2179 | ** URL: /jchunk/HASH?from=N&to=M |
| 2180 | ** |
| 2181 | ** Return lines of text from a file as a JSON array - one entry in the |
| 2182 | ** array for each line of text. |
| 2183 | ** |
| 2184 | ** The HASH is normally a sha1 or sha3 hash that identifies an artifact |
| 2185 | ** in the BLOB table of the database. However, if HASH starts with an "x" |
| 2186 | ** and is followed by valid hexadecimal, and if we are running in a |
| 2187 | ** "fossil ui" situation (locally and with privilege), then decode the hex |
| 2188 | ** into a filename and read the file content from that name. |
| 2189 | ** |
| 2190 | ** **Warning:** This is an internal-use-only interface that is subject to |
| 2191 | ** change at any moment. External application should not use this interface |
| 2192 | ** since the application will break when this interface changes, and this |
| 2193 | ** interface will undoubtedly change. |
| @@ -2180,10 +2199,11 @@ | |
| 2199 | ** ajax_route_error(). |
| 2200 | */ |
| 2201 | void jchunk_page(void){ |
| 2202 | int rid = 0; |
| 2203 | const char *zName = PD("name", ""); |
| 2204 | int nName = (int)(strlen(zName)&0x7fffffff); |
| 2205 | int iFrom = atoi(PD("from","0")); |
| 2206 | int iTo = atoi(PD("to","0")); |
| 2207 | int ln; |
| 2208 | int go = 1; |
| 2209 | const char *zSep; |
| @@ -2200,36 +2220,57 @@ | |
| 2220 | cgi_check_for_malice(); |
| 2221 | if( !g.perm.Read ){ |
| 2222 | ajax_route_error(403, "Access requires Read permissions."); |
| 2223 | return; |
| 2224 | } |
| 2225 | if( iFrom<1 || iTo<iFrom ){ |
| 2226 | ajax_route_error(500, "Invalid line range from=%d, to=%d.", |
| 2227 | iFrom, iTo); |
| 2228 | return; |
| 2229 | } |
| 2230 | if( zName[0]=='x' |
| 2231 | && ((nName-1)&1)==0 |
| 2232 | && validate16(&zName[1],nName-1) |
| 2233 | && g.perm.Admin |
| 2234 | && db_open_local(0) |
| 2235 | && cgi_is_loopback(g.zIpAddr) |
| 2236 | ){ |
| 2237 | /* Treat the HASH as a hex-encoded filename */ |
| 2238 | int n = (nName-1)/2; |
| 2239 | char *zFN = fossil_malloc(n+1); |
| 2240 | decode16((const u8*)&zName[1], (u8*)zFN, nName-1); |
| 2241 | zFN[n] = 0; |
| 2242 | if( file_size(zFN, ExtFILE)<0 ){ |
| 2243 | blob_zero(&content); |
| 2244 | }else{ |
| 2245 | blob_read_from_file(&content, zFN, ExtFILE); |
| 2246 | } |
| 2247 | fossil_free(zFN); |
| 2248 | }else{ |
| 2249 | /* Treat the HASH as an artifact hash matching BLOB.UUID */ |
| 2250 | #if 1 |
| 2251 | /* Re-enable this block once this code is integrated somewhere into |
| 2252 | the UI. */ |
| 2253 | rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", zName); |
| 2254 | if( rid==0 ){ |
| 2255 | ajax_route_error(404, "Unknown artifact: %h", zName); |
| 2256 | return; |
| 2257 | } |
| 2258 | #else |
| 2259 | /* This impl is only to simplify "manual" testing via the JS |
| 2260 | console. */ |
| 2261 | rid = symbolic_name_to_rid(zName, "*"); |
| 2262 | if( rid==0 ){ |
| 2263 | ajax_route_error(404, "Unknown artifact: %h", zName); |
| 2264 | return; |
| 2265 | }else if( rid<0 ){ |
| 2266 | ajax_route_error(418, "Ambiguous artifact name: %h", zName); |
| 2267 | return; |
| 2268 | } |
| 2269 | #endif |
| 2270 | content_get(rid, &content); |
| 2271 | } |
| 2272 | g.isConst = 1; |
| 2273 | cgi_set_content_type("application/json"); |
| 2274 | ln = 0; |
| 2275 | while( go && ln<iFrom ){ |
| 2276 | go = blob_line(&content, &line); |
| 2277 |