Fossil SCM
Add a new TH1 "http" command, a new "th1-uri-regexp" setting and two new hook scripts "xfer-commit-script" and "xfer-ticket-script". They can be used together to implement more advanced commit and ticket change notifications.
Commit
1311841a3c201a669bc0e433e24c6caf3fd3ab1d
Parent
44dec8ff077a186…
26 files changed
+2
-2
+2
-2
+4
-1
+3
+2
-2
+6
-1
+1
-1
+12
-13
+9
-8
+17
-15
+46
-46
+1
-1
+1
-1
+2
-2
+16
+38
-11
+1
-1
+1
-1
+1
-1
+115
+9
-4
+244
-184
+2
-2
+1
-1
+77
-32
+87
~
src/attach.c
~
src/branch.c
~
src/checkin.c
~
src/configure.c
~
src/content.c
~
src/db.c
~
src/event.c
~
src/http.c
~
src/http_socket.c
~
src/http_ssl.c
~
src/http_transport.c
~
src/info.c
~
src/info.c
~
src/json_branch.c
~
src/main.c
~
src/manifest.c
~
src/rebuild.c
~
src/setup.c
~
src/tag.c
~
src/th_main.c
~
src/tkt.c
~
src/url.c
~
src/user.c
~
src/wiki.c
~
src/xfer.c
~
src/xfersetup.c
+2
-2
| --- src/attach.c | ||
| +++ src/attach.c | ||
| @@ -214,11 +214,11 @@ | ||
| 214 | 214 | }else{ |
| 215 | 215 | rid = content_put(pAttach); |
| 216 | 216 | db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d);", rid); |
| 217 | 217 | db_multi_exec("INSERT OR IGNORE INTO unclustered VALUES(%d);", rid); |
| 218 | 218 | } |
| 219 | - manifest_crosslink(rid, pAttach); | |
| 219 | + manifest_crosslink(rid, pAttach, MC_NONE); | |
| 220 | 220 | } |
| 221 | 221 | |
| 222 | 222 | |
| 223 | 223 | /* |
| 224 | 224 | ** WEBPAGE: attachadd |
| @@ -431,11 +431,11 @@ | ||
| 431 | 431 | blob_appendf(&manifest, "D %s\n", zDate); |
| 432 | 432 | blob_appendf(&manifest, "U %F\n", g.zLogin ? g.zLogin : "nobody"); |
| 433 | 433 | md5sum_blob(&manifest, &cksum); |
| 434 | 434 | blob_appendf(&manifest, "Z %b\n", &cksum); |
| 435 | 435 | rid = content_put(&manifest); |
| 436 | - manifest_crosslink(rid, &manifest); | |
| 436 | + manifest_crosslink(rid, &manifest, MC_NONE); | |
| 437 | 437 | db_end_transaction(0); |
| 438 | 438 | @ <p>The attachment below has been deleted.</p> |
| 439 | 439 | } |
| 440 | 440 | |
| 441 | 441 | if( P("del") |
| 442 | 442 |
| --- src/attach.c | |
| +++ src/attach.c | |
| @@ -214,11 +214,11 @@ | |
| 214 | }else{ |
| 215 | rid = content_put(pAttach); |
| 216 | db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d);", rid); |
| 217 | db_multi_exec("INSERT OR IGNORE INTO unclustered VALUES(%d);", rid); |
| 218 | } |
| 219 | manifest_crosslink(rid, pAttach); |
| 220 | } |
| 221 | |
| 222 | |
| 223 | /* |
| 224 | ** WEBPAGE: attachadd |
| @@ -431,11 +431,11 @@ | |
| 431 | blob_appendf(&manifest, "D %s\n", zDate); |
| 432 | blob_appendf(&manifest, "U %F\n", g.zLogin ? g.zLogin : "nobody"); |
| 433 | md5sum_blob(&manifest, &cksum); |
| 434 | blob_appendf(&manifest, "Z %b\n", &cksum); |
| 435 | rid = content_put(&manifest); |
| 436 | manifest_crosslink(rid, &manifest); |
| 437 | db_end_transaction(0); |
| 438 | @ <p>The attachment below has been deleted.</p> |
| 439 | } |
| 440 | |
| 441 | if( P("del") |
| 442 |
| --- src/attach.c | |
| +++ src/attach.c | |
| @@ -214,11 +214,11 @@ | |
| 214 | }else{ |
| 215 | rid = content_put(pAttach); |
| 216 | db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d);", rid); |
| 217 | db_multi_exec("INSERT OR IGNORE INTO unclustered VALUES(%d);", rid); |
| 218 | } |
| 219 | manifest_crosslink(rid, pAttach, MC_NONE); |
| 220 | } |
| 221 | |
| 222 | |
| 223 | /* |
| 224 | ** WEBPAGE: attachadd |
| @@ -431,11 +431,11 @@ | |
| 431 | blob_appendf(&manifest, "D %s\n", zDate); |
| 432 | blob_appendf(&manifest, "U %F\n", g.zLogin ? g.zLogin : "nobody"); |
| 433 | md5sum_blob(&manifest, &cksum); |
| 434 | blob_appendf(&manifest, "Z %b\n", &cksum); |
| 435 | rid = content_put(&manifest); |
| 436 | manifest_crosslink(rid, &manifest, MC_NONE); |
| 437 | db_end_transaction(0); |
| 438 | @ <p>The attachment below has been deleted.</p> |
| 439 | } |
| 440 | |
| 441 | if( P("del") |
| 442 |
+2
-2
| --- src/branch.c | ||
| +++ src/branch.c | ||
| @@ -153,12 +153,12 @@ | ||
| 153 | 153 | brid = content_put_ex(&branch, 0, 0, 0, isPrivate); |
| 154 | 154 | if( brid==0 ){ |
| 155 | 155 | fossil_fatal("trouble committing manifest: %s", g.zErrMsg); |
| 156 | 156 | } |
| 157 | 157 | db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", brid); |
| 158 | - if( manifest_crosslink(brid, &branch)==0 ){ | |
| 159 | - fossil_fatal("unable to install new manifest"); | |
| 158 | + if( manifest_crosslink(brid, &branch, MC_PERMIT_HOOKS)==0 ){ | |
| 159 | + fossil_fatal("%s\n", g.zErrMsg); | |
| 160 | 160 | } |
| 161 | 161 | assert( blob_is_reset(&branch) ); |
| 162 | 162 | content_deltify(rootid, brid, 0); |
| 163 | 163 | zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", brid); |
| 164 | 164 | fossil_print("New branch: %s\n", zUuid); |
| 165 | 165 |
| --- src/branch.c | |
| +++ src/branch.c | |
| @@ -153,12 +153,12 @@ | |
| 153 | brid = content_put_ex(&branch, 0, 0, 0, isPrivate); |
| 154 | if( brid==0 ){ |
| 155 | fossil_fatal("trouble committing manifest: %s", g.zErrMsg); |
| 156 | } |
| 157 | db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", brid); |
| 158 | if( manifest_crosslink(brid, &branch)==0 ){ |
| 159 | fossil_fatal("unable to install new manifest"); |
| 160 | } |
| 161 | assert( blob_is_reset(&branch) ); |
| 162 | content_deltify(rootid, brid, 0); |
| 163 | zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", brid); |
| 164 | fossil_print("New branch: %s\n", zUuid); |
| 165 |
| --- src/branch.c | |
| +++ src/branch.c | |
| @@ -153,12 +153,12 @@ | |
| 153 | brid = content_put_ex(&branch, 0, 0, 0, isPrivate); |
| 154 | if( brid==0 ){ |
| 155 | fossil_fatal("trouble committing manifest: %s", g.zErrMsg); |
| 156 | } |
| 157 | db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", brid); |
| 158 | if( manifest_crosslink(brid, &branch, MC_PERMIT_HOOKS)==0 ){ |
| 159 | fossil_fatal("%s\n", g.zErrMsg); |
| 160 | } |
| 161 | assert( blob_is_reset(&branch) ); |
| 162 | content_deltify(rootid, brid, 0); |
| 163 | zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", brid); |
| 164 | fossil_print("New branch: %s\n", zUuid); |
| 165 |
+4
-1
| --- src/checkin.c | ||
| +++ src/checkin.c | ||
| @@ -1816,11 +1816,14 @@ | ||
| 1816 | 1816 | nvid = content_put(&manifest); |
| 1817 | 1817 | if( nvid==0 ){ |
| 1818 | 1818 | fossil_fatal("trouble committing manifest: %s", g.zErrMsg); |
| 1819 | 1819 | } |
| 1820 | 1820 | db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nvid); |
| 1821 | - manifest_crosslink(nvid, &manifest); | |
| 1821 | + if( manifest_crosslink(nvid, &manifest, | |
| 1822 | + dryRunFlag ? MC_NONE : MC_PERMIT_HOOKS)==0 ){ | |
| 1823 | + fossil_fatal("%s\n", g.zErrMsg); | |
| 1824 | + } | |
| 1822 | 1825 | assert( blob_is_reset(&manifest) ); |
| 1823 | 1826 | content_deltify(vid, nvid, 0); |
| 1824 | 1827 | zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", nvid); |
| 1825 | 1828 | |
| 1826 | 1829 | db_prepare(&q, "SELECT uuid,merge FROM vmerge JOIN blob ON merge=rid" |
| 1827 | 1830 |
| --- src/checkin.c | |
| +++ src/checkin.c | |
| @@ -1816,11 +1816,14 @@ | |
| 1816 | nvid = content_put(&manifest); |
| 1817 | if( nvid==0 ){ |
| 1818 | fossil_fatal("trouble committing manifest: %s", g.zErrMsg); |
| 1819 | } |
| 1820 | db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nvid); |
| 1821 | manifest_crosslink(nvid, &manifest); |
| 1822 | assert( blob_is_reset(&manifest) ); |
| 1823 | content_deltify(vid, nvid, 0); |
| 1824 | zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", nvid); |
| 1825 | |
| 1826 | db_prepare(&q, "SELECT uuid,merge FROM vmerge JOIN blob ON merge=rid" |
| 1827 |
| --- src/checkin.c | |
| +++ src/checkin.c | |
| @@ -1816,11 +1816,14 @@ | |
| 1816 | nvid = content_put(&manifest); |
| 1817 | if( nvid==0 ){ |
| 1818 | fossil_fatal("trouble committing manifest: %s", g.zErrMsg); |
| 1819 | } |
| 1820 | db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nvid); |
| 1821 | if( manifest_crosslink(nvid, &manifest, |
| 1822 | dryRunFlag ? MC_NONE : MC_PERMIT_HOOKS)==0 ){ |
| 1823 | fossil_fatal("%s\n", g.zErrMsg); |
| 1824 | } |
| 1825 | assert( blob_is_reset(&manifest) ); |
| 1826 | content_deltify(vid, nvid, 0); |
| 1827 | zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", nvid); |
| 1828 | |
| 1829 | db_prepare(&q, "SELECT uuid,merge FROM vmerge JOIN blob ON merge=rid" |
| 1830 |
+3
| --- src/configure.c | ||
| +++ src/configure.c | ||
| @@ -98,10 +98,11 @@ | ||
| 98 | 98 | { "timeline-plaintext", CONFIGSET_SKIN }, |
| 99 | 99 | { "adunit", CONFIGSET_SKIN }, |
| 100 | 100 | { "adunit-omit-if-admin", CONFIGSET_SKIN }, |
| 101 | 101 | { "adunit-omit-if-user", CONFIGSET_SKIN }, |
| 102 | 102 | { "th1-setup", CONFIGSET_TH1 }, |
| 103 | + { "th1-uri-regexp", CONFIGSET_TH1 }, | |
| 103 | 104 | |
| 104 | 105 | #ifdef FOSSIL_ENABLE_TCL |
| 105 | 106 | { "tcl", CONFIGSET_TH1 }, |
| 106 | 107 | { "tcl-setup", CONFIGSET_TH1 }, |
| 107 | 108 | #endif |
| @@ -138,10 +139,12 @@ | ||
| 138 | 139 | |
| 139 | 140 | { "@shun", CONFIGSET_SHUN }, |
| 140 | 141 | |
| 141 | 142 | { "xfer-common-script", CONFIGSET_XFER }, |
| 142 | 143 | { "xfer-push-script", CONFIGSET_XFER }, |
| 144 | + { "xfer-commit-script", CONFIGSET_XFER }, | |
| 145 | + { "xfer-ticket-script", CONFIGSET_XFER }, | |
| 143 | 146 | |
| 144 | 147 | }; |
| 145 | 148 | static int iConfig = 0; |
| 146 | 149 | |
| 147 | 150 | /* |
| 148 | 151 |
| --- src/configure.c | |
| +++ src/configure.c | |
| @@ -98,10 +98,11 @@ | |
| 98 | { "timeline-plaintext", CONFIGSET_SKIN }, |
| 99 | { "adunit", CONFIGSET_SKIN }, |
| 100 | { "adunit-omit-if-admin", CONFIGSET_SKIN }, |
| 101 | { "adunit-omit-if-user", CONFIGSET_SKIN }, |
| 102 | { "th1-setup", CONFIGSET_TH1 }, |
| 103 | |
| 104 | #ifdef FOSSIL_ENABLE_TCL |
| 105 | { "tcl", CONFIGSET_TH1 }, |
| 106 | { "tcl-setup", CONFIGSET_TH1 }, |
| 107 | #endif |
| @@ -138,10 +139,12 @@ | |
| 138 | |
| 139 | { "@shun", CONFIGSET_SHUN }, |
| 140 | |
| 141 | { "xfer-common-script", CONFIGSET_XFER }, |
| 142 | { "xfer-push-script", CONFIGSET_XFER }, |
| 143 | |
| 144 | }; |
| 145 | static int iConfig = 0; |
| 146 | |
| 147 | /* |
| 148 |
| --- src/configure.c | |
| +++ src/configure.c | |
| @@ -98,10 +98,11 @@ | |
| 98 | { "timeline-plaintext", CONFIGSET_SKIN }, |
| 99 | { "adunit", CONFIGSET_SKIN }, |
| 100 | { "adunit-omit-if-admin", CONFIGSET_SKIN }, |
| 101 | { "adunit-omit-if-user", CONFIGSET_SKIN }, |
| 102 | { "th1-setup", CONFIGSET_TH1 }, |
| 103 | { "th1-uri-regexp", CONFIGSET_TH1 }, |
| 104 | |
| 105 | #ifdef FOSSIL_ENABLE_TCL |
| 106 | { "tcl", CONFIGSET_TH1 }, |
| 107 | { "tcl-setup", CONFIGSET_TH1 }, |
| 108 | #endif |
| @@ -138,10 +139,12 @@ | |
| 139 | |
| 140 | { "@shun", CONFIGSET_SHUN }, |
| 141 | |
| 142 | { "xfer-common-script", CONFIGSET_XFER }, |
| 143 | { "xfer-push-script", CONFIGSET_XFER }, |
| 144 | { "xfer-commit-script", CONFIGSET_XFER }, |
| 145 | { "xfer-ticket-script", CONFIGSET_XFER }, |
| 146 | |
| 147 | }; |
| 148 | static int iConfig = 0; |
| 149 | |
| 150 | /* |
| 151 |
+2
-2
| --- src/content.c | ||
| +++ src/content.c | ||
| @@ -388,11 +388,11 @@ | ||
| 388 | 388 | int i; |
| 389 | 389 | |
| 390 | 390 | /* Parse the object rid itself */ |
| 391 | 391 | if( linkFlag ){ |
| 392 | 392 | content_get(rid, &content); |
| 393 | - manifest_crosslink(rid, &content); | |
| 393 | + manifest_crosslink(rid, &content, MC_NONE); | |
| 394 | 394 | assert( blob_is_reset(&content) ); |
| 395 | 395 | } |
| 396 | 396 | |
| 397 | 397 | /* Parse all delta-manifests that depend on baseline-manifest rid */ |
| 398 | 398 | db_prepare(&q, "SELECT rid FROM orphan WHERE baseline=%d", rid); |
| @@ -405,11 +405,11 @@ | ||
| 405 | 405 | aChild[nChildUsed++] = child; |
| 406 | 406 | } |
| 407 | 407 | db_finalize(&q); |
| 408 | 408 | for(i=0; i<nChildUsed; i++){ |
| 409 | 409 | content_get(aChild[i], &content); |
| 410 | - manifest_crosslink(aChild[i], &content); | |
| 410 | + manifest_crosslink(aChild[i], &content, MC_NONE); | |
| 411 | 411 | assert( blob_is_reset(&content) ); |
| 412 | 412 | } |
| 413 | 413 | if( nChildUsed ){ |
| 414 | 414 | db_multi_exec("DELETE FROM orphan WHERE baseline=%d", rid); |
| 415 | 415 | } |
| 416 | 416 |
| --- src/content.c | |
| +++ src/content.c | |
| @@ -388,11 +388,11 @@ | |
| 388 | int i; |
| 389 | |
| 390 | /* Parse the object rid itself */ |
| 391 | if( linkFlag ){ |
| 392 | content_get(rid, &content); |
| 393 | manifest_crosslink(rid, &content); |
| 394 | assert( blob_is_reset(&content) ); |
| 395 | } |
| 396 | |
| 397 | /* Parse all delta-manifests that depend on baseline-manifest rid */ |
| 398 | db_prepare(&q, "SELECT rid FROM orphan WHERE baseline=%d", rid); |
| @@ -405,11 +405,11 @@ | |
| 405 | aChild[nChildUsed++] = child; |
| 406 | } |
| 407 | db_finalize(&q); |
| 408 | for(i=0; i<nChildUsed; i++){ |
| 409 | content_get(aChild[i], &content); |
| 410 | manifest_crosslink(aChild[i], &content); |
| 411 | assert( blob_is_reset(&content) ); |
| 412 | } |
| 413 | if( nChildUsed ){ |
| 414 | db_multi_exec("DELETE FROM orphan WHERE baseline=%d", rid); |
| 415 | } |
| 416 |
| --- src/content.c | |
| +++ src/content.c | |
| @@ -388,11 +388,11 @@ | |
| 388 | int i; |
| 389 | |
| 390 | /* Parse the object rid itself */ |
| 391 | if( linkFlag ){ |
| 392 | content_get(rid, &content); |
| 393 | manifest_crosslink(rid, &content, MC_NONE); |
| 394 | assert( blob_is_reset(&content) ); |
| 395 | } |
| 396 | |
| 397 | /* Parse all delta-manifests that depend on baseline-manifest rid */ |
| 398 | db_prepare(&q, "SELECT rid FROM orphan WHERE baseline=%d", rid); |
| @@ -405,11 +405,11 @@ | |
| 405 | aChild[nChildUsed++] = child; |
| 406 | } |
| 407 | db_finalize(&q); |
| 408 | for(i=0; i<nChildUsed; i++){ |
| 409 | content_get(aChild[i], &content); |
| 410 | manifest_crosslink(aChild[i], &content, MC_NONE); |
| 411 | assert( blob_is_reset(&content) ); |
| 412 | } |
| 413 | if( nChildUsed ){ |
| 414 | db_multi_exec("DELETE FROM orphan WHERE baseline=%d", rid); |
| 415 | } |
| 416 |
M
src/db.c
+6
-1
| --- src/db.c | ||
| +++ src/db.c | ||
| @@ -1412,11 +1412,11 @@ | ||
| 1412 | 1412 | blob_appendf(&manifest, "U %F\n", g.zLogin); |
| 1413 | 1413 | md5sum_blob(&manifest, &hash); |
| 1414 | 1414 | blob_appendf(&manifest, "Z %b\n", &hash); |
| 1415 | 1415 | blob_reset(&hash); |
| 1416 | 1416 | rid = content_put(&manifest); |
| 1417 | - manifest_crosslink(rid, &manifest); | |
| 1417 | + manifest_crosslink(rid, &manifest, MC_NONE); | |
| 1418 | 1418 | } |
| 1419 | 1419 | } |
| 1420 | 1420 | |
| 1421 | 1421 | /* |
| 1422 | 1422 | ** COMMAND: new* |
| @@ -2152,10 +2152,11 @@ | ||
| 2152 | 2152 | #ifdef FOSSIL_ENABLE_TCL |
| 2153 | 2153 | { "tcl", 0, 0, 0, "off" }, |
| 2154 | 2154 | { "tcl-setup", 0, 40, 0, "" }, |
| 2155 | 2155 | #endif |
| 2156 | 2156 | { "th1-setup", 0, 40, 0, "" }, |
| 2157 | + { "th1-uri-regexp",0, 40, 0, "" }, | |
| 2157 | 2158 | { "web-browser", 0, 32, 0, "" }, |
| 2158 | 2159 | { "white-foreground", 0, 0, 0, "off" }, |
| 2159 | 2160 | { 0,0,0,0,0 } |
| 2160 | 2161 | }; |
| 2161 | 2162 | |
| @@ -2348,10 +2349,14 @@ | ||
| 2348 | 2349 | ** is empty and no extra setup is performed. |
| 2349 | 2350 | ** |
| 2350 | 2351 | ** th1-setup This is the setup script to be evaluated after creating |
| 2351 | 2352 | ** and initializing the TH1 interpreter. By default, this |
| 2352 | 2353 | ** is empty and no extra setup is performed. |
| 2354 | +** | |
| 2355 | +** th1-uri-regexp Specify which URI's are allowed in HTTP requests from | |
| 2356 | +** TH1 scripts. If empty, no HTTP requests are allowed | |
| 2357 | +** whatsoever. The default is an empty string. | |
| 2353 | 2358 | ** |
| 2354 | 2359 | ** web-browser A shell command used to launch your preferred |
| 2355 | 2360 | ** web browser when given a URL as an argument. |
| 2356 | 2361 | ** Defaults to "start" on windows, "open" on Mac, |
| 2357 | 2362 | ** and "firefox" on Unix. |
| 2358 | 2363 |
| --- src/db.c | |
| +++ src/db.c | |
| @@ -1412,11 +1412,11 @@ | |
| 1412 | blob_appendf(&manifest, "U %F\n", g.zLogin); |
| 1413 | md5sum_blob(&manifest, &hash); |
| 1414 | blob_appendf(&manifest, "Z %b\n", &hash); |
| 1415 | blob_reset(&hash); |
| 1416 | rid = content_put(&manifest); |
| 1417 | manifest_crosslink(rid, &manifest); |
| 1418 | } |
| 1419 | } |
| 1420 | |
| 1421 | /* |
| 1422 | ** COMMAND: new* |
| @@ -2152,10 +2152,11 @@ | |
| 2152 | #ifdef FOSSIL_ENABLE_TCL |
| 2153 | { "tcl", 0, 0, 0, "off" }, |
| 2154 | { "tcl-setup", 0, 40, 0, "" }, |
| 2155 | #endif |
| 2156 | { "th1-setup", 0, 40, 0, "" }, |
| 2157 | { "web-browser", 0, 32, 0, "" }, |
| 2158 | { "white-foreground", 0, 0, 0, "off" }, |
| 2159 | { 0,0,0,0,0 } |
| 2160 | }; |
| 2161 | |
| @@ -2348,10 +2349,14 @@ | |
| 2348 | ** is empty and no extra setup is performed. |
| 2349 | ** |
| 2350 | ** th1-setup This is the setup script to be evaluated after creating |
| 2351 | ** and initializing the TH1 interpreter. By default, this |
| 2352 | ** is empty and no extra setup is performed. |
| 2353 | ** |
| 2354 | ** web-browser A shell command used to launch your preferred |
| 2355 | ** web browser when given a URL as an argument. |
| 2356 | ** Defaults to "start" on windows, "open" on Mac, |
| 2357 | ** and "firefox" on Unix. |
| 2358 |
| --- src/db.c | |
| +++ src/db.c | |
| @@ -1412,11 +1412,11 @@ | |
| 1412 | blob_appendf(&manifest, "U %F\n", g.zLogin); |
| 1413 | md5sum_blob(&manifest, &hash); |
| 1414 | blob_appendf(&manifest, "Z %b\n", &hash); |
| 1415 | blob_reset(&hash); |
| 1416 | rid = content_put(&manifest); |
| 1417 | manifest_crosslink(rid, &manifest, MC_NONE); |
| 1418 | } |
| 1419 | } |
| 1420 | |
| 1421 | /* |
| 1422 | ** COMMAND: new* |
| @@ -2152,10 +2152,11 @@ | |
| 2152 | #ifdef FOSSIL_ENABLE_TCL |
| 2153 | { "tcl", 0, 0, 0, "off" }, |
| 2154 | { "tcl-setup", 0, 40, 0, "" }, |
| 2155 | #endif |
| 2156 | { "th1-setup", 0, 40, 0, "" }, |
| 2157 | { "th1-uri-regexp",0, 40, 0, "" }, |
| 2158 | { "web-browser", 0, 32, 0, "" }, |
| 2159 | { "white-foreground", 0, 0, 0, "off" }, |
| 2160 | { 0,0,0,0,0 } |
| 2161 | }; |
| 2162 | |
| @@ -2348,10 +2349,14 @@ | |
| 2349 | ** is empty and no extra setup is performed. |
| 2350 | ** |
| 2351 | ** th1-setup This is the setup script to be evaluated after creating |
| 2352 | ** and initializing the TH1 interpreter. By default, this |
| 2353 | ** is empty and no extra setup is performed. |
| 2354 | ** |
| 2355 | ** th1-uri-regexp Specify which URI's are allowed in HTTP requests from |
| 2356 | ** TH1 scripts. If empty, no HTTP requests are allowed |
| 2357 | ** whatsoever. The default is an empty string. |
| 2358 | ** |
| 2359 | ** web-browser A shell command used to launch your preferred |
| 2360 | ** web browser when given a URL as an argument. |
| 2361 | ** Defaults to "start" on windows, "open" on Mac, |
| 2362 | ** and "firefox" on Unix. |
| 2363 |
+1
-1
| --- src/event.c | ||
| +++ src/event.c | ||
| @@ -351,11 +351,11 @@ | ||
| 351 | 351 | md5sum_blob(&event, &cksum); |
| 352 | 352 | blob_appendf(&event, "Z %b\n", &cksum); |
| 353 | 353 | blob_reset(&cksum); |
| 354 | 354 | nrid = content_put(&event); |
| 355 | 355 | db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nrid); |
| 356 | - manifest_crosslink(nrid, &event); | |
| 356 | + manifest_crosslink(nrid, &event, MC_NONE); | |
| 357 | 357 | assert( blob_is_reset(&event) ); |
| 358 | 358 | content_deltify(rid, nrid, 0); |
| 359 | 359 | db_end_transaction(0); |
| 360 | 360 | cgi_redirectf("event?name=%T", zEventId); |
| 361 | 361 | } |
| 362 | 362 |
| --- src/event.c | |
| +++ src/event.c | |
| @@ -351,11 +351,11 @@ | |
| 351 | md5sum_blob(&event, &cksum); |
| 352 | blob_appendf(&event, "Z %b\n", &cksum); |
| 353 | blob_reset(&cksum); |
| 354 | nrid = content_put(&event); |
| 355 | db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nrid); |
| 356 | manifest_crosslink(nrid, &event); |
| 357 | assert( blob_is_reset(&event) ); |
| 358 | content_deltify(rid, nrid, 0); |
| 359 | db_end_transaction(0); |
| 360 | cgi_redirectf("event?name=%T", zEventId); |
| 361 | } |
| 362 |
| --- src/event.c | |
| +++ src/event.c | |
| @@ -351,11 +351,11 @@ | |
| 351 | md5sum_blob(&event, &cksum); |
| 352 | blob_appendf(&event, "Z %b\n", &cksum); |
| 353 | blob_reset(&cksum); |
| 354 | nrid = content_put(&event); |
| 355 | db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nrid); |
| 356 | manifest_crosslink(nrid, &event, MC_NONE); |
| 357 | assert( blob_is_reset(&event) ); |
| 358 | content_deltify(rid, nrid, 0); |
| 359 | db_end_transaction(0); |
| 360 | cgi_redirectf("event?name=%T", zEventId); |
| 361 | } |
| 362 |
+12
-13
| --- src/http.c | ||
| +++ src/http.c | ||
| @@ -110,12 +110,11 @@ | ||
| 110 | 110 | blob_appendf(pHdr, "Authorization: Basic %s\r\n", zEncoded); |
| 111 | 111 | fossil_free(zEncoded); |
| 112 | 112 | fossil_free(zCredentials); |
| 113 | 113 | } |
| 114 | 114 | blob_appendf(pHdr, "Host: %s\r\n", g.urlHostname); |
| 115 | - blob_appendf(pHdr, "User-Agent: Fossil/" RELEASE_VERSION | |
| 116 | - " (" MANIFEST_DATE " " MANIFEST_VERSION ")\r\n"); | |
| 115 | + blob_appendf(pHdr, "User-Agent: %s\r\n", get_user_agent()); | |
| 117 | 116 | if( g.urlIsSsh ) blob_appendf(pHdr, "X-Fossil-Transport: SSH\r\n"); |
| 118 | 117 | if( g.fHttpTrace ){ |
| 119 | 118 | blob_appendf(pHdr, "Content-Type: application/x-fossil-debug\r\n"); |
| 120 | 119 | }else{ |
| 121 | 120 | blob_appendf(pHdr, "Content-Type: application/x-fossil\r\n"); |
| @@ -144,12 +143,12 @@ | ||
| 144 | 143 | char *zLine; /* A single line of the reply header */ |
| 145 | 144 | int i; /* Loop counter */ |
| 146 | 145 | int isError = 0; /* True if the reply is an error message */ |
| 147 | 146 | int isCompressed = 1; /* True if the reply is compressed */ |
| 148 | 147 | |
| 149 | - if( transport_open() ){ | |
| 150 | - fossil_warning(transport_errmsg()); | |
| 148 | + if( transport_open(GLOBAL_URL()) ){ | |
| 149 | + fossil_warning(transport_errmsg(GLOBAL_URL())); | |
| 151 | 150 | return 1; |
| 152 | 151 | } |
| 153 | 152 | |
| 154 | 153 | /* Construct the login card and prepare the complete payload */ |
| 155 | 154 | blob_zero(&login); |
| @@ -191,22 +190,22 @@ | ||
| 191 | 190 | } |
| 192 | 191 | |
| 193 | 192 | /* |
| 194 | 193 | ** Send the request to the server. |
| 195 | 194 | */ |
| 196 | - transport_send(&hdr); | |
| 197 | - transport_send(&payload); | |
| 195 | + transport_send(GLOBAL_URL(), &hdr); | |
| 196 | + transport_send(GLOBAL_URL(), &payload); | |
| 198 | 197 | blob_reset(&hdr); |
| 199 | 198 | blob_reset(&payload); |
| 200 | - transport_flip(); | |
| 199 | + transport_flip(GLOBAL_URL()); | |
| 201 | 200 | |
| 202 | 201 | /* |
| 203 | 202 | ** Read and interpret the server reply |
| 204 | 203 | */ |
| 205 | 204 | closeConnection = 1; |
| 206 | 205 | iLength = -1; |
| 207 | - while( (zLine = transport_receive_line())!=0 && zLine[0]!=0 ){ | |
| 206 | + while( (zLine = transport_receive_line(GLOBAL_URL()))!=0 && zLine[0]!=0 ){ | |
| 208 | 207 | /* printf("[%s]\n", zLine); fflush(stdout); */ |
| 209 | 208 | if( fossil_strnicmp(zLine, "http/1.", 7)==0 ){ |
| 210 | 209 | if( sscanf(zLine, "HTTP/1.%d %d", &iHttpVersion, &rc)!=2 ) goto write_err; |
| 211 | 210 | if( rc!=200 && rc!=302 ){ |
| 212 | 211 | int ii; |
| @@ -255,11 +254,11 @@ | ||
| 255 | 254 | j -= 4; |
| 256 | 255 | zLine[j] = 0; |
| 257 | 256 | } |
| 258 | 257 | fossil_print("redirect to %s\n", &zLine[i]); |
| 259 | 258 | url_parse(&zLine[i], 0); |
| 260 | - transport_close(); | |
| 259 | + transport_close(GLOBAL_URL()); | |
| 261 | 260 | return http_exchange(pSend, pReply, useLogin, maxRedirect); |
| 262 | 261 | }else if( fossil_strnicmp(zLine, "content-type: ", 14)==0 ){ |
| 263 | 262 | if( fossil_strnicmp(&zLine[14], "application/x-fossil-debug", -1)==0 ){ |
| 264 | 263 | isCompressed = 0; |
| 265 | 264 | }else if( fossil_strnicmp(&zLine[14], |
| @@ -282,11 +281,11 @@ | ||
| 282 | 281 | /* |
| 283 | 282 | ** Extract the reply payload that follows the header |
| 284 | 283 | */ |
| 285 | 284 | blob_zero(pReply); |
| 286 | 285 | blob_resize(pReply, iLength); |
| 287 | - iLength = transport_receive(blob_buffer(pReply), iLength); | |
| 286 | + iLength = transport_receive(GLOBAL_URL(), blob_buffer(pReply), iLength); | |
| 288 | 287 | blob_resize(pReply, iLength); |
| 289 | 288 | if( isError ){ |
| 290 | 289 | char *z; |
| 291 | 290 | int i, j; |
| 292 | 291 | z = blob_str(pReply); |
| @@ -311,18 +310,18 @@ | ||
| 311 | 310 | ** |
| 312 | 311 | ** For SSH we will leave the connection open. |
| 313 | 312 | */ |
| 314 | 313 | if( ! g.urlIsSsh ) closeConnection = 1; /* FIX ME */ |
| 315 | 314 | if( closeConnection ){ |
| 316 | - transport_close(); | |
| 315 | + transport_close(GLOBAL_URL()); | |
| 317 | 316 | }else{ |
| 318 | - transport_rewind(); | |
| 317 | + transport_rewind(GLOBAL_URL()); | |
| 319 | 318 | } |
| 320 | 319 | return 0; |
| 321 | 320 | |
| 322 | 321 | /* |
| 323 | 322 | ** Jump to here if an error is seen. |
| 324 | 323 | */ |
| 325 | 324 | write_err: |
| 326 | - transport_close(); | |
| 325 | + transport_close(GLOBAL_URL()); | |
| 327 | 326 | return 1; |
| 328 | 327 | } |
| 329 | 328 |
| --- src/http.c | |
| +++ src/http.c | |
| @@ -110,12 +110,11 @@ | |
| 110 | blob_appendf(pHdr, "Authorization: Basic %s\r\n", zEncoded); |
| 111 | fossil_free(zEncoded); |
| 112 | fossil_free(zCredentials); |
| 113 | } |
| 114 | blob_appendf(pHdr, "Host: %s\r\n", g.urlHostname); |
| 115 | blob_appendf(pHdr, "User-Agent: Fossil/" RELEASE_VERSION |
| 116 | " (" MANIFEST_DATE " " MANIFEST_VERSION ")\r\n"); |
| 117 | if( g.urlIsSsh ) blob_appendf(pHdr, "X-Fossil-Transport: SSH\r\n"); |
| 118 | if( g.fHttpTrace ){ |
| 119 | blob_appendf(pHdr, "Content-Type: application/x-fossil-debug\r\n"); |
| 120 | }else{ |
| 121 | blob_appendf(pHdr, "Content-Type: application/x-fossil\r\n"); |
| @@ -144,12 +143,12 @@ | |
| 144 | char *zLine; /* A single line of the reply header */ |
| 145 | int i; /* Loop counter */ |
| 146 | int isError = 0; /* True if the reply is an error message */ |
| 147 | int isCompressed = 1; /* True if the reply is compressed */ |
| 148 | |
| 149 | if( transport_open() ){ |
| 150 | fossil_warning(transport_errmsg()); |
| 151 | return 1; |
| 152 | } |
| 153 | |
| 154 | /* Construct the login card and prepare the complete payload */ |
| 155 | blob_zero(&login); |
| @@ -191,22 +190,22 @@ | |
| 191 | } |
| 192 | |
| 193 | /* |
| 194 | ** Send the request to the server. |
| 195 | */ |
| 196 | transport_send(&hdr); |
| 197 | transport_send(&payload); |
| 198 | blob_reset(&hdr); |
| 199 | blob_reset(&payload); |
| 200 | transport_flip(); |
| 201 | |
| 202 | /* |
| 203 | ** Read and interpret the server reply |
| 204 | */ |
| 205 | closeConnection = 1; |
| 206 | iLength = -1; |
| 207 | while( (zLine = transport_receive_line())!=0 && zLine[0]!=0 ){ |
| 208 | /* printf("[%s]\n", zLine); fflush(stdout); */ |
| 209 | if( fossil_strnicmp(zLine, "http/1.", 7)==0 ){ |
| 210 | if( sscanf(zLine, "HTTP/1.%d %d", &iHttpVersion, &rc)!=2 ) goto write_err; |
| 211 | if( rc!=200 && rc!=302 ){ |
| 212 | int ii; |
| @@ -255,11 +254,11 @@ | |
| 255 | j -= 4; |
| 256 | zLine[j] = 0; |
| 257 | } |
| 258 | fossil_print("redirect to %s\n", &zLine[i]); |
| 259 | url_parse(&zLine[i], 0); |
| 260 | transport_close(); |
| 261 | return http_exchange(pSend, pReply, useLogin, maxRedirect); |
| 262 | }else if( fossil_strnicmp(zLine, "content-type: ", 14)==0 ){ |
| 263 | if( fossil_strnicmp(&zLine[14], "application/x-fossil-debug", -1)==0 ){ |
| 264 | isCompressed = 0; |
| 265 | }else if( fossil_strnicmp(&zLine[14], |
| @@ -282,11 +281,11 @@ | |
| 282 | /* |
| 283 | ** Extract the reply payload that follows the header |
| 284 | */ |
| 285 | blob_zero(pReply); |
| 286 | blob_resize(pReply, iLength); |
| 287 | iLength = transport_receive(blob_buffer(pReply), iLength); |
| 288 | blob_resize(pReply, iLength); |
| 289 | if( isError ){ |
| 290 | char *z; |
| 291 | int i, j; |
| 292 | z = blob_str(pReply); |
| @@ -311,18 +310,18 @@ | |
| 311 | ** |
| 312 | ** For SSH we will leave the connection open. |
| 313 | */ |
| 314 | if( ! g.urlIsSsh ) closeConnection = 1; /* FIX ME */ |
| 315 | if( closeConnection ){ |
| 316 | transport_close(); |
| 317 | }else{ |
| 318 | transport_rewind(); |
| 319 | } |
| 320 | return 0; |
| 321 | |
| 322 | /* |
| 323 | ** Jump to here if an error is seen. |
| 324 | */ |
| 325 | write_err: |
| 326 | transport_close(); |
| 327 | return 1; |
| 328 | } |
| 329 |
| --- src/http.c | |
| +++ src/http.c | |
| @@ -110,12 +110,11 @@ | |
| 110 | blob_appendf(pHdr, "Authorization: Basic %s\r\n", zEncoded); |
| 111 | fossil_free(zEncoded); |
| 112 | fossil_free(zCredentials); |
| 113 | } |
| 114 | blob_appendf(pHdr, "Host: %s\r\n", g.urlHostname); |
| 115 | blob_appendf(pHdr, "User-Agent: %s\r\n", get_user_agent()); |
| 116 | if( g.urlIsSsh ) blob_appendf(pHdr, "X-Fossil-Transport: SSH\r\n"); |
| 117 | if( g.fHttpTrace ){ |
| 118 | blob_appendf(pHdr, "Content-Type: application/x-fossil-debug\r\n"); |
| 119 | }else{ |
| 120 | blob_appendf(pHdr, "Content-Type: application/x-fossil\r\n"); |
| @@ -144,12 +143,12 @@ | |
| 143 | char *zLine; /* A single line of the reply header */ |
| 144 | int i; /* Loop counter */ |
| 145 | int isError = 0; /* True if the reply is an error message */ |
| 146 | int isCompressed = 1; /* True if the reply is compressed */ |
| 147 | |
| 148 | if( transport_open(GLOBAL_URL()) ){ |
| 149 | fossil_warning(transport_errmsg(GLOBAL_URL())); |
| 150 | return 1; |
| 151 | } |
| 152 | |
| 153 | /* Construct the login card and prepare the complete payload */ |
| 154 | blob_zero(&login); |
| @@ -191,22 +190,22 @@ | |
| 190 | } |
| 191 | |
| 192 | /* |
| 193 | ** Send the request to the server. |
| 194 | */ |
| 195 | transport_send(GLOBAL_URL(), &hdr); |
| 196 | transport_send(GLOBAL_URL(), &payload); |
| 197 | blob_reset(&hdr); |
| 198 | blob_reset(&payload); |
| 199 | transport_flip(GLOBAL_URL()); |
| 200 | |
| 201 | /* |
| 202 | ** Read and interpret the server reply |
| 203 | */ |
| 204 | closeConnection = 1; |
| 205 | iLength = -1; |
| 206 | while( (zLine = transport_receive_line(GLOBAL_URL()))!=0 && zLine[0]!=0 ){ |
| 207 | /* printf("[%s]\n", zLine); fflush(stdout); */ |
| 208 | if( fossil_strnicmp(zLine, "http/1.", 7)==0 ){ |
| 209 | if( sscanf(zLine, "HTTP/1.%d %d", &iHttpVersion, &rc)!=2 ) goto write_err; |
| 210 | if( rc!=200 && rc!=302 ){ |
| 211 | int ii; |
| @@ -255,11 +254,11 @@ | |
| 254 | j -= 4; |
| 255 | zLine[j] = 0; |
| 256 | } |
| 257 | fossil_print("redirect to %s\n", &zLine[i]); |
| 258 | url_parse(&zLine[i], 0); |
| 259 | transport_close(GLOBAL_URL()); |
| 260 | return http_exchange(pSend, pReply, useLogin, maxRedirect); |
| 261 | }else if( fossil_strnicmp(zLine, "content-type: ", 14)==0 ){ |
| 262 | if( fossil_strnicmp(&zLine[14], "application/x-fossil-debug", -1)==0 ){ |
| 263 | isCompressed = 0; |
| 264 | }else if( fossil_strnicmp(&zLine[14], |
| @@ -282,11 +281,11 @@ | |
| 281 | /* |
| 282 | ** Extract the reply payload that follows the header |
| 283 | */ |
| 284 | blob_zero(pReply); |
| 285 | blob_resize(pReply, iLength); |
| 286 | iLength = transport_receive(GLOBAL_URL(), blob_buffer(pReply), iLength); |
| 287 | blob_resize(pReply, iLength); |
| 288 | if( isError ){ |
| 289 | char *z; |
| 290 | int i, j; |
| 291 | z = blob_str(pReply); |
| @@ -311,18 +310,18 @@ | |
| 310 | ** |
| 311 | ** For SSH we will leave the connection open. |
| 312 | */ |
| 313 | if( ! g.urlIsSsh ) closeConnection = 1; /* FIX ME */ |
| 314 | if( closeConnection ){ |
| 315 | transport_close(GLOBAL_URL()); |
| 316 | }else{ |
| 317 | transport_rewind(GLOBAL_URL()); |
| 318 | } |
| 319 | return 0; |
| 320 | |
| 321 | /* |
| 322 | ** Jump to here if an error is seen. |
| 323 | */ |
| 324 | write_err: |
| 325 | transport_close(GLOBAL_URL()); |
| 326 | return 1; |
| 327 | } |
| 328 |
+9
-8
| --- src/http_socket.c | ||
| +++ src/http_socket.c | ||
| @@ -131,29 +131,29 @@ | ||
| 131 | 131 | ** g.urlName Name of the server. Ex: www.fossil-scm.org |
| 132 | 132 | ** g.urlPort TCP/IP port to use. Ex: 80 |
| 133 | 133 | ** |
| 134 | 134 | ** Return the number of errors. |
| 135 | 135 | */ |
| 136 | -int socket_open(void){ | |
| 136 | +int socket_open(UrlData *pUrlData){ | |
| 137 | 137 | static struct sockaddr_in addr; /* The server address */ |
| 138 | 138 | static int addrIsInit = 0; /* True once addr is initialized */ |
| 139 | 139 | |
| 140 | 140 | socket_global_init(); |
| 141 | 141 | if( !addrIsInit ){ |
| 142 | 142 | addr.sin_family = AF_INET; |
| 143 | - addr.sin_port = htons(g.urlPort); | |
| 144 | - *(int*)&addr.sin_addr = inet_addr(g.urlName); | |
| 143 | + addr.sin_port = htons(pUrlData->port); | |
| 144 | + *(int*)&addr.sin_addr = inet_addr(pUrlData->name); | |
| 145 | 145 | if( -1 == *(int*)&addr.sin_addr ){ |
| 146 | 146 | #ifndef FOSSIL_STATIC_LINK |
| 147 | 147 | struct hostent *pHost; |
| 148 | - pHost = gethostbyname(g.urlName); | |
| 148 | + pHost = gethostbyname(pUrlData->name); | |
| 149 | 149 | if( pHost!=0 ){ |
| 150 | 150 | memcpy(&addr.sin_addr,pHost->h_addr_list[0],pHost->h_length); |
| 151 | 151 | }else |
| 152 | 152 | #endif |
| 153 | 153 | { |
| 154 | - socket_set_errmsg("can't resolve host name: %s", g.urlName); | |
| 154 | + socket_set_errmsg("can't resolve host name: %s", pUrlData->name); | |
| 155 | 155 | return 1; |
| 156 | 156 | } |
| 157 | 157 | } |
| 158 | 158 | addrIsInit = 1; |
| 159 | 159 | |
| @@ -167,11 +167,12 @@ | ||
| 167 | 167 | if( iSocket<0 ){ |
| 168 | 168 | socket_set_errmsg("cannot create a socket"); |
| 169 | 169 | return 1; |
| 170 | 170 | } |
| 171 | 171 | if( connect(iSocket,(struct sockaddr*)&addr,sizeof(addr))<0 ){ |
| 172 | - socket_set_errmsg("cannot connect to host %s:%d", g.urlName, g.urlPort); | |
| 172 | + socket_set_errmsg("cannot connect to host %s:%d", pUrlData->name, | |
| 173 | + pUrlData->port); | |
| 173 | 174 | socket_close(); |
| 174 | 175 | return 1; |
| 175 | 176 | } |
| 176 | 177 | #if !defined(_WIN32) |
| 177 | 178 | signal(SIGPIPE, SIG_IGN); |
| @@ -215,16 +216,16 @@ | ||
| 215 | 216 | /* |
| 216 | 217 | ** Attempt to resolve g.urlName to IP and setup g.zIpAddr so rcvfrom gets |
| 217 | 218 | ** populated. For hostnames with more than one IP (or if overridden in |
| 218 | 219 | ** ~/.ssh/config) the rcvfrom may not match the host to which we connect. |
| 219 | 220 | */ |
| 220 | -void socket_ssh_resolve_addr(void){ | |
| 221 | +void socket_ssh_resolve_addr(UrlData *pUrlData){ | |
| 221 | 222 | struct hostent *pHost; /* Used to make best effort for rcvfrom */ |
| 222 | 223 | struct sockaddr_in addr; |
| 223 | 224 | |
| 224 | 225 | memset(&addr, 0, sizeof(addr)); |
| 225 | - pHost = gethostbyname(g.urlName); | |
| 226 | + pHost = gethostbyname(pUrlData->name); | |
| 226 | 227 | if( pHost!=0 ){ |
| 227 | 228 | memcpy(&addr.sin_addr,pHost->h_addr_list[0],pHost->h_length); |
| 228 | 229 | g.zIpAddr = mprintf("%s", inet_ntoa(addr.sin_addr)); |
| 229 | 230 | } |
| 230 | 231 | } |
| 231 | 232 |
| --- src/http_socket.c | |
| +++ src/http_socket.c | |
| @@ -131,29 +131,29 @@ | |
| 131 | ** g.urlName Name of the server. Ex: www.fossil-scm.org |
| 132 | ** g.urlPort TCP/IP port to use. Ex: 80 |
| 133 | ** |
| 134 | ** Return the number of errors. |
| 135 | */ |
| 136 | int socket_open(void){ |
| 137 | static struct sockaddr_in addr; /* The server address */ |
| 138 | static int addrIsInit = 0; /* True once addr is initialized */ |
| 139 | |
| 140 | socket_global_init(); |
| 141 | if( !addrIsInit ){ |
| 142 | addr.sin_family = AF_INET; |
| 143 | addr.sin_port = htons(g.urlPort); |
| 144 | *(int*)&addr.sin_addr = inet_addr(g.urlName); |
| 145 | if( -1 == *(int*)&addr.sin_addr ){ |
| 146 | #ifndef FOSSIL_STATIC_LINK |
| 147 | struct hostent *pHost; |
| 148 | pHost = gethostbyname(g.urlName); |
| 149 | if( pHost!=0 ){ |
| 150 | memcpy(&addr.sin_addr,pHost->h_addr_list[0],pHost->h_length); |
| 151 | }else |
| 152 | #endif |
| 153 | { |
| 154 | socket_set_errmsg("can't resolve host name: %s", g.urlName); |
| 155 | return 1; |
| 156 | } |
| 157 | } |
| 158 | addrIsInit = 1; |
| 159 | |
| @@ -167,11 +167,12 @@ | |
| 167 | if( iSocket<0 ){ |
| 168 | socket_set_errmsg("cannot create a socket"); |
| 169 | return 1; |
| 170 | } |
| 171 | if( connect(iSocket,(struct sockaddr*)&addr,sizeof(addr))<0 ){ |
| 172 | socket_set_errmsg("cannot connect to host %s:%d", g.urlName, g.urlPort); |
| 173 | socket_close(); |
| 174 | return 1; |
| 175 | } |
| 176 | #if !defined(_WIN32) |
| 177 | signal(SIGPIPE, SIG_IGN); |
| @@ -215,16 +216,16 @@ | |
| 215 | /* |
| 216 | ** Attempt to resolve g.urlName to IP and setup g.zIpAddr so rcvfrom gets |
| 217 | ** populated. For hostnames with more than one IP (or if overridden in |
| 218 | ** ~/.ssh/config) the rcvfrom may not match the host to which we connect. |
| 219 | */ |
| 220 | void socket_ssh_resolve_addr(void){ |
| 221 | struct hostent *pHost; /* Used to make best effort for rcvfrom */ |
| 222 | struct sockaddr_in addr; |
| 223 | |
| 224 | memset(&addr, 0, sizeof(addr)); |
| 225 | pHost = gethostbyname(g.urlName); |
| 226 | if( pHost!=0 ){ |
| 227 | memcpy(&addr.sin_addr,pHost->h_addr_list[0],pHost->h_length); |
| 228 | g.zIpAddr = mprintf("%s", inet_ntoa(addr.sin_addr)); |
| 229 | } |
| 230 | } |
| 231 |
| --- src/http_socket.c | |
| +++ src/http_socket.c | |
| @@ -131,29 +131,29 @@ | |
| 131 | ** g.urlName Name of the server. Ex: www.fossil-scm.org |
| 132 | ** g.urlPort TCP/IP port to use. Ex: 80 |
| 133 | ** |
| 134 | ** Return the number of errors. |
| 135 | */ |
| 136 | int socket_open(UrlData *pUrlData){ |
| 137 | static struct sockaddr_in addr; /* The server address */ |
| 138 | static int addrIsInit = 0; /* True once addr is initialized */ |
| 139 | |
| 140 | socket_global_init(); |
| 141 | if( !addrIsInit ){ |
| 142 | addr.sin_family = AF_INET; |
| 143 | addr.sin_port = htons(pUrlData->port); |
| 144 | *(int*)&addr.sin_addr = inet_addr(pUrlData->name); |
| 145 | if( -1 == *(int*)&addr.sin_addr ){ |
| 146 | #ifndef FOSSIL_STATIC_LINK |
| 147 | struct hostent *pHost; |
| 148 | pHost = gethostbyname(pUrlData->name); |
| 149 | if( pHost!=0 ){ |
| 150 | memcpy(&addr.sin_addr,pHost->h_addr_list[0],pHost->h_length); |
| 151 | }else |
| 152 | #endif |
| 153 | { |
| 154 | socket_set_errmsg("can't resolve host name: %s", pUrlData->name); |
| 155 | return 1; |
| 156 | } |
| 157 | } |
| 158 | addrIsInit = 1; |
| 159 | |
| @@ -167,11 +167,12 @@ | |
| 167 | if( iSocket<0 ){ |
| 168 | socket_set_errmsg("cannot create a socket"); |
| 169 | return 1; |
| 170 | } |
| 171 | if( connect(iSocket,(struct sockaddr*)&addr,sizeof(addr))<0 ){ |
| 172 | socket_set_errmsg("cannot connect to host %s:%d", pUrlData->name, |
| 173 | pUrlData->port); |
| 174 | socket_close(); |
| 175 | return 1; |
| 176 | } |
| 177 | #if !defined(_WIN32) |
| 178 | signal(SIGPIPE, SIG_IGN); |
| @@ -215,16 +216,16 @@ | |
| 216 | /* |
| 217 | ** Attempt to resolve g.urlName to IP and setup g.zIpAddr so rcvfrom gets |
| 218 | ** populated. For hostnames with more than one IP (or if overridden in |
| 219 | ** ~/.ssh/config) the rcvfrom may not match the host to which we connect. |
| 220 | */ |
| 221 | void socket_ssh_resolve_addr(UrlData *pUrlData){ |
| 222 | struct hostent *pHost; /* Used to make best effort for rcvfrom */ |
| 223 | struct sockaddr_in addr; |
| 224 | |
| 225 | memset(&addr, 0, sizeof(addr)); |
| 226 | pHost = gethostbyname(pUrlData->name); |
| 227 | if( pHost!=0 ){ |
| 228 | memcpy(&addr.sin_addr,pHost->h_addr_list[0],pHost->h_length); |
| 229 | g.zIpAddr = mprintf("%s", inet_ntoa(addr.sin_addr)); |
| 230 | } |
| 231 | } |
| 232 |
+17
-15
| --- src/http_ssl.c | ||
| +++ src/http_ssl.c | ||
| @@ -183,11 +183,11 @@ | ||
| 183 | 183 | ** g.urlName Name of the server. Ex: www.fossil-scm.org |
| 184 | 184 | ** g.urlPort TCP/IP port to use. Ex: 80 |
| 185 | 185 | ** |
| 186 | 186 | ** Return the number of errors. |
| 187 | 187 | */ |
| 188 | -int ssl_open(void){ | |
| 188 | +int ssl_open(UrlData *pUrlData){ | |
| 189 | 189 | X509 *cert; |
| 190 | 190 | int hasSavedCertificate = 0; |
| 191 | 191 | int trusted = 0; |
| 192 | 192 | unsigned long e; |
| 193 | 193 | |
| @@ -194,11 +194,11 @@ | ||
| 194 | 194 | ssl_global_init(); |
| 195 | 195 | |
| 196 | 196 | /* Get certificate for current server from global config and |
| 197 | 197 | * (if we have it in config) add it to certificate store. |
| 198 | 198 | */ |
| 199 | - cert = ssl_get_certificate(&trusted); | |
| 199 | + cert = ssl_get_certificate(pUrlData, &trusted); | |
| 200 | 200 | if ( cert!=NULL ){ |
| 201 | 201 | X509_STORE_add_cert(SSL_CTX_get_cert_store(sslCtx), cert); |
| 202 | 202 | X509_free(cert); |
| 203 | 203 | hasSavedCertificate = 1; |
| 204 | 204 | } |
| @@ -205,11 +205,11 @@ | ||
| 205 | 205 | |
| 206 | 206 | iBio = BIO_new_ssl_connect(sslCtx); |
| 207 | 207 | BIO_get_ssl(iBio, &ssl); |
| 208 | 208 | |
| 209 | 209 | #if (SSLEAY_VERSION_NUMBER >= 0x00908070) && !defined(OPENSSL_NO_TLSEXT) |
| 210 | - if( !SSL_set_tlsext_host_name(ssl, g.urlName) ){ | |
| 210 | + if( !SSL_set_tlsext_host_name(ssl, pUrlData->name) ){ | |
| 211 | 211 | fossil_warning("WARNING: failed to set server name indication (SNI), " |
| 212 | 212 | "continuing without it.\n"); |
| 213 | 213 | } |
| 214 | 214 | #endif |
| 215 | 215 | |
| @@ -218,23 +218,25 @@ | ||
| 218 | 218 | ssl_set_errmsg("SSL: cannot open SSL (%s)", |
| 219 | 219 | ERR_reason_error_string(ERR_get_error())); |
| 220 | 220 | return 1; |
| 221 | 221 | } |
| 222 | 222 | |
| 223 | - BIO_set_conn_hostname(iBio, g.urlName); | |
| 224 | - BIO_set_conn_int_port(iBio, &g.urlPort); | |
| 223 | + BIO_set_conn_hostname(iBio, pUrlData->name); | |
| 224 | + BIO_set_conn_int_port(iBio, &pUrlData->port); | |
| 225 | 225 | |
| 226 | 226 | if( BIO_do_connect(iBio)<=0 ){ |
| 227 | 227 | ssl_set_errmsg("SSL: cannot connect to host %s:%d (%s)", |
| 228 | - g.urlName, g.urlPort, ERR_reason_error_string(ERR_get_error())); | |
| 228 | + pUrlData->name, pUrlData->port, | |
| 229 | + ERR_reason_error_string(ERR_get_error())); | |
| 229 | 230 | ssl_close(); |
| 230 | 231 | return 1; |
| 231 | 232 | } |
| 232 | 233 | |
| 233 | 234 | if( BIO_do_handshake(iBio)<=0 ) { |
| 234 | 235 | ssl_set_errmsg("Error establishing SSL connection %s:%d (%s)", |
| 235 | - g.urlName, g.urlPort, ERR_reason_error_string(ERR_get_error())); | |
| 236 | + pUrlData->name, pUrlData->port, | |
| 237 | + ERR_reason_error_string(ERR_get_error())); | |
| 236 | 238 | ssl_close(); |
| 237 | 239 | return 1; |
| 238 | 240 | } |
| 239 | 241 | /* Check if certificate is valid */ |
| 240 | 242 | cert = SSL_get_peer_certificate(ssl); |
| @@ -281,11 +283,11 @@ | ||
| 281 | 283 | " certificates list\n\n" |
| 282 | 284 | "If you are not expecting this message, answer no and " |
| 283 | 285 | "contact your server\nadministrator.\n\n" |
| 284 | 286 | "Accept certificate for host %s (a=always/y/N)? ", |
| 285 | 287 | X509_verify_cert_error_string(e), desc, warning, |
| 286 | - g.urlName); | |
| 288 | + pUrlData->name); | |
| 287 | 289 | BIO_free(mem); |
| 288 | 290 | |
| 289 | 291 | prompt_user(prompt, &ans); |
| 290 | 292 | free(prompt); |
| 291 | 293 | cReply = blob_str(&ans)[0]; |
| @@ -302,11 +304,11 @@ | ||
| 302 | 304 | &ans); |
| 303 | 305 | cReply = blob_str(&ans)[0]; |
| 304 | 306 | trusted = ( cReply=='a' || cReply=='A' ); |
| 305 | 307 | blob_reset(&ans); |
| 306 | 308 | } |
| 307 | - ssl_save_certificate(cert, trusted); | |
| 309 | + ssl_save_certificate(pUrlData, cert, trusted); | |
| 308 | 310 | } |
| 309 | 311 | } |
| 310 | 312 | |
| 311 | 313 | /* Set the Global.zIpAddr variable to the server we are talking to. |
| 312 | 314 | ** This is used to populate the ipaddr column of the rcvfrom table, |
| @@ -323,44 +325,44 @@ | ||
| 323 | 325 | } |
| 324 | 326 | |
| 325 | 327 | /* |
| 326 | 328 | ** Save certificate to global config. |
| 327 | 329 | */ |
| 328 | -void ssl_save_certificate(X509 *cert, int trusted){ | |
| 330 | +void ssl_save_certificate(UrlData *pUrlData, X509 *cert, int trusted){ | |
| 329 | 331 | BIO *mem; |
| 330 | 332 | char *zCert, *zHost; |
| 331 | 333 | |
| 332 | 334 | mem = BIO_new(BIO_s_mem()); |
| 333 | 335 | PEM_write_bio_X509(mem, cert); |
| 334 | 336 | BIO_write(mem, "", 1); /* nul-terminate mem buffer */ |
| 335 | 337 | BIO_get_mem_data(mem, &zCert); |
| 336 | - zHost = mprintf("cert:%s", g.urlName); | |
| 338 | + zHost = mprintf("cert:%s", pUrlData->name); | |
| 337 | 339 | db_set(zHost, zCert, 1); |
| 338 | 340 | free(zHost); |
| 339 | - zHost = mprintf("trusted:%s", g.urlName); | |
| 341 | + zHost = mprintf("trusted:%s", pUrlData->name); | |
| 340 | 342 | db_set_int(zHost, trusted, 1); |
| 341 | 343 | free(zHost); |
| 342 | 344 | BIO_free(mem); |
| 343 | 345 | } |
| 344 | 346 | |
| 345 | 347 | /* |
| 346 | 348 | ** Get certificate for g.urlName from global config. |
| 347 | 349 | ** Return NULL if no certificate found. |
| 348 | 350 | */ |
| 349 | -X509 *ssl_get_certificate(int *pTrusted){ | |
| 351 | +X509 *ssl_get_certificate(UrlData *pUrlData, int *pTrusted){ | |
| 350 | 352 | char *zHost, *zCert; |
| 351 | 353 | BIO *mem; |
| 352 | 354 | X509 *cert; |
| 353 | 355 | |
| 354 | - zHost = mprintf("cert:%s", g.urlName); | |
| 356 | + zHost = mprintf("cert:%s", pUrlData->name); | |
| 355 | 357 | zCert = db_get(zHost, NULL); |
| 356 | 358 | free(zHost); |
| 357 | 359 | if ( zCert==NULL ) |
| 358 | 360 | return NULL; |
| 359 | 361 | |
| 360 | 362 | if ( pTrusted!=0 ){ |
| 361 | - zHost = mprintf("trusted:%s", g.urlName); | |
| 363 | + zHost = mprintf("trusted:%s", pUrlData->name); | |
| 362 | 364 | *pTrusted = db_get_int(zHost, 0); |
| 363 | 365 | free(zHost); |
| 364 | 366 | } |
| 365 | 367 | |
| 366 | 368 | mem = BIO_new(BIO_s_mem()); |
| 367 | 369 |
| --- src/http_ssl.c | |
| +++ src/http_ssl.c | |
| @@ -183,11 +183,11 @@ | |
| 183 | ** g.urlName Name of the server. Ex: www.fossil-scm.org |
| 184 | ** g.urlPort TCP/IP port to use. Ex: 80 |
| 185 | ** |
| 186 | ** Return the number of errors. |
| 187 | */ |
| 188 | int ssl_open(void){ |
| 189 | X509 *cert; |
| 190 | int hasSavedCertificate = 0; |
| 191 | int trusted = 0; |
| 192 | unsigned long e; |
| 193 | |
| @@ -194,11 +194,11 @@ | |
| 194 | ssl_global_init(); |
| 195 | |
| 196 | /* Get certificate for current server from global config and |
| 197 | * (if we have it in config) add it to certificate store. |
| 198 | */ |
| 199 | cert = ssl_get_certificate(&trusted); |
| 200 | if ( cert!=NULL ){ |
| 201 | X509_STORE_add_cert(SSL_CTX_get_cert_store(sslCtx), cert); |
| 202 | X509_free(cert); |
| 203 | hasSavedCertificate = 1; |
| 204 | } |
| @@ -205,11 +205,11 @@ | |
| 205 | |
| 206 | iBio = BIO_new_ssl_connect(sslCtx); |
| 207 | BIO_get_ssl(iBio, &ssl); |
| 208 | |
| 209 | #if (SSLEAY_VERSION_NUMBER >= 0x00908070) && !defined(OPENSSL_NO_TLSEXT) |
| 210 | if( !SSL_set_tlsext_host_name(ssl, g.urlName) ){ |
| 211 | fossil_warning("WARNING: failed to set server name indication (SNI), " |
| 212 | "continuing without it.\n"); |
| 213 | } |
| 214 | #endif |
| 215 | |
| @@ -218,23 +218,25 @@ | |
| 218 | ssl_set_errmsg("SSL: cannot open SSL (%s)", |
| 219 | ERR_reason_error_string(ERR_get_error())); |
| 220 | return 1; |
| 221 | } |
| 222 | |
| 223 | BIO_set_conn_hostname(iBio, g.urlName); |
| 224 | BIO_set_conn_int_port(iBio, &g.urlPort); |
| 225 | |
| 226 | if( BIO_do_connect(iBio)<=0 ){ |
| 227 | ssl_set_errmsg("SSL: cannot connect to host %s:%d (%s)", |
| 228 | g.urlName, g.urlPort, ERR_reason_error_string(ERR_get_error())); |
| 229 | ssl_close(); |
| 230 | return 1; |
| 231 | } |
| 232 | |
| 233 | if( BIO_do_handshake(iBio)<=0 ) { |
| 234 | ssl_set_errmsg("Error establishing SSL connection %s:%d (%s)", |
| 235 | g.urlName, g.urlPort, ERR_reason_error_string(ERR_get_error())); |
| 236 | ssl_close(); |
| 237 | return 1; |
| 238 | } |
| 239 | /* Check if certificate is valid */ |
| 240 | cert = SSL_get_peer_certificate(ssl); |
| @@ -281,11 +283,11 @@ | |
| 281 | " certificates list\n\n" |
| 282 | "If you are not expecting this message, answer no and " |
| 283 | "contact your server\nadministrator.\n\n" |
| 284 | "Accept certificate for host %s (a=always/y/N)? ", |
| 285 | X509_verify_cert_error_string(e), desc, warning, |
| 286 | g.urlName); |
| 287 | BIO_free(mem); |
| 288 | |
| 289 | prompt_user(prompt, &ans); |
| 290 | free(prompt); |
| 291 | cReply = blob_str(&ans)[0]; |
| @@ -302,11 +304,11 @@ | |
| 302 | &ans); |
| 303 | cReply = blob_str(&ans)[0]; |
| 304 | trusted = ( cReply=='a' || cReply=='A' ); |
| 305 | blob_reset(&ans); |
| 306 | } |
| 307 | ssl_save_certificate(cert, trusted); |
| 308 | } |
| 309 | } |
| 310 | |
| 311 | /* Set the Global.zIpAddr variable to the server we are talking to. |
| 312 | ** This is used to populate the ipaddr column of the rcvfrom table, |
| @@ -323,44 +325,44 @@ | |
| 323 | } |
| 324 | |
| 325 | /* |
| 326 | ** Save certificate to global config. |
| 327 | */ |
| 328 | void ssl_save_certificate(X509 *cert, int trusted){ |
| 329 | BIO *mem; |
| 330 | char *zCert, *zHost; |
| 331 | |
| 332 | mem = BIO_new(BIO_s_mem()); |
| 333 | PEM_write_bio_X509(mem, cert); |
| 334 | BIO_write(mem, "", 1); /* nul-terminate mem buffer */ |
| 335 | BIO_get_mem_data(mem, &zCert); |
| 336 | zHost = mprintf("cert:%s", g.urlName); |
| 337 | db_set(zHost, zCert, 1); |
| 338 | free(zHost); |
| 339 | zHost = mprintf("trusted:%s", g.urlName); |
| 340 | db_set_int(zHost, trusted, 1); |
| 341 | free(zHost); |
| 342 | BIO_free(mem); |
| 343 | } |
| 344 | |
| 345 | /* |
| 346 | ** Get certificate for g.urlName from global config. |
| 347 | ** Return NULL if no certificate found. |
| 348 | */ |
| 349 | X509 *ssl_get_certificate(int *pTrusted){ |
| 350 | char *zHost, *zCert; |
| 351 | BIO *mem; |
| 352 | X509 *cert; |
| 353 | |
| 354 | zHost = mprintf("cert:%s", g.urlName); |
| 355 | zCert = db_get(zHost, NULL); |
| 356 | free(zHost); |
| 357 | if ( zCert==NULL ) |
| 358 | return NULL; |
| 359 | |
| 360 | if ( pTrusted!=0 ){ |
| 361 | zHost = mprintf("trusted:%s", g.urlName); |
| 362 | *pTrusted = db_get_int(zHost, 0); |
| 363 | free(zHost); |
| 364 | } |
| 365 | |
| 366 | mem = BIO_new(BIO_s_mem()); |
| 367 |
| --- src/http_ssl.c | |
| +++ src/http_ssl.c | |
| @@ -183,11 +183,11 @@ | |
| 183 | ** g.urlName Name of the server. Ex: www.fossil-scm.org |
| 184 | ** g.urlPort TCP/IP port to use. Ex: 80 |
| 185 | ** |
| 186 | ** Return the number of errors. |
| 187 | */ |
| 188 | int ssl_open(UrlData *pUrlData){ |
| 189 | X509 *cert; |
| 190 | int hasSavedCertificate = 0; |
| 191 | int trusted = 0; |
| 192 | unsigned long e; |
| 193 | |
| @@ -194,11 +194,11 @@ | |
| 194 | ssl_global_init(); |
| 195 | |
| 196 | /* Get certificate for current server from global config and |
| 197 | * (if we have it in config) add it to certificate store. |
| 198 | */ |
| 199 | cert = ssl_get_certificate(pUrlData, &trusted); |
| 200 | if ( cert!=NULL ){ |
| 201 | X509_STORE_add_cert(SSL_CTX_get_cert_store(sslCtx), cert); |
| 202 | X509_free(cert); |
| 203 | hasSavedCertificate = 1; |
| 204 | } |
| @@ -205,11 +205,11 @@ | |
| 205 | |
| 206 | iBio = BIO_new_ssl_connect(sslCtx); |
| 207 | BIO_get_ssl(iBio, &ssl); |
| 208 | |
| 209 | #if (SSLEAY_VERSION_NUMBER >= 0x00908070) && !defined(OPENSSL_NO_TLSEXT) |
| 210 | if( !SSL_set_tlsext_host_name(ssl, pUrlData->name) ){ |
| 211 | fossil_warning("WARNING: failed to set server name indication (SNI), " |
| 212 | "continuing without it.\n"); |
| 213 | } |
| 214 | #endif |
| 215 | |
| @@ -218,23 +218,25 @@ | |
| 218 | ssl_set_errmsg("SSL: cannot open SSL (%s)", |
| 219 | ERR_reason_error_string(ERR_get_error())); |
| 220 | return 1; |
| 221 | } |
| 222 | |
| 223 | BIO_set_conn_hostname(iBio, pUrlData->name); |
| 224 | BIO_set_conn_int_port(iBio, &pUrlData->port); |
| 225 | |
| 226 | if( BIO_do_connect(iBio)<=0 ){ |
| 227 | ssl_set_errmsg("SSL: cannot connect to host %s:%d (%s)", |
| 228 | pUrlData->name, pUrlData->port, |
| 229 | ERR_reason_error_string(ERR_get_error())); |
| 230 | ssl_close(); |
| 231 | return 1; |
| 232 | } |
| 233 | |
| 234 | if( BIO_do_handshake(iBio)<=0 ) { |
| 235 | ssl_set_errmsg("Error establishing SSL connection %s:%d (%s)", |
| 236 | pUrlData->name, pUrlData->port, |
| 237 | ERR_reason_error_string(ERR_get_error())); |
| 238 | ssl_close(); |
| 239 | return 1; |
| 240 | } |
| 241 | /* Check if certificate is valid */ |
| 242 | cert = SSL_get_peer_certificate(ssl); |
| @@ -281,11 +283,11 @@ | |
| 283 | " certificates list\n\n" |
| 284 | "If you are not expecting this message, answer no and " |
| 285 | "contact your server\nadministrator.\n\n" |
| 286 | "Accept certificate for host %s (a=always/y/N)? ", |
| 287 | X509_verify_cert_error_string(e), desc, warning, |
| 288 | pUrlData->name); |
| 289 | BIO_free(mem); |
| 290 | |
| 291 | prompt_user(prompt, &ans); |
| 292 | free(prompt); |
| 293 | cReply = blob_str(&ans)[0]; |
| @@ -302,11 +304,11 @@ | |
| 304 | &ans); |
| 305 | cReply = blob_str(&ans)[0]; |
| 306 | trusted = ( cReply=='a' || cReply=='A' ); |
| 307 | blob_reset(&ans); |
| 308 | } |
| 309 | ssl_save_certificate(pUrlData, cert, trusted); |
| 310 | } |
| 311 | } |
| 312 | |
| 313 | /* Set the Global.zIpAddr variable to the server we are talking to. |
| 314 | ** This is used to populate the ipaddr column of the rcvfrom table, |
| @@ -323,44 +325,44 @@ | |
| 325 | } |
| 326 | |
| 327 | /* |
| 328 | ** Save certificate to global config. |
| 329 | */ |
| 330 | void ssl_save_certificate(UrlData *pUrlData, X509 *cert, int trusted){ |
| 331 | BIO *mem; |
| 332 | char *zCert, *zHost; |
| 333 | |
| 334 | mem = BIO_new(BIO_s_mem()); |
| 335 | PEM_write_bio_X509(mem, cert); |
| 336 | BIO_write(mem, "", 1); /* nul-terminate mem buffer */ |
| 337 | BIO_get_mem_data(mem, &zCert); |
| 338 | zHost = mprintf("cert:%s", pUrlData->name); |
| 339 | db_set(zHost, zCert, 1); |
| 340 | free(zHost); |
| 341 | zHost = mprintf("trusted:%s", pUrlData->name); |
| 342 | db_set_int(zHost, trusted, 1); |
| 343 | free(zHost); |
| 344 | BIO_free(mem); |
| 345 | } |
| 346 | |
| 347 | /* |
| 348 | ** Get certificate for g.urlName from global config. |
| 349 | ** Return NULL if no certificate found. |
| 350 | */ |
| 351 | X509 *ssl_get_certificate(UrlData *pUrlData, int *pTrusted){ |
| 352 | char *zHost, *zCert; |
| 353 | BIO *mem; |
| 354 | X509 *cert; |
| 355 | |
| 356 | zHost = mprintf("cert:%s", pUrlData->name); |
| 357 | zCert = db_get(zHost, NULL); |
| 358 | free(zHost); |
| 359 | if ( zCert==NULL ) |
| 360 | return NULL; |
| 361 | |
| 362 | if ( pTrusted!=0 ){ |
| 363 | zHost = mprintf("trusted:%s", pUrlData->name); |
| 364 | *pTrusted = db_get_int(zHost, 0); |
| 365 | free(zHost); |
| 366 | } |
| 367 | |
| 368 | mem = BIO_new(BIO_s_mem()); |
| 369 |
+46
-46
| --- src/http_transport.c | ||
| +++ src/http_transport.c | ||
| @@ -52,13 +52,13 @@ | ||
| 52 | 52 | |
| 53 | 53 | |
| 54 | 54 | /* |
| 55 | 55 | ** Return the current transport error message. |
| 56 | 56 | */ |
| 57 | -const char *transport_errmsg(void){ | |
| 57 | +const char *transport_errmsg(UrlData *pUrlData){ | |
| 58 | 58 | #ifdef FOSSIL_ENABLE_SSL |
| 59 | - if( g.urlIsHttps ){ | |
| 59 | + if( pUrlData->isHttps ){ | |
| 60 | 60 | return ssl_errmsg(); |
| 61 | 61 | } |
| 62 | 62 | #endif |
| 63 | 63 | return socket_errmsg(); |
| 64 | 64 | } |
| @@ -86,47 +86,47 @@ | ||
| 86 | 86 | #endif |
| 87 | 87 | |
| 88 | 88 | /* |
| 89 | 89 | ** SSH initialization of the transport layer |
| 90 | 90 | */ |
| 91 | -int transport_ssh_open(void){ | |
| 91 | +int transport_ssh_open(UrlData *pUrlData){ | |
| 92 | 92 | /* For SSH we need to create and run SSH fossil http |
| 93 | 93 | ** to talk to the remote machine. |
| 94 | 94 | */ |
| 95 | 95 | const char *zSsh; /* The base SSH command */ |
| 96 | 96 | Blob zCmd; /* The SSH command */ |
| 97 | 97 | char *zHost; /* The host name to contact */ |
| 98 | 98 | int n; /* Size of prefix string */ |
| 99 | 99 | |
| 100 | - socket_ssh_resolve_addr(); | |
| 100 | + socket_ssh_resolve_addr(pUrlData); | |
| 101 | 101 | zSsh = db_get("ssh-command", zDefaultSshCmd); |
| 102 | 102 | blob_init(&zCmd, zSsh, -1); |
| 103 | - if( g.urlPort!=g.urlDfltPort && g.urlPort ){ | |
| 103 | + if( pUrlData->port!=pUrlData->dfltPort && pUrlData->port ){ | |
| 104 | 104 | #ifdef __MINGW32__ |
| 105 | - blob_appendf(&zCmd, " -P %d", g.urlPort); | |
| 105 | + blob_appendf(&zCmd, " -P %d", pUrlData->port); | |
| 106 | 106 | #else |
| 107 | - blob_appendf(&zCmd, " -p %d", g.urlPort); | |
| 107 | + blob_appendf(&zCmd, " -p %d", pUrlData->port); | |
| 108 | 108 | #endif |
| 109 | 109 | } |
| 110 | 110 | if( g.fSshTrace ){ |
| 111 | 111 | fossil_force_newline(); |
| 112 | 112 | fossil_print("%s", blob_str(&zCmd)); /* Show the base of the SSH command */ |
| 113 | 113 | } |
| 114 | - if( g.urlUser && g.urlUser[0] ){ | |
| 115 | - zHost = mprintf("%s@%s", g.urlUser, g.urlName); | |
| 114 | + if( pUrlData->user && pUrlData->user[0] ){ | |
| 115 | + zHost = mprintf("%s@%s", pUrlData->user, pUrlData->name); | |
| 116 | 116 | }else{ |
| 117 | - zHost = mprintf("%s", g.urlName); | |
| 117 | + zHost = mprintf("%s", pUrlData->name); | |
| 118 | 118 | } |
| 119 | 119 | n = blob_size(&zCmd); |
| 120 | 120 | blob_append(&zCmd, " ", 1); |
| 121 | 121 | shell_escape(&zCmd, zHost); |
| 122 | 122 | blob_append(&zCmd, " ", 1); |
| 123 | - shell_escape(&zCmd, mprintf("%s", g.urlFossil)); | |
| 123 | + shell_escape(&zCmd, mprintf("%s", pUrlData->fossil)); | |
| 124 | 124 | blob_append(&zCmd, " test-http", 10); |
| 125 | - if( g.urlPath && g.urlPath[0] ){ | |
| 125 | + if( pUrlData->path && pUrlData->path[0] ){ | |
| 126 | 126 | blob_append(&zCmd, " ", 1); |
| 127 | - shell_escape(&zCmd, mprintf("%s", g.urlPath)); | |
| 127 | + shell_escape(&zCmd, mprintf("%s", pUrlData->path)); | |
| 128 | 128 | } |
| 129 | 129 | if( g.fSshTrace ){ |
| 130 | 130 | fossil_print("%s\n", blob_str(&zCmd)+n); /* Show tail of SSH command */ |
| 131 | 131 | } |
| 132 | 132 | free(zHost); |
| @@ -146,25 +146,25 @@ | ||
| 146 | 146 | ** g.urlPort TCP/IP port. Ex: 80 |
| 147 | 147 | ** g.urlIsHttps Use TLS for the connection |
| 148 | 148 | ** |
| 149 | 149 | ** Return the number of errors. |
| 150 | 150 | */ |
| 151 | -int transport_open(void){ | |
| 151 | +int transport_open(UrlData *pUrlData){ | |
| 152 | 152 | int rc = 0; |
| 153 | 153 | if( transport.isOpen==0 ){ |
| 154 | - if( g.urlIsSsh ){ | |
| 155 | - rc = transport_ssh_open(); | |
| 154 | + if( pUrlData->isSsh ){ | |
| 155 | + rc = transport_ssh_open(pUrlData); | |
| 156 | 156 | if( rc==0 ) transport.isOpen = 1; |
| 157 | - }else if( g.urlIsHttps ){ | |
| 157 | + }else if( pUrlData->isHttps ){ | |
| 158 | 158 | #ifdef FOSSIL_ENABLE_SSL |
| 159 | - rc = ssl_open(); | |
| 159 | + rc = ssl_open(pUrlData); | |
| 160 | 160 | if( rc==0 ) transport.isOpen = 1; |
| 161 | 161 | #else |
| 162 | 162 | socket_set_errmsg("HTTPS: Fossil has been compiled without SSL support"); |
| 163 | 163 | rc = 1; |
| 164 | 164 | #endif |
| 165 | - }else if( g.urlIsFile ){ | |
| 165 | + }else if( pUrlData->isFile ){ | |
| 166 | 166 | sqlite3_uint64 iRandId; |
| 167 | 167 | sqlite3_randomness(sizeof(iRandId), &iRandId); |
| 168 | 168 | transport.zOutFile = mprintf("%s-%llu-out.http", |
| 169 | 169 | g.zRepositoryName, iRandId); |
| 170 | 170 | transport.zInFile = mprintf("%s-%llu-in.http", |
| @@ -173,21 +173,21 @@ | ||
| 173 | 173 | if( transport.pFile==0 ){ |
| 174 | 174 | fossil_fatal("cannot output temporary file: %s", transport.zOutFile); |
| 175 | 175 | } |
| 176 | 176 | transport.isOpen = 1; |
| 177 | 177 | }else{ |
| 178 | - rc = socket_open(); | |
| 178 | + rc = socket_open(pUrlData); | |
| 179 | 179 | if( rc==0 ) transport.isOpen = 1; |
| 180 | 180 | } |
| 181 | 181 | } |
| 182 | 182 | return rc; |
| 183 | 183 | } |
| 184 | 184 | |
| 185 | 185 | /* |
| 186 | 186 | ** Close the current connection |
| 187 | 187 | */ |
| 188 | -void transport_close(void){ | |
| 188 | +void transport_close(UrlData *pUrlData){ | |
| 189 | 189 | if( transport.isOpen ){ |
| 190 | 190 | free(transport.pBuf); |
| 191 | 191 | transport.pBuf = 0; |
| 192 | 192 | transport.nAlloc = 0; |
| 193 | 193 | transport.nUsed = 0; |
| @@ -194,17 +194,17 @@ | ||
| 194 | 194 | transport.iCursor = 0; |
| 195 | 195 | if( transport.pLog ){ |
| 196 | 196 | fclose(transport.pLog); |
| 197 | 197 | transport.pLog = 0; |
| 198 | 198 | } |
| 199 | - if( g.urlIsSsh ){ | |
| 199 | + if( pUrlData->isSsh ){ | |
| 200 | 200 | transport_ssh_close(); |
| 201 | - }else if( g.urlIsHttps ){ | |
| 201 | + }else if( pUrlData->isHttps ){ | |
| 202 | 202 | #ifdef FOSSIL_ENABLE_SSL |
| 203 | 203 | ssl_close(); |
| 204 | 204 | #endif |
| 205 | - }else if( g.urlIsFile ){ | |
| 205 | + }else if( pUrlData->isFile ){ | |
| 206 | 206 | if( transport.pFile ){ |
| 207 | 207 | fclose(transport.pFile); |
| 208 | 208 | transport.pFile = 0; |
| 209 | 209 | } |
| 210 | 210 | file_delete(transport.zInFile); |
| @@ -219,28 +219,28 @@ | ||
| 219 | 219 | } |
| 220 | 220 | |
| 221 | 221 | /* |
| 222 | 222 | ** Send content over the wire. |
| 223 | 223 | */ |
| 224 | -void transport_send(Blob *toSend){ | |
| 224 | +void transport_send(UrlData *pUrlData, Blob *toSend){ | |
| 225 | 225 | char *z = blob_buffer(toSend); |
| 226 | 226 | int n = blob_size(toSend); |
| 227 | 227 | transport.nSent += n; |
| 228 | - if( g.urlIsSsh ){ | |
| 228 | + if( pUrlData->isSsh ){ | |
| 229 | 229 | fwrite(z, 1, n, sshOut); |
| 230 | 230 | fflush(sshOut); |
| 231 | - }else if( g.urlIsHttps ){ | |
| 231 | + }else if( pUrlData->isHttps ){ | |
| 232 | 232 | #ifdef FOSSIL_ENABLE_SSL |
| 233 | 233 | int sent; |
| 234 | 234 | while( n>0 ){ |
| 235 | 235 | sent = ssl_send(0, z, n); |
| 236 | 236 | /* printf("Sent %d of %d bytes\n", sent, n); fflush(stdout); */ |
| 237 | 237 | if( sent<=0 ) break; |
| 238 | 238 | n -= sent; |
| 239 | 239 | } |
| 240 | 240 | #endif |
| 241 | - }else if( g.urlIsFile ){ | |
| 241 | + }else if( pUrlData->isFile ){ | |
| 242 | 242 | fwrite(z, 1, n, transport.pFile); |
| 243 | 243 | }else{ |
| 244 | 244 | int sent; |
| 245 | 245 | while( n>0 ){ |
| 246 | 246 | sent = socket_send(0, z, n); |
| @@ -253,16 +253,16 @@ | ||
| 253 | 253 | |
| 254 | 254 | /* |
| 255 | 255 | ** This routine is called when the outbound message is complete and |
| 256 | 256 | ** it is time to being receiving a reply. |
| 257 | 257 | */ |
| 258 | -void transport_flip(void){ | |
| 259 | - if( g.urlIsFile ){ | |
| 258 | +void transport_flip(UrlData *pUrlData){ | |
| 259 | + if( pUrlData->isFile ){ | |
| 260 | 260 | char *zCmd; |
| 261 | 261 | fclose(transport.pFile); |
| 262 | 262 | zCmd = mprintf("\"%s\" http \"%s\" \"%s\" \"%s\" 127.0.0.1 --localauth", |
| 263 | - g.nameOfExe, g.urlName, transport.zOutFile, transport.zInFile | |
| 263 | + g.nameOfExe, pUrlData->name, transport.zOutFile, transport.zInFile | |
| 264 | 264 | ); |
| 265 | 265 | fossil_system(zCmd); |
| 266 | 266 | free(zCmd); |
| 267 | 267 | transport.pFile = fossil_fopen(transport.zInFile, "rb"); |
| 268 | 268 | } |
| @@ -282,21 +282,21 @@ | ||
| 282 | 282 | |
| 283 | 283 | /* |
| 284 | 284 | ** This routine is called when the inbound message has been received |
| 285 | 285 | ** and it is time to start sending again. |
| 286 | 286 | */ |
| 287 | -void transport_rewind(void){ | |
| 288 | - if( g.urlIsFile ){ | |
| 289 | - transport_close(); | |
| 287 | +void transport_rewind(UrlData *pUrlData){ | |
| 288 | + if( pUrlData->isFile ){ | |
| 289 | + transport_close(pUrlData); | |
| 290 | 290 | } |
| 291 | 291 | } |
| 292 | 292 | |
| 293 | 293 | /* |
| 294 | 294 | ** Read N bytes of content directly from the wire and write into |
| 295 | 295 | ** the buffer. |
| 296 | 296 | */ |
| 297 | -static int transport_fetch(char *zBuf, int N){ | |
| 297 | +static int transport_fetch(UrlData *pUrlData, char *zBuf, int N){ | |
| 298 | 298 | int got; |
| 299 | 299 | if( sshIn ){ |
| 300 | 300 | int x; |
| 301 | 301 | int wanted = N; |
| 302 | 302 | got = 0; |
| @@ -304,17 +304,17 @@ | ||
| 304 | 304 | x = read(sshIn, &zBuf[got], wanted); |
| 305 | 305 | if( x<=0 ) break; |
| 306 | 306 | got += x; |
| 307 | 307 | wanted -= x; |
| 308 | 308 | } |
| 309 | - }else if( g.urlIsHttps ){ | |
| 309 | + }else if( pUrlData->isHttps ){ | |
| 310 | 310 | #ifdef FOSSIL_ENABLE_SSL |
| 311 | 311 | got = ssl_receive(0, zBuf, N); |
| 312 | 312 | #else |
| 313 | 313 | got = 0; |
| 314 | 314 | #endif |
| 315 | - }else if( g.urlIsFile ){ | |
| 315 | + }else if( pUrlData->isFile ){ | |
| 316 | 316 | got = fread(zBuf, 1, N, transport.pFile); |
| 317 | 317 | }else{ |
| 318 | 318 | got = socket_receive(0, zBuf, N); |
| 319 | 319 | } |
| 320 | 320 | /* printf("received %d of %d bytes\n", got, N); fflush(stdout); */ |
| @@ -327,11 +327,11 @@ | ||
| 327 | 327 | |
| 328 | 328 | /* |
| 329 | 329 | ** Read N bytes of content from the wire and store in the supplied buffer. |
| 330 | 330 | ** Return the number of bytes actually received. |
| 331 | 331 | */ |
| 332 | -int transport_receive(char *zBuf, int N){ | |
| 332 | +int transport_receive(UrlData *pUrlData, char *zBuf, int N){ | |
| 333 | 333 | int onHand; /* Bytes current held in the transport buffer */ |
| 334 | 334 | int nByte = 0; /* Bytes of content received */ |
| 335 | 335 | |
| 336 | 336 | onHand = transport.nUsed - transport.iCursor; |
| 337 | 337 | if( g.fSshTrace){ |
| @@ -351,11 +351,11 @@ | ||
| 351 | 351 | N -= toMove; |
| 352 | 352 | zBuf += toMove; |
| 353 | 353 | nByte += toMove; |
| 354 | 354 | } |
| 355 | 355 | if( N>0 ){ |
| 356 | - int got = transport_fetch(zBuf, N); | |
| 356 | + int got = transport_fetch(pUrlData, zBuf, N); | |
| 357 | 357 | if( got>0 ){ |
| 358 | 358 | nByte += got; |
| 359 | 359 | transport.nRcvd += got; |
| 360 | 360 | } |
| 361 | 361 | } |
| @@ -366,11 +366,11 @@ | ||
| 366 | 366 | /* |
| 367 | 367 | ** Load up to N new bytes of content into the transport.pBuf buffer. |
| 368 | 368 | ** The buffer itself might be moved. And the transport.iCursor value |
| 369 | 369 | ** might be reset to 0. |
| 370 | 370 | */ |
| 371 | -static void transport_load_buffer(int N){ | |
| 371 | +static void transport_load_buffer(UrlData *pUrlData, int N){ | |
| 372 | 372 | int i, j; |
| 373 | 373 | if( transport.nAlloc==0 ){ |
| 374 | 374 | transport.nAlloc = N; |
| 375 | 375 | transport.pBuf = fossil_malloc( N ); |
| 376 | 376 | transport.iCursor = 0; |
| @@ -388,11 +388,11 @@ | ||
| 388 | 388 | transport.nAlloc = transport.nUsed + N; |
| 389 | 389 | pNew = fossil_realloc(transport.pBuf, transport.nAlloc); |
| 390 | 390 | transport.pBuf = pNew; |
| 391 | 391 | } |
| 392 | 392 | if( N>0 ){ |
| 393 | - i = transport_fetch(&transport.pBuf[transport.nUsed], N); | |
| 393 | + i = transport_fetch(pUrlData, &transport.pBuf[transport.nUsed], N); | |
| 394 | 394 | if( i>0 ){ |
| 395 | 395 | transport.nRcvd += i; |
| 396 | 396 | transport.nUsed += i; |
| 397 | 397 | } |
| 398 | 398 | } |
| @@ -404,18 +404,18 @@ | ||
| 404 | 404 | ** from the received line and zero-terminate the result. Return a pointer |
| 405 | 405 | ** to the line. |
| 406 | 406 | ** |
| 407 | 407 | ** Each call to this routine potentially overwrites the returned buffer. |
| 408 | 408 | */ |
| 409 | -char *transport_receive_line(void){ | |
| 409 | +char *transport_receive_line(UrlData *pUrlData){ | |
| 410 | 410 | int i; |
| 411 | 411 | int iStart; |
| 412 | 412 | |
| 413 | 413 | i = iStart = transport.iCursor; |
| 414 | 414 | while(1){ |
| 415 | 415 | if( i >= transport.nUsed ){ |
| 416 | - transport_load_buffer(g.urlIsSsh ? 2 : 1000); | |
| 416 | + transport_load_buffer(pUrlData, pUrlData->isSsh ? 2 : 1000); | |
| 417 | 417 | i -= iStart; |
| 418 | 418 | iStart = 0; |
| 419 | 419 | if( i >= transport.nUsed ){ |
| 420 | 420 | transport.pBuf[i] = 0; |
| 421 | 421 | transport.iCursor = i; |
| @@ -437,15 +437,15 @@ | ||
| 437 | 437 | } |
| 438 | 438 | |
| 439 | 439 | /* |
| 440 | 440 | ** Global transport shutdown |
| 441 | 441 | */ |
| 442 | -void transport_global_shutdown(void){ | |
| 443 | - if( g.urlIsSsh ){ | |
| 442 | +void transport_global_shutdown(UrlData *pUrlData){ | |
| 443 | + if( pUrlData->isSsh ){ | |
| 444 | 444 | transport_ssh_close(); |
| 445 | 445 | } |
| 446 | - if( g.urlIsHttps ){ | |
| 446 | + if( pUrlData->isHttps ){ | |
| 447 | 447 | #ifdef FOSSIL_ENABLE_SSL |
| 448 | 448 | ssl_global_shutdown(); |
| 449 | 449 | #endif |
| 450 | 450 | }else{ |
| 451 | 451 | socket_global_shutdown(); |
| 452 | 452 |
| --- src/http_transport.c | |
| +++ src/http_transport.c | |
| @@ -52,13 +52,13 @@ | |
| 52 | |
| 53 | |
| 54 | /* |
| 55 | ** Return the current transport error message. |
| 56 | */ |
| 57 | const char *transport_errmsg(void){ |
| 58 | #ifdef FOSSIL_ENABLE_SSL |
| 59 | if( g.urlIsHttps ){ |
| 60 | return ssl_errmsg(); |
| 61 | } |
| 62 | #endif |
| 63 | return socket_errmsg(); |
| 64 | } |
| @@ -86,47 +86,47 @@ | |
| 86 | #endif |
| 87 | |
| 88 | /* |
| 89 | ** SSH initialization of the transport layer |
| 90 | */ |
| 91 | int transport_ssh_open(void){ |
| 92 | /* For SSH we need to create and run SSH fossil http |
| 93 | ** to talk to the remote machine. |
| 94 | */ |
| 95 | const char *zSsh; /* The base SSH command */ |
| 96 | Blob zCmd; /* The SSH command */ |
| 97 | char *zHost; /* The host name to contact */ |
| 98 | int n; /* Size of prefix string */ |
| 99 | |
| 100 | socket_ssh_resolve_addr(); |
| 101 | zSsh = db_get("ssh-command", zDefaultSshCmd); |
| 102 | blob_init(&zCmd, zSsh, -1); |
| 103 | if( g.urlPort!=g.urlDfltPort && g.urlPort ){ |
| 104 | #ifdef __MINGW32__ |
| 105 | blob_appendf(&zCmd, " -P %d", g.urlPort); |
| 106 | #else |
| 107 | blob_appendf(&zCmd, " -p %d", g.urlPort); |
| 108 | #endif |
| 109 | } |
| 110 | if( g.fSshTrace ){ |
| 111 | fossil_force_newline(); |
| 112 | fossil_print("%s", blob_str(&zCmd)); /* Show the base of the SSH command */ |
| 113 | } |
| 114 | if( g.urlUser && g.urlUser[0] ){ |
| 115 | zHost = mprintf("%s@%s", g.urlUser, g.urlName); |
| 116 | }else{ |
| 117 | zHost = mprintf("%s", g.urlName); |
| 118 | } |
| 119 | n = blob_size(&zCmd); |
| 120 | blob_append(&zCmd, " ", 1); |
| 121 | shell_escape(&zCmd, zHost); |
| 122 | blob_append(&zCmd, " ", 1); |
| 123 | shell_escape(&zCmd, mprintf("%s", g.urlFossil)); |
| 124 | blob_append(&zCmd, " test-http", 10); |
| 125 | if( g.urlPath && g.urlPath[0] ){ |
| 126 | blob_append(&zCmd, " ", 1); |
| 127 | shell_escape(&zCmd, mprintf("%s", g.urlPath)); |
| 128 | } |
| 129 | if( g.fSshTrace ){ |
| 130 | fossil_print("%s\n", blob_str(&zCmd)+n); /* Show tail of SSH command */ |
| 131 | } |
| 132 | free(zHost); |
| @@ -146,25 +146,25 @@ | |
| 146 | ** g.urlPort TCP/IP port. Ex: 80 |
| 147 | ** g.urlIsHttps Use TLS for the connection |
| 148 | ** |
| 149 | ** Return the number of errors. |
| 150 | */ |
| 151 | int transport_open(void){ |
| 152 | int rc = 0; |
| 153 | if( transport.isOpen==0 ){ |
| 154 | if( g.urlIsSsh ){ |
| 155 | rc = transport_ssh_open(); |
| 156 | if( rc==0 ) transport.isOpen = 1; |
| 157 | }else if( g.urlIsHttps ){ |
| 158 | #ifdef FOSSIL_ENABLE_SSL |
| 159 | rc = ssl_open(); |
| 160 | if( rc==0 ) transport.isOpen = 1; |
| 161 | #else |
| 162 | socket_set_errmsg("HTTPS: Fossil has been compiled without SSL support"); |
| 163 | rc = 1; |
| 164 | #endif |
| 165 | }else if( g.urlIsFile ){ |
| 166 | sqlite3_uint64 iRandId; |
| 167 | sqlite3_randomness(sizeof(iRandId), &iRandId); |
| 168 | transport.zOutFile = mprintf("%s-%llu-out.http", |
| 169 | g.zRepositoryName, iRandId); |
| 170 | transport.zInFile = mprintf("%s-%llu-in.http", |
| @@ -173,21 +173,21 @@ | |
| 173 | if( transport.pFile==0 ){ |
| 174 | fossil_fatal("cannot output temporary file: %s", transport.zOutFile); |
| 175 | } |
| 176 | transport.isOpen = 1; |
| 177 | }else{ |
| 178 | rc = socket_open(); |
| 179 | if( rc==0 ) transport.isOpen = 1; |
| 180 | } |
| 181 | } |
| 182 | return rc; |
| 183 | } |
| 184 | |
| 185 | /* |
| 186 | ** Close the current connection |
| 187 | */ |
| 188 | void transport_close(void){ |
| 189 | if( transport.isOpen ){ |
| 190 | free(transport.pBuf); |
| 191 | transport.pBuf = 0; |
| 192 | transport.nAlloc = 0; |
| 193 | transport.nUsed = 0; |
| @@ -194,17 +194,17 @@ | |
| 194 | transport.iCursor = 0; |
| 195 | if( transport.pLog ){ |
| 196 | fclose(transport.pLog); |
| 197 | transport.pLog = 0; |
| 198 | } |
| 199 | if( g.urlIsSsh ){ |
| 200 | transport_ssh_close(); |
| 201 | }else if( g.urlIsHttps ){ |
| 202 | #ifdef FOSSIL_ENABLE_SSL |
| 203 | ssl_close(); |
| 204 | #endif |
| 205 | }else if( g.urlIsFile ){ |
| 206 | if( transport.pFile ){ |
| 207 | fclose(transport.pFile); |
| 208 | transport.pFile = 0; |
| 209 | } |
| 210 | file_delete(transport.zInFile); |
| @@ -219,28 +219,28 @@ | |
| 219 | } |
| 220 | |
| 221 | /* |
| 222 | ** Send content over the wire. |
| 223 | */ |
| 224 | void transport_send(Blob *toSend){ |
| 225 | char *z = blob_buffer(toSend); |
| 226 | int n = blob_size(toSend); |
| 227 | transport.nSent += n; |
| 228 | if( g.urlIsSsh ){ |
| 229 | fwrite(z, 1, n, sshOut); |
| 230 | fflush(sshOut); |
| 231 | }else if( g.urlIsHttps ){ |
| 232 | #ifdef FOSSIL_ENABLE_SSL |
| 233 | int sent; |
| 234 | while( n>0 ){ |
| 235 | sent = ssl_send(0, z, n); |
| 236 | /* printf("Sent %d of %d bytes\n", sent, n); fflush(stdout); */ |
| 237 | if( sent<=0 ) break; |
| 238 | n -= sent; |
| 239 | } |
| 240 | #endif |
| 241 | }else if( g.urlIsFile ){ |
| 242 | fwrite(z, 1, n, transport.pFile); |
| 243 | }else{ |
| 244 | int sent; |
| 245 | while( n>0 ){ |
| 246 | sent = socket_send(0, z, n); |
| @@ -253,16 +253,16 @@ | |
| 253 | |
| 254 | /* |
| 255 | ** This routine is called when the outbound message is complete and |
| 256 | ** it is time to being receiving a reply. |
| 257 | */ |
| 258 | void transport_flip(void){ |
| 259 | if( g.urlIsFile ){ |
| 260 | char *zCmd; |
| 261 | fclose(transport.pFile); |
| 262 | zCmd = mprintf("\"%s\" http \"%s\" \"%s\" \"%s\" 127.0.0.1 --localauth", |
| 263 | g.nameOfExe, g.urlName, transport.zOutFile, transport.zInFile |
| 264 | ); |
| 265 | fossil_system(zCmd); |
| 266 | free(zCmd); |
| 267 | transport.pFile = fossil_fopen(transport.zInFile, "rb"); |
| 268 | } |
| @@ -282,21 +282,21 @@ | |
| 282 | |
| 283 | /* |
| 284 | ** This routine is called when the inbound message has been received |
| 285 | ** and it is time to start sending again. |
| 286 | */ |
| 287 | void transport_rewind(void){ |
| 288 | if( g.urlIsFile ){ |
| 289 | transport_close(); |
| 290 | } |
| 291 | } |
| 292 | |
| 293 | /* |
| 294 | ** Read N bytes of content directly from the wire and write into |
| 295 | ** the buffer. |
| 296 | */ |
| 297 | static int transport_fetch(char *zBuf, int N){ |
| 298 | int got; |
| 299 | if( sshIn ){ |
| 300 | int x; |
| 301 | int wanted = N; |
| 302 | got = 0; |
| @@ -304,17 +304,17 @@ | |
| 304 | x = read(sshIn, &zBuf[got], wanted); |
| 305 | if( x<=0 ) break; |
| 306 | got += x; |
| 307 | wanted -= x; |
| 308 | } |
| 309 | }else if( g.urlIsHttps ){ |
| 310 | #ifdef FOSSIL_ENABLE_SSL |
| 311 | got = ssl_receive(0, zBuf, N); |
| 312 | #else |
| 313 | got = 0; |
| 314 | #endif |
| 315 | }else if( g.urlIsFile ){ |
| 316 | got = fread(zBuf, 1, N, transport.pFile); |
| 317 | }else{ |
| 318 | got = socket_receive(0, zBuf, N); |
| 319 | } |
| 320 | /* printf("received %d of %d bytes\n", got, N); fflush(stdout); */ |
| @@ -327,11 +327,11 @@ | |
| 327 | |
| 328 | /* |
| 329 | ** Read N bytes of content from the wire and store in the supplied buffer. |
| 330 | ** Return the number of bytes actually received. |
| 331 | */ |
| 332 | int transport_receive(char *zBuf, int N){ |
| 333 | int onHand; /* Bytes current held in the transport buffer */ |
| 334 | int nByte = 0; /* Bytes of content received */ |
| 335 | |
| 336 | onHand = transport.nUsed - transport.iCursor; |
| 337 | if( g.fSshTrace){ |
| @@ -351,11 +351,11 @@ | |
| 351 | N -= toMove; |
| 352 | zBuf += toMove; |
| 353 | nByte += toMove; |
| 354 | } |
| 355 | if( N>0 ){ |
| 356 | int got = transport_fetch(zBuf, N); |
| 357 | if( got>0 ){ |
| 358 | nByte += got; |
| 359 | transport.nRcvd += got; |
| 360 | } |
| 361 | } |
| @@ -366,11 +366,11 @@ | |
| 366 | /* |
| 367 | ** Load up to N new bytes of content into the transport.pBuf buffer. |
| 368 | ** The buffer itself might be moved. And the transport.iCursor value |
| 369 | ** might be reset to 0. |
| 370 | */ |
| 371 | static void transport_load_buffer(int N){ |
| 372 | int i, j; |
| 373 | if( transport.nAlloc==0 ){ |
| 374 | transport.nAlloc = N; |
| 375 | transport.pBuf = fossil_malloc( N ); |
| 376 | transport.iCursor = 0; |
| @@ -388,11 +388,11 @@ | |
| 388 | transport.nAlloc = transport.nUsed + N; |
| 389 | pNew = fossil_realloc(transport.pBuf, transport.nAlloc); |
| 390 | transport.pBuf = pNew; |
| 391 | } |
| 392 | if( N>0 ){ |
| 393 | i = transport_fetch(&transport.pBuf[transport.nUsed], N); |
| 394 | if( i>0 ){ |
| 395 | transport.nRcvd += i; |
| 396 | transport.nUsed += i; |
| 397 | } |
| 398 | } |
| @@ -404,18 +404,18 @@ | |
| 404 | ** from the received line and zero-terminate the result. Return a pointer |
| 405 | ** to the line. |
| 406 | ** |
| 407 | ** Each call to this routine potentially overwrites the returned buffer. |
| 408 | */ |
| 409 | char *transport_receive_line(void){ |
| 410 | int i; |
| 411 | int iStart; |
| 412 | |
| 413 | i = iStart = transport.iCursor; |
| 414 | while(1){ |
| 415 | if( i >= transport.nUsed ){ |
| 416 | transport_load_buffer(g.urlIsSsh ? 2 : 1000); |
| 417 | i -= iStart; |
| 418 | iStart = 0; |
| 419 | if( i >= transport.nUsed ){ |
| 420 | transport.pBuf[i] = 0; |
| 421 | transport.iCursor = i; |
| @@ -437,15 +437,15 @@ | |
| 437 | } |
| 438 | |
| 439 | /* |
| 440 | ** Global transport shutdown |
| 441 | */ |
| 442 | void transport_global_shutdown(void){ |
| 443 | if( g.urlIsSsh ){ |
| 444 | transport_ssh_close(); |
| 445 | } |
| 446 | if( g.urlIsHttps ){ |
| 447 | #ifdef FOSSIL_ENABLE_SSL |
| 448 | ssl_global_shutdown(); |
| 449 | #endif |
| 450 | }else{ |
| 451 | socket_global_shutdown(); |
| 452 |
| --- src/http_transport.c | |
| +++ src/http_transport.c | |
| @@ -52,13 +52,13 @@ | |
| 52 | |
| 53 | |
| 54 | /* |
| 55 | ** Return the current transport error message. |
| 56 | */ |
| 57 | const char *transport_errmsg(UrlData *pUrlData){ |
| 58 | #ifdef FOSSIL_ENABLE_SSL |
| 59 | if( pUrlData->isHttps ){ |
| 60 | return ssl_errmsg(); |
| 61 | } |
| 62 | #endif |
| 63 | return socket_errmsg(); |
| 64 | } |
| @@ -86,47 +86,47 @@ | |
| 86 | #endif |
| 87 | |
| 88 | /* |
| 89 | ** SSH initialization of the transport layer |
| 90 | */ |
| 91 | int transport_ssh_open(UrlData *pUrlData){ |
| 92 | /* For SSH we need to create and run SSH fossil http |
| 93 | ** to talk to the remote machine. |
| 94 | */ |
| 95 | const char *zSsh; /* The base SSH command */ |
| 96 | Blob zCmd; /* The SSH command */ |
| 97 | char *zHost; /* The host name to contact */ |
| 98 | int n; /* Size of prefix string */ |
| 99 | |
| 100 | socket_ssh_resolve_addr(pUrlData); |
| 101 | zSsh = db_get("ssh-command", zDefaultSshCmd); |
| 102 | blob_init(&zCmd, zSsh, -1); |
| 103 | if( pUrlData->port!=pUrlData->dfltPort && pUrlData->port ){ |
| 104 | #ifdef __MINGW32__ |
| 105 | blob_appendf(&zCmd, " -P %d", pUrlData->port); |
| 106 | #else |
| 107 | blob_appendf(&zCmd, " -p %d", pUrlData->port); |
| 108 | #endif |
| 109 | } |
| 110 | if( g.fSshTrace ){ |
| 111 | fossil_force_newline(); |
| 112 | fossil_print("%s", blob_str(&zCmd)); /* Show the base of the SSH command */ |
| 113 | } |
| 114 | if( pUrlData->user && pUrlData->user[0] ){ |
| 115 | zHost = mprintf("%s@%s", pUrlData->user, pUrlData->name); |
| 116 | }else{ |
| 117 | zHost = mprintf("%s", pUrlData->name); |
| 118 | } |
| 119 | n = blob_size(&zCmd); |
| 120 | blob_append(&zCmd, " ", 1); |
| 121 | shell_escape(&zCmd, zHost); |
| 122 | blob_append(&zCmd, " ", 1); |
| 123 | shell_escape(&zCmd, mprintf("%s", pUrlData->fossil)); |
| 124 | blob_append(&zCmd, " test-http", 10); |
| 125 | if( pUrlData->path && pUrlData->path[0] ){ |
| 126 | blob_append(&zCmd, " ", 1); |
| 127 | shell_escape(&zCmd, mprintf("%s", pUrlData->path)); |
| 128 | } |
| 129 | if( g.fSshTrace ){ |
| 130 | fossil_print("%s\n", blob_str(&zCmd)+n); /* Show tail of SSH command */ |
| 131 | } |
| 132 | free(zHost); |
| @@ -146,25 +146,25 @@ | |
| 146 | ** g.urlPort TCP/IP port. Ex: 80 |
| 147 | ** g.urlIsHttps Use TLS for the connection |
| 148 | ** |
| 149 | ** Return the number of errors. |
| 150 | */ |
| 151 | int transport_open(UrlData *pUrlData){ |
| 152 | int rc = 0; |
| 153 | if( transport.isOpen==0 ){ |
| 154 | if( pUrlData->isSsh ){ |
| 155 | rc = transport_ssh_open(pUrlData); |
| 156 | if( rc==0 ) transport.isOpen = 1; |
| 157 | }else if( pUrlData->isHttps ){ |
| 158 | #ifdef FOSSIL_ENABLE_SSL |
| 159 | rc = ssl_open(pUrlData); |
| 160 | if( rc==0 ) transport.isOpen = 1; |
| 161 | #else |
| 162 | socket_set_errmsg("HTTPS: Fossil has been compiled without SSL support"); |
| 163 | rc = 1; |
| 164 | #endif |
| 165 | }else if( pUrlData->isFile ){ |
| 166 | sqlite3_uint64 iRandId; |
| 167 | sqlite3_randomness(sizeof(iRandId), &iRandId); |
| 168 | transport.zOutFile = mprintf("%s-%llu-out.http", |
| 169 | g.zRepositoryName, iRandId); |
| 170 | transport.zInFile = mprintf("%s-%llu-in.http", |
| @@ -173,21 +173,21 @@ | |
| 173 | if( transport.pFile==0 ){ |
| 174 | fossil_fatal("cannot output temporary file: %s", transport.zOutFile); |
| 175 | } |
| 176 | transport.isOpen = 1; |
| 177 | }else{ |
| 178 | rc = socket_open(pUrlData); |
| 179 | if( rc==0 ) transport.isOpen = 1; |
| 180 | } |
| 181 | } |
| 182 | return rc; |
| 183 | } |
| 184 | |
| 185 | /* |
| 186 | ** Close the current connection |
| 187 | */ |
| 188 | void transport_close(UrlData *pUrlData){ |
| 189 | if( transport.isOpen ){ |
| 190 | free(transport.pBuf); |
| 191 | transport.pBuf = 0; |
| 192 | transport.nAlloc = 0; |
| 193 | transport.nUsed = 0; |
| @@ -194,17 +194,17 @@ | |
| 194 | transport.iCursor = 0; |
| 195 | if( transport.pLog ){ |
| 196 | fclose(transport.pLog); |
| 197 | transport.pLog = 0; |
| 198 | } |
| 199 | if( pUrlData->isSsh ){ |
| 200 | transport_ssh_close(); |
| 201 | }else if( pUrlData->isHttps ){ |
| 202 | #ifdef FOSSIL_ENABLE_SSL |
| 203 | ssl_close(); |
| 204 | #endif |
| 205 | }else if( pUrlData->isFile ){ |
| 206 | if( transport.pFile ){ |
| 207 | fclose(transport.pFile); |
| 208 | transport.pFile = 0; |
| 209 | } |
| 210 | file_delete(transport.zInFile); |
| @@ -219,28 +219,28 @@ | |
| 219 | } |
| 220 | |
| 221 | /* |
| 222 | ** Send content over the wire. |
| 223 | */ |
| 224 | void transport_send(UrlData *pUrlData, Blob *toSend){ |
| 225 | char *z = blob_buffer(toSend); |
| 226 | int n = blob_size(toSend); |
| 227 | transport.nSent += n; |
| 228 | if( pUrlData->isSsh ){ |
| 229 | fwrite(z, 1, n, sshOut); |
| 230 | fflush(sshOut); |
| 231 | }else if( pUrlData->isHttps ){ |
| 232 | #ifdef FOSSIL_ENABLE_SSL |
| 233 | int sent; |
| 234 | while( n>0 ){ |
| 235 | sent = ssl_send(0, z, n); |
| 236 | /* printf("Sent %d of %d bytes\n", sent, n); fflush(stdout); */ |
| 237 | if( sent<=0 ) break; |
| 238 | n -= sent; |
| 239 | } |
| 240 | #endif |
| 241 | }else if( pUrlData->isFile ){ |
| 242 | fwrite(z, 1, n, transport.pFile); |
| 243 | }else{ |
| 244 | int sent; |
| 245 | while( n>0 ){ |
| 246 | sent = socket_send(0, z, n); |
| @@ -253,16 +253,16 @@ | |
| 253 | |
| 254 | /* |
| 255 | ** This routine is called when the outbound message is complete and |
| 256 | ** it is time to being receiving a reply. |
| 257 | */ |
| 258 | void transport_flip(UrlData *pUrlData){ |
| 259 | if( pUrlData->isFile ){ |
| 260 | char *zCmd; |
| 261 | fclose(transport.pFile); |
| 262 | zCmd = mprintf("\"%s\" http \"%s\" \"%s\" \"%s\" 127.0.0.1 --localauth", |
| 263 | g.nameOfExe, pUrlData->name, transport.zOutFile, transport.zInFile |
| 264 | ); |
| 265 | fossil_system(zCmd); |
| 266 | free(zCmd); |
| 267 | transport.pFile = fossil_fopen(transport.zInFile, "rb"); |
| 268 | } |
| @@ -282,21 +282,21 @@ | |
| 282 | |
| 283 | /* |
| 284 | ** This routine is called when the inbound message has been received |
| 285 | ** and it is time to start sending again. |
| 286 | */ |
| 287 | void transport_rewind(UrlData *pUrlData){ |
| 288 | if( pUrlData->isFile ){ |
| 289 | transport_close(pUrlData); |
| 290 | } |
| 291 | } |
| 292 | |
| 293 | /* |
| 294 | ** Read N bytes of content directly from the wire and write into |
| 295 | ** the buffer. |
| 296 | */ |
| 297 | static int transport_fetch(UrlData *pUrlData, char *zBuf, int N){ |
| 298 | int got; |
| 299 | if( sshIn ){ |
| 300 | int x; |
| 301 | int wanted = N; |
| 302 | got = 0; |
| @@ -304,17 +304,17 @@ | |
| 304 | x = read(sshIn, &zBuf[got], wanted); |
| 305 | if( x<=0 ) break; |
| 306 | got += x; |
| 307 | wanted -= x; |
| 308 | } |
| 309 | }else if( pUrlData->isHttps ){ |
| 310 | #ifdef FOSSIL_ENABLE_SSL |
| 311 | got = ssl_receive(0, zBuf, N); |
| 312 | #else |
| 313 | got = 0; |
| 314 | #endif |
| 315 | }else if( pUrlData->isFile ){ |
| 316 | got = fread(zBuf, 1, N, transport.pFile); |
| 317 | }else{ |
| 318 | got = socket_receive(0, zBuf, N); |
| 319 | } |
| 320 | /* printf("received %d of %d bytes\n", got, N); fflush(stdout); */ |
| @@ -327,11 +327,11 @@ | |
| 327 | |
| 328 | /* |
| 329 | ** Read N bytes of content from the wire and store in the supplied buffer. |
| 330 | ** Return the number of bytes actually received. |
| 331 | */ |
| 332 | int transport_receive(UrlData *pUrlData, char *zBuf, int N){ |
| 333 | int onHand; /* Bytes current held in the transport buffer */ |
| 334 | int nByte = 0; /* Bytes of content received */ |
| 335 | |
| 336 | onHand = transport.nUsed - transport.iCursor; |
| 337 | if( g.fSshTrace){ |
| @@ -351,11 +351,11 @@ | |
| 351 | N -= toMove; |
| 352 | zBuf += toMove; |
| 353 | nByte += toMove; |
| 354 | } |
| 355 | if( N>0 ){ |
| 356 | int got = transport_fetch(pUrlData, zBuf, N); |
| 357 | if( got>0 ){ |
| 358 | nByte += got; |
| 359 | transport.nRcvd += got; |
| 360 | } |
| 361 | } |
| @@ -366,11 +366,11 @@ | |
| 366 | /* |
| 367 | ** Load up to N new bytes of content into the transport.pBuf buffer. |
| 368 | ** The buffer itself might be moved. And the transport.iCursor value |
| 369 | ** might be reset to 0. |
| 370 | */ |
| 371 | static void transport_load_buffer(UrlData *pUrlData, int N){ |
| 372 | int i, j; |
| 373 | if( transport.nAlloc==0 ){ |
| 374 | transport.nAlloc = N; |
| 375 | transport.pBuf = fossil_malloc( N ); |
| 376 | transport.iCursor = 0; |
| @@ -388,11 +388,11 @@ | |
| 388 | transport.nAlloc = transport.nUsed + N; |
| 389 | pNew = fossil_realloc(transport.pBuf, transport.nAlloc); |
| 390 | transport.pBuf = pNew; |
| 391 | } |
| 392 | if( N>0 ){ |
| 393 | i = transport_fetch(pUrlData, &transport.pBuf[transport.nUsed], N); |
| 394 | if( i>0 ){ |
| 395 | transport.nRcvd += i; |
| 396 | transport.nUsed += i; |
| 397 | } |
| 398 | } |
| @@ -404,18 +404,18 @@ | |
| 404 | ** from the received line and zero-terminate the result. Return a pointer |
| 405 | ** to the line. |
| 406 | ** |
| 407 | ** Each call to this routine potentially overwrites the returned buffer. |
| 408 | */ |
| 409 | char *transport_receive_line(UrlData *pUrlData){ |
| 410 | int i; |
| 411 | int iStart; |
| 412 | |
| 413 | i = iStart = transport.iCursor; |
| 414 | while(1){ |
| 415 | if( i >= transport.nUsed ){ |
| 416 | transport_load_buffer(pUrlData, pUrlData->isSsh ? 2 : 1000); |
| 417 | i -= iStart; |
| 418 | iStart = 0; |
| 419 | if( i >= transport.nUsed ){ |
| 420 | transport.pBuf[i] = 0; |
| 421 | transport.iCursor = i; |
| @@ -437,15 +437,15 @@ | |
| 437 | } |
| 438 | |
| 439 | /* |
| 440 | ** Global transport shutdown |
| 441 | */ |
| 442 | void transport_global_shutdown(UrlData *pUrlData){ |
| 443 | if( pUrlData->isSsh ){ |
| 444 | transport_ssh_close(); |
| 445 | } |
| 446 | if( pUrlData->isHttps ){ |
| 447 | #ifdef FOSSIL_ENABLE_SSL |
| 448 | ssl_global_shutdown(); |
| 449 | #endif |
| 450 | }else{ |
| 451 | socket_global_shutdown(); |
| 452 |
+1
-1
| --- src/info.c | ||
| +++ src/info.c | ||
| @@ -2195,11 +2195,11 @@ | ||
| 2195 | 2195 | md5sum_blob(&ctrl, &cksum); |
| 2196 | 2196 | blob_appendf(&ctrl, "Z %b\n", &cksum); |
| 2197 | 2197 | db_begin_transaction(); |
| 2198 | 2198 | g.markPrivate = content_is_private(rid); |
| 2199 | 2199 | nrid = content_put(&ctrl); |
| 2200 | - manifest_crosslink(nrid, &ctrl); | |
| 2200 | + manifest_crosslink(nrid, &ctrl, MC_PERMIT_HOOKS); | |
| 2201 | 2201 | assert( blob_is_reset(&ctrl) ); |
| 2202 | 2202 | db_end_transaction(0); |
| 2203 | 2203 | } |
| 2204 | 2204 | cgi_redirectf("ci?name=%s", zUuid); |
| 2205 | 2205 | } |
| 2206 | 2206 |
| --- src/info.c | |
| +++ src/info.c | |
| @@ -2195,11 +2195,11 @@ | |
| 2195 | md5sum_blob(&ctrl, &cksum); |
| 2196 | blob_appendf(&ctrl, "Z %b\n", &cksum); |
| 2197 | db_begin_transaction(); |
| 2198 | g.markPrivate = content_is_private(rid); |
| 2199 | nrid = content_put(&ctrl); |
| 2200 | manifest_crosslink(nrid, &ctrl); |
| 2201 | assert( blob_is_reset(&ctrl) ); |
| 2202 | db_end_transaction(0); |
| 2203 | } |
| 2204 | cgi_redirectf("ci?name=%s", zUuid); |
| 2205 | } |
| 2206 |
| --- src/info.c | |
| +++ src/info.c | |
| @@ -2195,11 +2195,11 @@ | |
| 2195 | md5sum_blob(&ctrl, &cksum); |
| 2196 | blob_appendf(&ctrl, "Z %b\n", &cksum); |
| 2197 | db_begin_transaction(); |
| 2198 | g.markPrivate = content_is_private(rid); |
| 2199 | nrid = content_put(&ctrl); |
| 2200 | manifest_crosslink(nrid, &ctrl, MC_PERMIT_HOOKS); |
| 2201 | assert( blob_is_reset(&ctrl) ); |
| 2202 | db_end_transaction(0); |
| 2203 | } |
| 2204 | cgi_redirectf("ci?name=%s", zUuid); |
| 2205 | } |
| 2206 |
+1
-1
| --- src/info.c | ||
| +++ src/info.c | ||
| @@ -2195,11 +2195,11 @@ | ||
| 2195 | 2195 | md5sum_blob(&ctrl, &cksum); |
| 2196 | 2196 | blob_appendf(&ctrl, "Z %b\n", &cksum); |
| 2197 | 2197 | db_begin_transaction(); |
| 2198 | 2198 | g.markPrivate = content_is_private(rid); |
| 2199 | 2199 | nrid = content_put(&ctrl); |
| 2200 | - manifest_crosslink(nrid, &ctrl); | |
| 2200 | + manifest_crosslink(nrid, &ctrl, MC_PERMIT_HOOKS); | |
| 2201 | 2201 | assert( blob_is_reset(&ctrl) ); |
| 2202 | 2202 | db_end_transaction(0); |
| 2203 | 2203 | } |
| 2204 | 2204 | cgi_redirectf("ci?name=%s", zUuid); |
| 2205 | 2205 | } |
| 2206 | 2206 |
| --- src/info.c | |
| +++ src/info.c | |
| @@ -2195,11 +2195,11 @@ | |
| 2195 | md5sum_blob(&ctrl, &cksum); |
| 2196 | blob_appendf(&ctrl, "Z %b\n", &cksum); |
| 2197 | db_begin_transaction(); |
| 2198 | g.markPrivate = content_is_private(rid); |
| 2199 | nrid = content_put(&ctrl); |
| 2200 | manifest_crosslink(nrid, &ctrl); |
| 2201 | assert( blob_is_reset(&ctrl) ); |
| 2202 | db_end_transaction(0); |
| 2203 | } |
| 2204 | cgi_redirectf("ci?name=%s", zUuid); |
| 2205 | } |
| 2206 |
| --- src/info.c | |
| +++ src/info.c | |
| @@ -2195,11 +2195,11 @@ | |
| 2195 | md5sum_blob(&ctrl, &cksum); |
| 2196 | blob_appendf(&ctrl, "Z %b\n", &cksum); |
| 2197 | db_begin_transaction(); |
| 2198 | g.markPrivate = content_is_private(rid); |
| 2199 | nrid = content_put(&ctrl); |
| 2200 | manifest_crosslink(nrid, &ctrl, MC_PERMIT_HOOKS); |
| 2201 | assert( blob_is_reset(&ctrl) ); |
| 2202 | db_end_transaction(0); |
| 2203 | } |
| 2204 | cgi_redirectf("ci?name=%s", zUuid); |
| 2205 | } |
| 2206 |
+2
-2
| --- src/json_branch.c | ||
| +++ src/json_branch.c | ||
| @@ -291,12 +291,12 @@ | ||
| 291 | 291 | brid = content_put(&branch); |
| 292 | 292 | if( brid==0 ){ |
| 293 | 293 | fossil_fatal("Problem committing manifest: %s", g.zErrMsg); |
| 294 | 294 | } |
| 295 | 295 | db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", brid); |
| 296 | - if( manifest_crosslink(brid, &branch)==0 ){ | |
| 297 | - fossil_fatal("unable to install new manifest"); | |
| 296 | + if( manifest_crosslink(brid, &branch, MC_PERMIT_HOOKS)==0 ){ | |
| 297 | + fossil_fatal("%s\n", g.zErrMsg); | |
| 298 | 298 | } |
| 299 | 299 | assert( blob_is_reset(&branch) ); |
| 300 | 300 | content_deltify(rootid, brid, 0); |
| 301 | 301 | if( zNewRid ){ |
| 302 | 302 | *zNewRid = brid; |
| 303 | 303 |
| --- src/json_branch.c | |
| +++ src/json_branch.c | |
| @@ -291,12 +291,12 @@ | |
| 291 | brid = content_put(&branch); |
| 292 | if( brid==0 ){ |
| 293 | fossil_fatal("Problem committing manifest: %s", g.zErrMsg); |
| 294 | } |
| 295 | db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", brid); |
| 296 | if( manifest_crosslink(brid, &branch)==0 ){ |
| 297 | fossil_fatal("unable to install new manifest"); |
| 298 | } |
| 299 | assert( blob_is_reset(&branch) ); |
| 300 | content_deltify(rootid, brid, 0); |
| 301 | if( zNewRid ){ |
| 302 | *zNewRid = brid; |
| 303 |
| --- src/json_branch.c | |
| +++ src/json_branch.c | |
| @@ -291,12 +291,12 @@ | |
| 291 | brid = content_put(&branch); |
| 292 | if( brid==0 ){ |
| 293 | fossil_fatal("Problem committing manifest: %s", g.zErrMsg); |
| 294 | } |
| 295 | db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", brid); |
| 296 | if( manifest_crosslink(brid, &branch, MC_PERMIT_HOOKS)==0 ){ |
| 297 | fossil_fatal("%s\n", g.zErrMsg); |
| 298 | } |
| 299 | assert( blob_is_reset(&branch) ); |
| 300 | content_deltify(rootid, brid, 0); |
| 301 | if( zNewRid ){ |
| 302 | *zNewRid = brid; |
| 303 |
+16
| --- src/main.c | ||
| +++ src/main.c | ||
| @@ -114,10 +114,12 @@ | ||
| 114 | 114 | #endif |
| 115 | 115 | |
| 116 | 116 | /* |
| 117 | 117 | ** All global variables are in this structure. |
| 118 | 118 | */ |
| 119 | +#define GLOBAL_URL() ((UrlData *)(&g.urlIsFile)) | |
| 120 | + | |
| 119 | 121 | struct Global { |
| 120 | 122 | int argc; char **argv; /* Command-line arguments to the program */ |
| 121 | 123 | char *nameOfExe; /* Full path of executable. */ |
| 122 | 124 | const char *zErrlog; /* Log errors to this file, if not NULL */ |
| 123 | 125 | int isConst; /* True if the output is unchanging */ |
| @@ -168,10 +170,14 @@ | ||
| 168 | 170 | int wikiFlags; /* Wiki conversion flags applied to %w and %W */ |
| 169 | 171 | char isHTTP; /* True if server/CGI modes, else assume CLI. */ |
| 170 | 172 | char javascriptHyperlink; /* If true, set href= using script, not HTML */ |
| 171 | 173 | Blob httpHeader; /* Complete text of the HTTP request header */ |
| 172 | 174 | |
| 175 | + /* | |
| 176 | + ** NOTE: These members MUST be kept in sync with those in the "UrlData" | |
| 177 | + ** structure defined in "url.c". | |
| 178 | + */ | |
| 173 | 179 | int urlIsFile; /* True if a "file:" url */ |
| 174 | 180 | int urlIsHttps; /* True if a "https:" url */ |
| 175 | 181 | int urlIsSsh; /* True if an "ssh:" url */ |
| 176 | 182 | char *urlName; /* Hostname for http: or filename for file: */ |
| 177 | 183 | char *urlHostname; /* The HOST: parameter on http headers */ |
| @@ -831,10 +837,20 @@ | ||
| 831 | 837 | const char *get_version(){ |
| 832 | 838 | static const char version[] = RELEASE_VERSION " " MANIFEST_VERSION " " |
| 833 | 839 | MANIFEST_DATE " UTC"; |
| 834 | 840 | return version; |
| 835 | 841 | } |
| 842 | + | |
| 843 | +/* | |
| 844 | +** This function returns the user-agent string for Fossil, for | |
| 845 | +** use in HTTP(S) requests. | |
| 846 | +*/ | |
| 847 | +const char *get_user_agent(){ | |
| 848 | + static const char version[] = "Fossil/" RELEASE_VERSION " (" MANIFEST_DATE | |
| 849 | + " " MANIFEST_VERSION ")"; | |
| 850 | + return version; | |
| 851 | +} | |
| 836 | 852 | |
| 837 | 853 | /* |
| 838 | 854 | ** COMMAND: version |
| 839 | 855 | ** |
| 840 | 856 | ** Usage: %fossil version ?-verbose|-v? |
| 841 | 857 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -114,10 +114,12 @@ | |
| 114 | #endif |
| 115 | |
| 116 | /* |
| 117 | ** All global variables are in this structure. |
| 118 | */ |
| 119 | struct Global { |
| 120 | int argc; char **argv; /* Command-line arguments to the program */ |
| 121 | char *nameOfExe; /* Full path of executable. */ |
| 122 | const char *zErrlog; /* Log errors to this file, if not NULL */ |
| 123 | int isConst; /* True if the output is unchanging */ |
| @@ -168,10 +170,14 @@ | |
| 168 | int wikiFlags; /* Wiki conversion flags applied to %w and %W */ |
| 169 | char isHTTP; /* True if server/CGI modes, else assume CLI. */ |
| 170 | char javascriptHyperlink; /* If true, set href= using script, not HTML */ |
| 171 | Blob httpHeader; /* Complete text of the HTTP request header */ |
| 172 | |
| 173 | int urlIsFile; /* True if a "file:" url */ |
| 174 | int urlIsHttps; /* True if a "https:" url */ |
| 175 | int urlIsSsh; /* True if an "ssh:" url */ |
| 176 | char *urlName; /* Hostname for http: or filename for file: */ |
| 177 | char *urlHostname; /* The HOST: parameter on http headers */ |
| @@ -831,10 +837,20 @@ | |
| 831 | const char *get_version(){ |
| 832 | static const char version[] = RELEASE_VERSION " " MANIFEST_VERSION " " |
| 833 | MANIFEST_DATE " UTC"; |
| 834 | return version; |
| 835 | } |
| 836 | |
| 837 | /* |
| 838 | ** COMMAND: version |
| 839 | ** |
| 840 | ** Usage: %fossil version ?-verbose|-v? |
| 841 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -114,10 +114,12 @@ | |
| 114 | #endif |
| 115 | |
| 116 | /* |
| 117 | ** All global variables are in this structure. |
| 118 | */ |
| 119 | #define GLOBAL_URL() ((UrlData *)(&g.urlIsFile)) |
| 120 | |
| 121 | struct Global { |
| 122 | int argc; char **argv; /* Command-line arguments to the program */ |
| 123 | char *nameOfExe; /* Full path of executable. */ |
| 124 | const char *zErrlog; /* Log errors to this file, if not NULL */ |
| 125 | int isConst; /* True if the output is unchanging */ |
| @@ -168,10 +170,14 @@ | |
| 170 | int wikiFlags; /* Wiki conversion flags applied to %w and %W */ |
| 171 | char isHTTP; /* True if server/CGI modes, else assume CLI. */ |
| 172 | char javascriptHyperlink; /* If true, set href= using script, not HTML */ |
| 173 | Blob httpHeader; /* Complete text of the HTTP request header */ |
| 174 | |
| 175 | /* |
| 176 | ** NOTE: These members MUST be kept in sync with those in the "UrlData" |
| 177 | ** structure defined in "url.c". |
| 178 | */ |
| 179 | int urlIsFile; /* True if a "file:" url */ |
| 180 | int urlIsHttps; /* True if a "https:" url */ |
| 181 | int urlIsSsh; /* True if an "ssh:" url */ |
| 182 | char *urlName; /* Hostname for http: or filename for file: */ |
| 183 | char *urlHostname; /* The HOST: parameter on http headers */ |
| @@ -831,10 +837,20 @@ | |
| 837 | const char *get_version(){ |
| 838 | static const char version[] = RELEASE_VERSION " " MANIFEST_VERSION " " |
| 839 | MANIFEST_DATE " UTC"; |
| 840 | return version; |
| 841 | } |
| 842 | |
| 843 | /* |
| 844 | ** This function returns the user-agent string for Fossil, for |
| 845 | ** use in HTTP(S) requests. |
| 846 | */ |
| 847 | const char *get_user_agent(){ |
| 848 | static const char version[] = "Fossil/" RELEASE_VERSION " (" MANIFEST_DATE |
| 849 | " " MANIFEST_VERSION ")"; |
| 850 | return version; |
| 851 | } |
| 852 | |
| 853 | /* |
| 854 | ** COMMAND: version |
| 855 | ** |
| 856 | ** Usage: %fossil version ?-verbose|-v? |
| 857 |
+38
-11
| --- src/manifest.c | ||
| +++ src/manifest.c | ||
| @@ -42,10 +42,16 @@ | ||
| 42 | 42 | */ |
| 43 | 43 | #define PERM_REG 0 /* regular file */ |
| 44 | 44 | #define PERM_EXE 1 /* executable */ |
| 45 | 45 | #define PERM_LNK 2 /* symlink */ |
| 46 | 46 | |
| 47 | +/* | |
| 48 | +** Flags for use with manifest_crosslink(). | |
| 49 | +*/ | |
| 50 | +#define MC_NONE 0 /* default handling */ | |
| 51 | +#define MC_PERMIT_HOOKS 1 /* permit hooks to execute */ | |
| 52 | + | |
| 47 | 53 | /* |
| 48 | 54 | ** A single F-card within a manifest |
| 49 | 55 | */ |
| 50 | 56 | struct ManifestFile { |
| 51 | 57 | char *zName; /* Name of a file */ |
| @@ -1650,34 +1656,41 @@ | ||
| 1650 | 1656 | ** Historical note: This routine original processed manifests only. |
| 1651 | 1657 | ** Processing for other control artifacts was added later. The name |
| 1652 | 1658 | ** of the routine, "manifest_crosslink", and the name of this source |
| 1653 | 1659 | ** file, is a legacy of its original use. |
| 1654 | 1660 | */ |
| 1655 | -int manifest_crosslink(int rid, Blob *pContent){ | |
| 1656 | - int i; | |
| 1661 | +int manifest_crosslink(int rid, Blob *pContent, int flags){ | |
| 1662 | + int i, result = TH_OK; | |
| 1657 | 1663 | Manifest *p; |
| 1658 | 1664 | Stmt q; |
| 1659 | 1665 | int parentid = 0; |
| 1666 | + const char *zScript = 0; | |
| 1667 | + const char *zUuid = 0; | |
| 1660 | 1668 | |
| 1661 | 1669 | if( (p = manifest_cache_find(rid))!=0 ){ |
| 1662 | 1670 | blob_reset(pContent); |
| 1663 | 1671 | }else if( (p = manifest_parse(pContent, rid, 0))==0 ){ |
| 1664 | 1672 | assert( blob_is_reset(pContent) || pContent==0 ); |
| 1673 | + fossil_error(1, "syntax error in manifest"); | |
| 1665 | 1674 | return 0; |
| 1666 | 1675 | } |
| 1667 | 1676 | if( g.xlinkClusterOnly && p->type!=CFTYPE_CLUSTER ){ |
| 1668 | 1677 | manifest_destroy(p); |
| 1669 | 1678 | assert( blob_is_reset(pContent) ); |
| 1679 | + fossil_error(1, "no manifest"); | |
| 1670 | 1680 | return 0; |
| 1671 | 1681 | } |
| 1672 | 1682 | if( p->type==CFTYPE_MANIFEST && fetch_baseline(p, 0) ){ |
| 1673 | 1683 | manifest_destroy(p); |
| 1674 | 1684 | assert( blob_is_reset(pContent) ); |
| 1685 | + fossil_error(1, "cannot fetch baseline manifest"); | |
| 1675 | 1686 | return 0; |
| 1676 | 1687 | } |
| 1677 | 1688 | db_begin_transaction(); |
| 1678 | 1689 | if( p->type==CFTYPE_MANIFEST ){ |
| 1690 | + zScript = xfer_commit_code(); | |
| 1691 | + zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); | |
| 1679 | 1692 | if( !db_exists("SELECT 1 FROM mlink WHERE mid=%d", rid) ){ |
| 1680 | 1693 | char *zCom; |
| 1681 | 1694 | for(i=0; i<p->nParent; i++){ |
| 1682 | 1695 | int pid = uuid_to_rid(p->azParent[i], 1); |
| 1683 | 1696 | db_multi_exec("INSERT OR IGNORE INTO plink(pid, cid, isprim, mtime)" |
| @@ -1768,11 +1781,11 @@ | ||
| 1768 | 1781 | switch( p->aTag[i].zName[0] ){ |
| 1769 | 1782 | case '-': type = 0; break; /* Cancel prior occurrences */ |
| 1770 | 1783 | case '+': type = 1; break; /* Apply to target only */ |
| 1771 | 1784 | case '*': type = 2; break; /* Propagate to descendants */ |
| 1772 | 1785 | default: |
| 1773 | - fossil_fatal("unknown tag type in manifest: %s", p->aTag); | |
| 1786 | + fossil_error(1, "unknown tag type in manifest: %s", p->aTag); | |
| 1774 | 1787 | return 0; |
| 1775 | 1788 | } |
| 1776 | 1789 | tag_insert(&p->aTag[i].zName[1], type, p->aTag[i].zValue, |
| 1777 | 1790 | rid, p->rDate, tid); |
| 1778 | 1791 | } |
| @@ -1870,10 +1883,12 @@ | ||
| 1870 | 1883 | } |
| 1871 | 1884 | } |
| 1872 | 1885 | if( p->type==CFTYPE_TICKET ){ |
| 1873 | 1886 | char *zTag; |
| 1874 | 1887 | |
| 1888 | + zScript = xfer_ticket_code(); | |
| 1889 | + zUuid = p->zTicketUuid; | |
| 1875 | 1890 | assert( manifest_crosslink_busy==1 ); |
| 1876 | 1891 | zTag = mprintf("tkt-%s", p->zTicketUuid); |
| 1877 | 1892 | tag_insert(zTag, 1, 0, rid, p->rDate, rid); |
| 1878 | 1893 | free(zTag); |
| 1879 | 1894 | db_multi_exec("INSERT OR IGNORE INTO pending_tkt VALUES(%Q)", |
| @@ -1934,33 +1949,39 @@ | ||
| 1934 | 1949 | if( p->type==CFTYPE_CONTROL ){ |
| 1935 | 1950 | Blob comment; |
| 1936 | 1951 | int i; |
| 1937 | 1952 | const char *zName; |
| 1938 | 1953 | const char *zValue; |
| 1939 | - const char *zUuid; | |
| 1954 | + const char *zTagUuid; | |
| 1940 | 1955 | int branchMove = 0; |
| 1941 | 1956 | blob_zero(&comment); |
| 1942 | 1957 | if( p->zComment ){ |
| 1943 | 1958 | blob_appendf(&comment, " %s.", p->zComment); |
| 1944 | 1959 | } |
| 1945 | 1960 | /* Next loop expects tags to be sorted on UUID, so sort it. */ |
| 1946 | 1961 | qsort(p->aTag, p->nTag, sizeof(p->aTag[0]), tag_compare); |
| 1947 | 1962 | for(i=0; i<p->nTag; i++){ |
| 1948 | - zUuid = p->aTag[i].zUuid; | |
| 1949 | - if( !zUuid ) continue; | |
| 1950 | - if( i==0 || fossil_strcmp(zUuid, p->aTag[i-1].zUuid)!=0 ){ | |
| 1963 | + zTagUuid = p->aTag[i].zUuid; | |
| 1964 | + if( !zTagUuid ) continue; | |
| 1965 | + if( i==0 || fossil_strcmp(zTagUuid, p->aTag[i-1].zUuid)!=0 ){ | |
| 1951 | 1966 | blob_appendf(&comment, |
| 1952 | 1967 | " Edit [%S]:", |
| 1953 | - zUuid); | |
| 1968 | + zTagUuid); | |
| 1954 | 1969 | branchMove = 0; |
| 1970 | + if( db_exists("SELECT 1 FROM event, blob" | |
| 1971 | + " WHERE event.type='ci' AND event.objid=blob.rid" | |
| 1972 | + " AND blob.uuid='%s'", zTagUuid) ){ | |
| 1973 | + zScript = xfer_commit_code(); | |
| 1974 | + zUuid = zTagUuid; | |
| 1975 | + } | |
| 1955 | 1976 | } |
| 1956 | 1977 | zName = p->aTag[i].zName; |
| 1957 | 1978 | zValue = p->aTag[i].zValue; |
| 1958 | 1979 | if( strcmp(zName, "*branch")==0 ){ |
| 1959 | 1980 | blob_appendf(&comment, |
| 1960 | 1981 | " Move to branch [/timeline?r=%h&nd&dp=%S | %h].", |
| 1961 | - zValue, zUuid, zValue); | |
| 1982 | + zValue, zTagUuid, zValue); | |
| 1962 | 1983 | branchMove = 1; |
| 1963 | 1984 | continue; |
| 1964 | 1985 | }else if( strcmp(zName, "*bgcolor")==0 ){ |
| 1965 | 1986 | blob_appendf(&comment, |
| 1966 | 1987 | " Change branch background color to \"%h\".", zValue); |
| @@ -2021,17 +2042,23 @@ | ||
| 2021 | 2042 | p->rDate, rid, p->zUser, blob_str(&comment)+1 |
| 2022 | 2043 | ); |
| 2023 | 2044 | blob_reset(&comment); |
| 2024 | 2045 | } |
| 2025 | 2046 | db_end_transaction(0); |
| 2047 | + if( zScript && (flags & MC_PERMIT_HOOKS) ){ | |
| 2048 | + result = xfer_run_common_script(); | |
| 2049 | + if( result==TH_OK ){ | |
| 2050 | + result = xfer_run_script(zScript, zUuid); | |
| 2051 | + } | |
| 2052 | + } | |
| 2026 | 2053 | if( p->type==CFTYPE_MANIFEST ){ |
| 2027 | 2054 | manifest_cache_insert(p); |
| 2028 | 2055 | }else{ |
| 2029 | 2056 | manifest_destroy(p); |
| 2030 | 2057 | } |
| 2031 | 2058 | assert( blob_is_reset(pContent) ); |
| 2032 | - return 1; | |
| 2059 | + return ( result!=TH_ERROR ); | |
| 2033 | 2060 | } |
| 2034 | 2061 | |
| 2035 | 2062 | /* |
| 2036 | 2063 | ** COMMAND: test-crosslink |
| 2037 | 2064 | ** |
| @@ -2045,7 +2072,7 @@ | ||
| 2045 | 2072 | Blob content; |
| 2046 | 2073 | db_find_and_open_repository(0, 0); |
| 2047 | 2074 | if( g.argc!=3 ) usage("RECORDID"); |
| 2048 | 2075 | rid = name_to_rid(g.argv[2]); |
| 2049 | 2076 | content_get(rid, &content); |
| 2050 | - manifest_crosslink(rid, &content); | |
| 2077 | + manifest_crosslink(rid, &content, MC_NONE); | |
| 2051 | 2078 | } |
| 2052 | 2079 |
| --- src/manifest.c | |
| +++ src/manifest.c | |
| @@ -42,10 +42,16 @@ | |
| 42 | */ |
| 43 | #define PERM_REG 0 /* regular file */ |
| 44 | #define PERM_EXE 1 /* executable */ |
| 45 | #define PERM_LNK 2 /* symlink */ |
| 46 | |
| 47 | /* |
| 48 | ** A single F-card within a manifest |
| 49 | */ |
| 50 | struct ManifestFile { |
| 51 | char *zName; /* Name of a file */ |
| @@ -1650,34 +1656,41 @@ | |
| 1650 | ** Historical note: This routine original processed manifests only. |
| 1651 | ** Processing for other control artifacts was added later. The name |
| 1652 | ** of the routine, "manifest_crosslink", and the name of this source |
| 1653 | ** file, is a legacy of its original use. |
| 1654 | */ |
| 1655 | int manifest_crosslink(int rid, Blob *pContent){ |
| 1656 | int i; |
| 1657 | Manifest *p; |
| 1658 | Stmt q; |
| 1659 | int parentid = 0; |
| 1660 | |
| 1661 | if( (p = manifest_cache_find(rid))!=0 ){ |
| 1662 | blob_reset(pContent); |
| 1663 | }else if( (p = manifest_parse(pContent, rid, 0))==0 ){ |
| 1664 | assert( blob_is_reset(pContent) || pContent==0 ); |
| 1665 | return 0; |
| 1666 | } |
| 1667 | if( g.xlinkClusterOnly && p->type!=CFTYPE_CLUSTER ){ |
| 1668 | manifest_destroy(p); |
| 1669 | assert( blob_is_reset(pContent) ); |
| 1670 | return 0; |
| 1671 | } |
| 1672 | if( p->type==CFTYPE_MANIFEST && fetch_baseline(p, 0) ){ |
| 1673 | manifest_destroy(p); |
| 1674 | assert( blob_is_reset(pContent) ); |
| 1675 | return 0; |
| 1676 | } |
| 1677 | db_begin_transaction(); |
| 1678 | if( p->type==CFTYPE_MANIFEST ){ |
| 1679 | if( !db_exists("SELECT 1 FROM mlink WHERE mid=%d", rid) ){ |
| 1680 | char *zCom; |
| 1681 | for(i=0; i<p->nParent; i++){ |
| 1682 | int pid = uuid_to_rid(p->azParent[i], 1); |
| 1683 | db_multi_exec("INSERT OR IGNORE INTO plink(pid, cid, isprim, mtime)" |
| @@ -1768,11 +1781,11 @@ | |
| 1768 | switch( p->aTag[i].zName[0] ){ |
| 1769 | case '-': type = 0; break; /* Cancel prior occurrences */ |
| 1770 | case '+': type = 1; break; /* Apply to target only */ |
| 1771 | case '*': type = 2; break; /* Propagate to descendants */ |
| 1772 | default: |
| 1773 | fossil_fatal("unknown tag type in manifest: %s", p->aTag); |
| 1774 | return 0; |
| 1775 | } |
| 1776 | tag_insert(&p->aTag[i].zName[1], type, p->aTag[i].zValue, |
| 1777 | rid, p->rDate, tid); |
| 1778 | } |
| @@ -1870,10 +1883,12 @@ | |
| 1870 | } |
| 1871 | } |
| 1872 | if( p->type==CFTYPE_TICKET ){ |
| 1873 | char *zTag; |
| 1874 | |
| 1875 | assert( manifest_crosslink_busy==1 ); |
| 1876 | zTag = mprintf("tkt-%s", p->zTicketUuid); |
| 1877 | tag_insert(zTag, 1, 0, rid, p->rDate, rid); |
| 1878 | free(zTag); |
| 1879 | db_multi_exec("INSERT OR IGNORE INTO pending_tkt VALUES(%Q)", |
| @@ -1934,33 +1949,39 @@ | |
| 1934 | if( p->type==CFTYPE_CONTROL ){ |
| 1935 | Blob comment; |
| 1936 | int i; |
| 1937 | const char *zName; |
| 1938 | const char *zValue; |
| 1939 | const char *zUuid; |
| 1940 | int branchMove = 0; |
| 1941 | blob_zero(&comment); |
| 1942 | if( p->zComment ){ |
| 1943 | blob_appendf(&comment, " %s.", p->zComment); |
| 1944 | } |
| 1945 | /* Next loop expects tags to be sorted on UUID, so sort it. */ |
| 1946 | qsort(p->aTag, p->nTag, sizeof(p->aTag[0]), tag_compare); |
| 1947 | for(i=0; i<p->nTag; i++){ |
| 1948 | zUuid = p->aTag[i].zUuid; |
| 1949 | if( !zUuid ) continue; |
| 1950 | if( i==0 || fossil_strcmp(zUuid, p->aTag[i-1].zUuid)!=0 ){ |
| 1951 | blob_appendf(&comment, |
| 1952 | " Edit [%S]:", |
| 1953 | zUuid); |
| 1954 | branchMove = 0; |
| 1955 | } |
| 1956 | zName = p->aTag[i].zName; |
| 1957 | zValue = p->aTag[i].zValue; |
| 1958 | if( strcmp(zName, "*branch")==0 ){ |
| 1959 | blob_appendf(&comment, |
| 1960 | " Move to branch [/timeline?r=%h&nd&dp=%S | %h].", |
| 1961 | zValue, zUuid, zValue); |
| 1962 | branchMove = 1; |
| 1963 | continue; |
| 1964 | }else if( strcmp(zName, "*bgcolor")==0 ){ |
| 1965 | blob_appendf(&comment, |
| 1966 | " Change branch background color to \"%h\".", zValue); |
| @@ -2021,17 +2042,23 @@ | |
| 2021 | p->rDate, rid, p->zUser, blob_str(&comment)+1 |
| 2022 | ); |
| 2023 | blob_reset(&comment); |
| 2024 | } |
| 2025 | db_end_transaction(0); |
| 2026 | if( p->type==CFTYPE_MANIFEST ){ |
| 2027 | manifest_cache_insert(p); |
| 2028 | }else{ |
| 2029 | manifest_destroy(p); |
| 2030 | } |
| 2031 | assert( blob_is_reset(pContent) ); |
| 2032 | return 1; |
| 2033 | } |
| 2034 | |
| 2035 | /* |
| 2036 | ** COMMAND: test-crosslink |
| 2037 | ** |
| @@ -2045,7 +2072,7 @@ | |
| 2045 | Blob content; |
| 2046 | db_find_and_open_repository(0, 0); |
| 2047 | if( g.argc!=3 ) usage("RECORDID"); |
| 2048 | rid = name_to_rid(g.argv[2]); |
| 2049 | content_get(rid, &content); |
| 2050 | manifest_crosslink(rid, &content); |
| 2051 | } |
| 2052 |
| --- src/manifest.c | |
| +++ src/manifest.c | |
| @@ -42,10 +42,16 @@ | |
| 42 | */ |
| 43 | #define PERM_REG 0 /* regular file */ |
| 44 | #define PERM_EXE 1 /* executable */ |
| 45 | #define PERM_LNK 2 /* symlink */ |
| 46 | |
| 47 | /* |
| 48 | ** Flags for use with manifest_crosslink(). |
| 49 | */ |
| 50 | #define MC_NONE 0 /* default handling */ |
| 51 | #define MC_PERMIT_HOOKS 1 /* permit hooks to execute */ |
| 52 | |
| 53 | /* |
| 54 | ** A single F-card within a manifest |
| 55 | */ |
| 56 | struct ManifestFile { |
| 57 | char *zName; /* Name of a file */ |
| @@ -1650,34 +1656,41 @@ | |
| 1656 | ** Historical note: This routine original processed manifests only. |
| 1657 | ** Processing for other control artifacts was added later. The name |
| 1658 | ** of the routine, "manifest_crosslink", and the name of this source |
| 1659 | ** file, is a legacy of its original use. |
| 1660 | */ |
| 1661 | int manifest_crosslink(int rid, Blob *pContent, int flags){ |
| 1662 | int i, result = TH_OK; |
| 1663 | Manifest *p; |
| 1664 | Stmt q; |
| 1665 | int parentid = 0; |
| 1666 | const char *zScript = 0; |
| 1667 | const char *zUuid = 0; |
| 1668 | |
| 1669 | if( (p = manifest_cache_find(rid))!=0 ){ |
| 1670 | blob_reset(pContent); |
| 1671 | }else if( (p = manifest_parse(pContent, rid, 0))==0 ){ |
| 1672 | assert( blob_is_reset(pContent) || pContent==0 ); |
| 1673 | fossil_error(1, "syntax error in manifest"); |
| 1674 | return 0; |
| 1675 | } |
| 1676 | if( g.xlinkClusterOnly && p->type!=CFTYPE_CLUSTER ){ |
| 1677 | manifest_destroy(p); |
| 1678 | assert( blob_is_reset(pContent) ); |
| 1679 | fossil_error(1, "no manifest"); |
| 1680 | return 0; |
| 1681 | } |
| 1682 | if( p->type==CFTYPE_MANIFEST && fetch_baseline(p, 0) ){ |
| 1683 | manifest_destroy(p); |
| 1684 | assert( blob_is_reset(pContent) ); |
| 1685 | fossil_error(1, "cannot fetch baseline manifest"); |
| 1686 | return 0; |
| 1687 | } |
| 1688 | db_begin_transaction(); |
| 1689 | if( p->type==CFTYPE_MANIFEST ){ |
| 1690 | zScript = xfer_commit_code(); |
| 1691 | zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); |
| 1692 | if( !db_exists("SELECT 1 FROM mlink WHERE mid=%d", rid) ){ |
| 1693 | char *zCom; |
| 1694 | for(i=0; i<p->nParent; i++){ |
| 1695 | int pid = uuid_to_rid(p->azParent[i], 1); |
| 1696 | db_multi_exec("INSERT OR IGNORE INTO plink(pid, cid, isprim, mtime)" |
| @@ -1768,11 +1781,11 @@ | |
| 1781 | switch( p->aTag[i].zName[0] ){ |
| 1782 | case '-': type = 0; break; /* Cancel prior occurrences */ |
| 1783 | case '+': type = 1; break; /* Apply to target only */ |
| 1784 | case '*': type = 2; break; /* Propagate to descendants */ |
| 1785 | default: |
| 1786 | fossil_error(1, "unknown tag type in manifest: %s", p->aTag); |
| 1787 | return 0; |
| 1788 | } |
| 1789 | tag_insert(&p->aTag[i].zName[1], type, p->aTag[i].zValue, |
| 1790 | rid, p->rDate, tid); |
| 1791 | } |
| @@ -1870,10 +1883,12 @@ | |
| 1883 | } |
| 1884 | } |
| 1885 | if( p->type==CFTYPE_TICKET ){ |
| 1886 | char *zTag; |
| 1887 | |
| 1888 | zScript = xfer_ticket_code(); |
| 1889 | zUuid = p->zTicketUuid; |
| 1890 | assert( manifest_crosslink_busy==1 ); |
| 1891 | zTag = mprintf("tkt-%s", p->zTicketUuid); |
| 1892 | tag_insert(zTag, 1, 0, rid, p->rDate, rid); |
| 1893 | free(zTag); |
| 1894 | db_multi_exec("INSERT OR IGNORE INTO pending_tkt VALUES(%Q)", |
| @@ -1934,33 +1949,39 @@ | |
| 1949 | if( p->type==CFTYPE_CONTROL ){ |
| 1950 | Blob comment; |
| 1951 | int i; |
| 1952 | const char *zName; |
| 1953 | const char *zValue; |
| 1954 | const char *zTagUuid; |
| 1955 | int branchMove = 0; |
| 1956 | blob_zero(&comment); |
| 1957 | if( p->zComment ){ |
| 1958 | blob_appendf(&comment, " %s.", p->zComment); |
| 1959 | } |
| 1960 | /* Next loop expects tags to be sorted on UUID, so sort it. */ |
| 1961 | qsort(p->aTag, p->nTag, sizeof(p->aTag[0]), tag_compare); |
| 1962 | for(i=0; i<p->nTag; i++){ |
| 1963 | zTagUuid = p->aTag[i].zUuid; |
| 1964 | if( !zTagUuid ) continue; |
| 1965 | if( i==0 || fossil_strcmp(zTagUuid, p->aTag[i-1].zUuid)!=0 ){ |
| 1966 | blob_appendf(&comment, |
| 1967 | " Edit [%S]:", |
| 1968 | zTagUuid); |
| 1969 | branchMove = 0; |
| 1970 | if( db_exists("SELECT 1 FROM event, blob" |
| 1971 | " WHERE event.type='ci' AND event.objid=blob.rid" |
| 1972 | " AND blob.uuid='%s'", zTagUuid) ){ |
| 1973 | zScript = xfer_commit_code(); |
| 1974 | zUuid = zTagUuid; |
| 1975 | } |
| 1976 | } |
| 1977 | zName = p->aTag[i].zName; |
| 1978 | zValue = p->aTag[i].zValue; |
| 1979 | if( strcmp(zName, "*branch")==0 ){ |
| 1980 | blob_appendf(&comment, |
| 1981 | " Move to branch [/timeline?r=%h&nd&dp=%S | %h].", |
| 1982 | zValue, zTagUuid, zValue); |
| 1983 | branchMove = 1; |
| 1984 | continue; |
| 1985 | }else if( strcmp(zName, "*bgcolor")==0 ){ |
| 1986 | blob_appendf(&comment, |
| 1987 | " Change branch background color to \"%h\".", zValue); |
| @@ -2021,17 +2042,23 @@ | |
| 2042 | p->rDate, rid, p->zUser, blob_str(&comment)+1 |
| 2043 | ); |
| 2044 | blob_reset(&comment); |
| 2045 | } |
| 2046 | db_end_transaction(0); |
| 2047 | if( zScript && (flags & MC_PERMIT_HOOKS) ){ |
| 2048 | result = xfer_run_common_script(); |
| 2049 | if( result==TH_OK ){ |
| 2050 | result = xfer_run_script(zScript, zUuid); |
| 2051 | } |
| 2052 | } |
| 2053 | if( p->type==CFTYPE_MANIFEST ){ |
| 2054 | manifest_cache_insert(p); |
| 2055 | }else{ |
| 2056 | manifest_destroy(p); |
| 2057 | } |
| 2058 | assert( blob_is_reset(pContent) ); |
| 2059 | return ( result!=TH_ERROR ); |
| 2060 | } |
| 2061 | |
| 2062 | /* |
| 2063 | ** COMMAND: test-crosslink |
| 2064 | ** |
| @@ -2045,7 +2072,7 @@ | |
| 2072 | Blob content; |
| 2073 | db_find_and_open_repository(0, 0); |
| 2074 | if( g.argc!=3 ) usage("RECORDID"); |
| 2075 | rid = name_to_rid(g.argv[2]); |
| 2076 | content_get(rid, &content); |
| 2077 | manifest_crosslink(rid, &content, MC_NONE); |
| 2078 | } |
| 2079 |
+1
-1
| --- src/rebuild.c | ||
| +++ src/rebuild.c | ||
| @@ -250,11 +250,11 @@ | ||
| 250 | 250 | blob_copy(©, pBase); |
| 251 | 251 | pUse = © |
| 252 | 252 | } |
| 253 | 253 | if( zFNameFormat==0 ){ |
| 254 | 254 | /* We are doing "fossil rebuild" */ |
| 255 | - manifest_crosslink(rid, pUse); | |
| 255 | + manifest_crosslink(rid, pUse, MC_NONE); | |
| 256 | 256 | }else{ |
| 257 | 257 | /* We are doing "fossil deconstruct" */ |
| 258 | 258 | char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); |
| 259 | 259 | char *zFile = mprintf(zFNameFormat, zUuid, zUuid+prefixLength); |
| 260 | 260 | blob_write_to_file(pUse,zFile); |
| 261 | 261 |
| --- src/rebuild.c | |
| +++ src/rebuild.c | |
| @@ -250,11 +250,11 @@ | |
| 250 | blob_copy(©, pBase); |
| 251 | pUse = © |
| 252 | } |
| 253 | if( zFNameFormat==0 ){ |
| 254 | /* We are doing "fossil rebuild" */ |
| 255 | manifest_crosslink(rid, pUse); |
| 256 | }else{ |
| 257 | /* We are doing "fossil deconstruct" */ |
| 258 | char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); |
| 259 | char *zFile = mprintf(zFNameFormat, zUuid, zUuid+prefixLength); |
| 260 | blob_write_to_file(pUse,zFile); |
| 261 |
| --- src/rebuild.c | |
| +++ src/rebuild.c | |
| @@ -250,11 +250,11 @@ | |
| 250 | blob_copy(©, pBase); |
| 251 | pUse = © |
| 252 | } |
| 253 | if( zFNameFormat==0 ){ |
| 254 | /* We are doing "fossil rebuild" */ |
| 255 | manifest_crosslink(rid, pUse, MC_NONE); |
| 256 | }else{ |
| 257 | /* We are doing "fossil deconstruct" */ |
| 258 | char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); |
| 259 | char *zFile = mprintf(zFNameFormat, zUuid, zUuid+prefixLength); |
| 260 | blob_write_to_file(pUse,zFile); |
| 261 |
+1
-1
| --- src/setup.c | ||
| +++ src/setup.c | ||
| @@ -812,11 +812,11 @@ | ||
| 812 | 812 | if( zQ && fossil_strcmp(zQ,zVal)!=0 ){ |
| 813 | 813 | login_verify_csrf_secret(); |
| 814 | 814 | db_set(zVar, zQ, 0); |
| 815 | 815 | zVal = zQ; |
| 816 | 816 | } |
| 817 | - @ <input type="text" name="%s(zQParm)" value="%h(zVal)" size="%d(width)" | |
| 817 | + @ <input type="text" id="%s(zQParm)" name="%s(zQParm)" value="%h(zVal)" size="%d(width)" | |
| 818 | 818 | if( disabled ){ |
| 819 | 819 | @ disabled="disabled" |
| 820 | 820 | } |
| 821 | 821 | @ /> <b>%s(zLabel)</b> |
| 822 | 822 | } |
| 823 | 823 |
| --- src/setup.c | |
| +++ src/setup.c | |
| @@ -812,11 +812,11 @@ | |
| 812 | if( zQ && fossil_strcmp(zQ,zVal)!=0 ){ |
| 813 | login_verify_csrf_secret(); |
| 814 | db_set(zVar, zQ, 0); |
| 815 | zVal = zQ; |
| 816 | } |
| 817 | @ <input type="text" name="%s(zQParm)" value="%h(zVal)" size="%d(width)" |
| 818 | if( disabled ){ |
| 819 | @ disabled="disabled" |
| 820 | } |
| 821 | @ /> <b>%s(zLabel)</b> |
| 822 | } |
| 823 |
| --- src/setup.c | |
| +++ src/setup.c | |
| @@ -812,11 +812,11 @@ | |
| 812 | if( zQ && fossil_strcmp(zQ,zVal)!=0 ){ |
| 813 | login_verify_csrf_secret(); |
| 814 | db_set(zVar, zQ, 0); |
| 815 | zVal = zQ; |
| 816 | } |
| 817 | @ <input type="text" id="%s(zQParm)" name="%s(zQParm)" value="%h(zVal)" size="%d(width)" |
| 818 | if( disabled ){ |
| 819 | @ disabled="disabled" |
| 820 | } |
| 821 | @ /> <b>%s(zLabel)</b> |
| 822 | } |
| 823 |
+1
-1
| --- src/tag.c | ||
| +++ src/tag.c | ||
| @@ -326,11 +326,11 @@ | ||
| 326 | 326 | } |
| 327 | 327 | blob_appendf(&ctrl, "U %F\n", zUserOvrd ? zUserOvrd : g.zLogin); |
| 328 | 328 | md5sum_blob(&ctrl, &cksum); |
| 329 | 329 | blob_appendf(&ctrl, "Z %b\n", &cksum); |
| 330 | 330 | nrid = content_put(&ctrl); |
| 331 | - manifest_crosslink(nrid, &ctrl); | |
| 331 | + manifest_crosslink(nrid, &ctrl, MC_PERMIT_HOOKS); | |
| 332 | 332 | assert( blob_is_reset(&ctrl) ); |
| 333 | 333 | } |
| 334 | 334 | |
| 335 | 335 | /* |
| 336 | 336 | ** COMMAND: tag |
| 337 | 337 |
| --- src/tag.c | |
| +++ src/tag.c | |
| @@ -326,11 +326,11 @@ | |
| 326 | } |
| 327 | blob_appendf(&ctrl, "U %F\n", zUserOvrd ? zUserOvrd : g.zLogin); |
| 328 | md5sum_blob(&ctrl, &cksum); |
| 329 | blob_appendf(&ctrl, "Z %b\n", &cksum); |
| 330 | nrid = content_put(&ctrl); |
| 331 | manifest_crosslink(nrid, &ctrl); |
| 332 | assert( blob_is_reset(&ctrl) ); |
| 333 | } |
| 334 | |
| 335 | /* |
| 336 | ** COMMAND: tag |
| 337 |
| --- src/tag.c | |
| +++ src/tag.c | |
| @@ -326,11 +326,11 @@ | |
| 326 | } |
| 327 | blob_appendf(&ctrl, "U %F\n", zUserOvrd ? zUserOvrd : g.zLogin); |
| 328 | md5sum_blob(&ctrl, &cksum); |
| 329 | blob_appendf(&ctrl, "Z %b\n", &cksum); |
| 330 | nrid = content_put(&ctrl); |
| 331 | manifest_crosslink(nrid, &ctrl, MC_PERMIT_HOOKS); |
| 332 | assert( blob_is_reset(&ctrl) ); |
| 333 | } |
| 334 | |
| 335 | /* |
| 336 | ** COMMAND: tag |
| 337 |
+115
| --- src/th_main.c | ||
| +++ src/th_main.c | ||
| @@ -827,10 +827,124 @@ | ||
| 827 | 827 | rc = TH_ERROR; |
| 828 | 828 | } |
| 829 | 829 | re_free(pRe); |
| 830 | 830 | return rc; |
| 831 | 831 | } |
| 832 | + | |
| 833 | +/* | |
| 834 | +** TH command: http ?-asynchronous? ?--? url ?payload? | |
| 835 | +** | |
| 836 | +** Perform an HTTP or HTTPS request for the specified URL. If a | |
| 837 | +** payload is present, it will be interpreted as text/plain and | |
| 838 | +** the POST method will be used; otherwise, the GET method will | |
| 839 | +** be used. Upon success, if the -asynchronous option is used, an | |
| 840 | +** empty string is returned as the result; otherwise, the response | |
| 841 | +** from the server is returned as the result. Synchronous requests | |
| 842 | +** are not currently implemented. | |
| 843 | +*/ | |
| 844 | +#define HTTP_WRONGNUMARGS "http ?-asynchronous? ?--? url ?payload?" | |
| 845 | +static int httpCmd( | |
| 846 | + Th_Interp *interp, | |
| 847 | + void *p, | |
| 848 | + int argc, | |
| 849 | + const char **argv, | |
| 850 | + int *argl | |
| 851 | +){ | |
| 852 | + int nArg = 1; | |
| 853 | + int fAsynchronous = 0; | |
| 854 | + const char *zType, *zRegexp; | |
| 855 | + Blob payload; | |
| 856 | + ReCompiled *pRe = 0; | |
| 857 | + UrlData urlData; | |
| 858 | + | |
| 859 | + if( argc<2 || argc>5 ){ | |
| 860 | + return Th_WrongNumArgs(interp, HTTP_WRONGNUMARGS); | |
| 861 | + } | |
| 862 | + if( fossil_strnicmp(argv[nArg], "-asynchronous", argl[nArg])==0 ){ | |
| 863 | + fAsynchronous = 1; nArg++; | |
| 864 | + } | |
| 865 | + if( fossil_strcmp(argv[nArg], "--")==0 ) nArg++; | |
| 866 | + if( nArg+1!=argc && nArg+2!=argc ){ | |
| 867 | + return Th_WrongNumArgs(interp, REGEXP_WRONGNUMARGS); | |
| 868 | + } | |
| 869 | + memset(&urlData, '\0', sizeof(urlData)); | |
| 870 | + url_parse_local(argv[nArg], 0, &urlData); | |
| 871 | + if( urlData.isSsh || urlData.isFile ){ | |
| 872 | + Th_ErrorMessage(interp, "url must be http:// or https://", 0, 0); | |
| 873 | + return TH_ERROR; | |
| 874 | + } | |
| 875 | + zRegexp = db_get("th1-uri-regexp", 0); | |
| 876 | + if( zRegexp && zRegexp[0] ){ | |
| 877 | + const char *zErr = re_compile(&pRe, zRegexp, 0); | |
| 878 | + if( zErr ){ | |
| 879 | + Th_SetResult(interp, zErr, -1); | |
| 880 | + return TH_ERROR; | |
| 881 | + } | |
| 882 | + } | |
| 883 | + if( !pRe || !re_match(pRe, (const unsigned char *)urlData.canonical, -1) ){ | |
| 884 | + Th_SetResult(interp, "url not allowed", -1); | |
| 885 | + re_free(pRe); | |
| 886 | + return TH_ERROR; | |
| 887 | + } | |
| 888 | + re_free(pRe); | |
| 889 | + blob_zero(&payload); | |
| 890 | + if( nArg+2==argc ){ | |
| 891 | + blob_append(&payload, argv[nArg+1], argl[nArg+1]); | |
| 892 | + zType = "POST"; | |
| 893 | + }else{ | |
| 894 | + zType = "GET"; | |
| 895 | + } | |
| 896 | + if( fAsynchronous ){ | |
| 897 | + const char *zSep, *zParams; | |
| 898 | + Blob hdr; | |
| 899 | + zParams = strrchr(argv[nArg], '?'); | |
| 900 | + if( strlen(urlData.path)>0 && zParams!=argv[nArg] ){ | |
| 901 | + zSep = ""; | |
| 902 | + }else{ | |
| 903 | + zSep = "/"; | |
| 904 | + } | |
| 905 | + blob_zero(&hdr); | |
| 906 | + blob_appendf(&hdr, "%s %s%s%s HTTP/1.0\r\n", | |
| 907 | + zType, zSep, urlData.path, zParams ? zParams : ""); | |
| 908 | + if( urlData.proxyAuth ){ | |
| 909 | + blob_appendf(&hdr, "Proxy-Authorization: %s\r\n", urlData.proxyAuth); | |
| 910 | + } | |
| 911 | + if( urlData.passwd && urlData.user && urlData.passwd[0]=='#' ){ | |
| 912 | + char *zCredentials = mprintf("%s:%s", urlData.user, &urlData.passwd[1]); | |
| 913 | + char *zEncoded = encode64(zCredentials, -1); | |
| 914 | + blob_appendf(&hdr, "Authorization: Basic %s\r\n", zEncoded); | |
| 915 | + fossil_free(zEncoded); | |
| 916 | + fossil_free(zCredentials); | |
| 917 | + } | |
| 918 | + blob_appendf(&hdr, "Host: %s\r\n" | |
| 919 | + "User-Agent: %s\r\n", urlData.hostname, get_user_agent()); | |
| 920 | + if( zType[0]=='P' ){ | |
| 921 | + blob_appendf(&hdr, "Content-Type: application/x-www-form-urlencoded\r\n" | |
| 922 | + "Content-Length: %d\r\n\r\n", blob_size(&payload)); | |
| 923 | + }else{ | |
| 924 | + blob_appendf(&hdr, "\r\n"); | |
| 925 | + } | |
| 926 | + if( transport_open(&urlData) ){ | |
| 927 | + Th_ErrorMessage(interp, transport_errmsg(&urlData), 0, 0); | |
| 928 | + blob_reset(&hdr); | |
| 929 | + blob_reset(&payload); | |
| 930 | + return TH_ERROR; | |
| 931 | + } | |
| 932 | + transport_send(&urlData, &hdr); | |
| 933 | + transport_send(&urlData, &payload); | |
| 934 | + blob_reset(&hdr); | |
| 935 | + blob_reset(&payload); | |
| 936 | + transport_close(&urlData); | |
| 937 | + Th_SetResult(interp, 0, 0); /* NOTE: Asynchronous, no results. */ | |
| 938 | + return TH_OK; | |
| 939 | + }else{ | |
| 940 | + Th_ErrorMessage(interp, | |
| 941 | + "synchronous requests are not yet implemented", 0, 0); | |
| 942 | + blob_reset(&payload); | |
| 943 | + return TH_ERROR; | |
| 944 | + } | |
| 945 | +} | |
| 832 | 946 | |
| 833 | 947 | /* |
| 834 | 948 | ** Make sure the interpreter has been initialized. Initialize it if |
| 835 | 949 | ** it has not been already. |
| 836 | 950 | ** |
| @@ -855,10 +969,11 @@ | ||
| 855 | 969 | {"enable_output", enableOutputCmd, 0}, |
| 856 | 970 | {"hascap", hascapCmd, 0}, |
| 857 | 971 | {"hasfeature", hasfeatureCmd, 0}, |
| 858 | 972 | {"html", putsCmd, (void*)&aFlags[0]}, |
| 859 | 973 | {"htmlize", htmlizeCmd, 0}, |
| 974 | + {"http", httpCmd, 0}, | |
| 860 | 975 | {"linecount", linecntCmd, 0}, |
| 861 | 976 | {"puts", putsCmd, (void*)&aFlags[1]}, |
| 862 | 977 | {"query", queryCmd, 0}, |
| 863 | 978 | {"randhex", randhexCmd, 0}, |
| 864 | 979 | {"regexp", regexpCmd, 0}, |
| 865 | 980 |
| --- src/th_main.c | |
| +++ src/th_main.c | |
| @@ -827,10 +827,124 @@ | |
| 827 | rc = TH_ERROR; |
| 828 | } |
| 829 | re_free(pRe); |
| 830 | return rc; |
| 831 | } |
| 832 | |
| 833 | /* |
| 834 | ** Make sure the interpreter has been initialized. Initialize it if |
| 835 | ** it has not been already. |
| 836 | ** |
| @@ -855,10 +969,11 @@ | |
| 855 | {"enable_output", enableOutputCmd, 0}, |
| 856 | {"hascap", hascapCmd, 0}, |
| 857 | {"hasfeature", hasfeatureCmd, 0}, |
| 858 | {"html", putsCmd, (void*)&aFlags[0]}, |
| 859 | {"htmlize", htmlizeCmd, 0}, |
| 860 | {"linecount", linecntCmd, 0}, |
| 861 | {"puts", putsCmd, (void*)&aFlags[1]}, |
| 862 | {"query", queryCmd, 0}, |
| 863 | {"randhex", randhexCmd, 0}, |
| 864 | {"regexp", regexpCmd, 0}, |
| 865 |
| --- src/th_main.c | |
| +++ src/th_main.c | |
| @@ -827,10 +827,124 @@ | |
| 827 | rc = TH_ERROR; |
| 828 | } |
| 829 | re_free(pRe); |
| 830 | return rc; |
| 831 | } |
| 832 | |
| 833 | /* |
| 834 | ** TH command: http ?-asynchronous? ?--? url ?payload? |
| 835 | ** |
| 836 | ** Perform an HTTP or HTTPS request for the specified URL. If a |
| 837 | ** payload is present, it will be interpreted as text/plain and |
| 838 | ** the POST method will be used; otherwise, the GET method will |
| 839 | ** be used. Upon success, if the -asynchronous option is used, an |
| 840 | ** empty string is returned as the result; otherwise, the response |
| 841 | ** from the server is returned as the result. Synchronous requests |
| 842 | ** are not currently implemented. |
| 843 | */ |
| 844 | #define HTTP_WRONGNUMARGS "http ?-asynchronous? ?--? url ?payload?" |
| 845 | static int httpCmd( |
| 846 | Th_Interp *interp, |
| 847 | void *p, |
| 848 | int argc, |
| 849 | const char **argv, |
| 850 | int *argl |
| 851 | ){ |
| 852 | int nArg = 1; |
| 853 | int fAsynchronous = 0; |
| 854 | const char *zType, *zRegexp; |
| 855 | Blob payload; |
| 856 | ReCompiled *pRe = 0; |
| 857 | UrlData urlData; |
| 858 | |
| 859 | if( argc<2 || argc>5 ){ |
| 860 | return Th_WrongNumArgs(interp, HTTP_WRONGNUMARGS); |
| 861 | } |
| 862 | if( fossil_strnicmp(argv[nArg], "-asynchronous", argl[nArg])==0 ){ |
| 863 | fAsynchronous = 1; nArg++; |
| 864 | } |
| 865 | if( fossil_strcmp(argv[nArg], "--")==0 ) nArg++; |
| 866 | if( nArg+1!=argc && nArg+2!=argc ){ |
| 867 | return Th_WrongNumArgs(interp, REGEXP_WRONGNUMARGS); |
| 868 | } |
| 869 | memset(&urlData, '\0', sizeof(urlData)); |
| 870 | url_parse_local(argv[nArg], 0, &urlData); |
| 871 | if( urlData.isSsh || urlData.isFile ){ |
| 872 | Th_ErrorMessage(interp, "url must be http:// or https://", 0, 0); |
| 873 | return TH_ERROR; |
| 874 | } |
| 875 | zRegexp = db_get("th1-uri-regexp", 0); |
| 876 | if( zRegexp && zRegexp[0] ){ |
| 877 | const char *zErr = re_compile(&pRe, zRegexp, 0); |
| 878 | if( zErr ){ |
| 879 | Th_SetResult(interp, zErr, -1); |
| 880 | return TH_ERROR; |
| 881 | } |
| 882 | } |
| 883 | if( !pRe || !re_match(pRe, (const unsigned char *)urlData.canonical, -1) ){ |
| 884 | Th_SetResult(interp, "url not allowed", -1); |
| 885 | re_free(pRe); |
| 886 | return TH_ERROR; |
| 887 | } |
| 888 | re_free(pRe); |
| 889 | blob_zero(&payload); |
| 890 | if( nArg+2==argc ){ |
| 891 | blob_append(&payload, argv[nArg+1], argl[nArg+1]); |
| 892 | zType = "POST"; |
| 893 | }else{ |
| 894 | zType = "GET"; |
| 895 | } |
| 896 | if( fAsynchronous ){ |
| 897 | const char *zSep, *zParams; |
| 898 | Blob hdr; |
| 899 | zParams = strrchr(argv[nArg], '?'); |
| 900 | if( strlen(urlData.path)>0 && zParams!=argv[nArg] ){ |
| 901 | zSep = ""; |
| 902 | }else{ |
| 903 | zSep = "/"; |
| 904 | } |
| 905 | blob_zero(&hdr); |
| 906 | blob_appendf(&hdr, "%s %s%s%s HTTP/1.0\r\n", |
| 907 | zType, zSep, urlData.path, zParams ? zParams : ""); |
| 908 | if( urlData.proxyAuth ){ |
| 909 | blob_appendf(&hdr, "Proxy-Authorization: %s\r\n", urlData.proxyAuth); |
| 910 | } |
| 911 | if( urlData.passwd && urlData.user && urlData.passwd[0]=='#' ){ |
| 912 | char *zCredentials = mprintf("%s:%s", urlData.user, &urlData.passwd[1]); |
| 913 | char *zEncoded = encode64(zCredentials, -1); |
| 914 | blob_appendf(&hdr, "Authorization: Basic %s\r\n", zEncoded); |
| 915 | fossil_free(zEncoded); |
| 916 | fossil_free(zCredentials); |
| 917 | } |
| 918 | blob_appendf(&hdr, "Host: %s\r\n" |
| 919 | "User-Agent: %s\r\n", urlData.hostname, get_user_agent()); |
| 920 | if( zType[0]=='P' ){ |
| 921 | blob_appendf(&hdr, "Content-Type: application/x-www-form-urlencoded\r\n" |
| 922 | "Content-Length: %d\r\n\r\n", blob_size(&payload)); |
| 923 | }else{ |
| 924 | blob_appendf(&hdr, "\r\n"); |
| 925 | } |
| 926 | if( transport_open(&urlData) ){ |
| 927 | Th_ErrorMessage(interp, transport_errmsg(&urlData), 0, 0); |
| 928 | blob_reset(&hdr); |
| 929 | blob_reset(&payload); |
| 930 | return TH_ERROR; |
| 931 | } |
| 932 | transport_send(&urlData, &hdr); |
| 933 | transport_send(&urlData, &payload); |
| 934 | blob_reset(&hdr); |
| 935 | blob_reset(&payload); |
| 936 | transport_close(&urlData); |
| 937 | Th_SetResult(interp, 0, 0); /* NOTE: Asynchronous, no results. */ |
| 938 | return TH_OK; |
| 939 | }else{ |
| 940 | Th_ErrorMessage(interp, |
| 941 | "synchronous requests are not yet implemented", 0, 0); |
| 942 | blob_reset(&payload); |
| 943 | return TH_ERROR; |
| 944 | } |
| 945 | } |
| 946 | |
| 947 | /* |
| 948 | ** Make sure the interpreter has been initialized. Initialize it if |
| 949 | ** it has not been already. |
| 950 | ** |
| @@ -855,10 +969,11 @@ | |
| 969 | {"enable_output", enableOutputCmd, 0}, |
| 970 | {"hascap", hascapCmd, 0}, |
| 971 | {"hasfeature", hasfeatureCmd, 0}, |
| 972 | {"html", putsCmd, (void*)&aFlags[0]}, |
| 973 | {"htmlize", htmlizeCmd, 0}, |
| 974 | {"http", httpCmd, 0}, |
| 975 | {"linecount", linecntCmd, 0}, |
| 976 | {"puts", putsCmd, (void*)&aFlags[1]}, |
| 977 | {"query", queryCmd, 0}, |
| 978 | {"randhex", randhexCmd, 0}, |
| 979 | {"regexp", regexpCmd, 0}, |
| 980 |
+9
-4
| --- src/tkt.c | ||
| +++ src/tkt.c | ||
| @@ -513,15 +513,16 @@ | ||
| 513 | 513 | } |
| 514 | 514 | |
| 515 | 515 | /* |
| 516 | 516 | ** Write a ticket into the repository. |
| 517 | 517 | */ |
| 518 | -static void ticket_put( | |
| 518 | +static int ticket_put( | |
| 519 | 519 | Blob *pTicket, /* The text of the ticket change record */ |
| 520 | 520 | const char *zTktId, /* The ticket to which this change is applied */ |
| 521 | 521 | int needMod /* True if moderation is needed */ |
| 522 | 522 | ){ |
| 523 | + int result; | |
| 523 | 524 | int rid = content_put_ex(pTicket, 0, 0, 0, needMod); |
| 524 | 525 | if( rid==0 ){ |
| 525 | 526 | fossil_fatal("trouble committing ticket: %s", g.zErrMsg); |
| 526 | 527 | } |
| 527 | 528 | if( needMod ){ |
| @@ -533,13 +534,14 @@ | ||
| 533 | 534 | }else{ |
| 534 | 535 | db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d);", rid); |
| 535 | 536 | db_multi_exec("INSERT OR IGNORE INTO unclustered VALUES(%d);", rid); |
| 536 | 537 | } |
| 537 | 538 | manifest_crosslink_begin(); |
| 538 | - manifest_crosslink(rid, pTicket); | |
| 539 | + result = (manifest_crosslink(rid, pTicket, MC_PERMIT_HOOKS)==0); | |
| 539 | 540 | assert( blob_is_reset(pTicket) ); |
| 540 | 541 | manifest_crosslink_end(); |
| 542 | + return result; | |
| 541 | 543 | } |
| 542 | 544 | |
| 543 | 545 | /* |
| 544 | 546 | ** Subscript command: submit_ticket |
| 545 | 547 | ** |
| @@ -1344,11 +1346,14 @@ | ||
| 1344 | 1346 | } |
| 1345 | 1347 | blob_appendf(&tktchng, "K %s\n", zTktUuid); |
| 1346 | 1348 | blob_appendf(&tktchng, "U %F\n", zUser); |
| 1347 | 1349 | md5sum_blob(&tktchng, &cksum); |
| 1348 | 1350 | blob_appendf(&tktchng, "Z %b\n", &cksum); |
| 1349 | - ticket_put(&tktchng, zTktUuid, 0); | |
| 1350 | - printf("ticket %s succeeded for %s\n", | |
| 1351 | + if( ticket_put(&tktchng, zTktUuid, 0) ){ | |
| 1352 | + fossil_fatal("%s\n", g.zErrMsg); | |
| 1353 | + }else{ | |
| 1354 | + fossil_print("ticket %s succeeded for %s\n", | |
| 1351 | 1355 | (eCmd==set?"set":"add"),zTktUuid); |
| 1356 | + } | |
| 1352 | 1357 | } |
| 1353 | 1358 | } |
| 1354 | 1359 | } |
| 1355 | 1360 |
| --- src/tkt.c | |
| +++ src/tkt.c | |
| @@ -513,15 +513,16 @@ | |
| 513 | } |
| 514 | |
| 515 | /* |
| 516 | ** Write a ticket into the repository. |
| 517 | */ |
| 518 | static void ticket_put( |
| 519 | Blob *pTicket, /* The text of the ticket change record */ |
| 520 | const char *zTktId, /* The ticket to which this change is applied */ |
| 521 | int needMod /* True if moderation is needed */ |
| 522 | ){ |
| 523 | int rid = content_put_ex(pTicket, 0, 0, 0, needMod); |
| 524 | if( rid==0 ){ |
| 525 | fossil_fatal("trouble committing ticket: %s", g.zErrMsg); |
| 526 | } |
| 527 | if( needMod ){ |
| @@ -533,13 +534,14 @@ | |
| 533 | }else{ |
| 534 | db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d);", rid); |
| 535 | db_multi_exec("INSERT OR IGNORE INTO unclustered VALUES(%d);", rid); |
| 536 | } |
| 537 | manifest_crosslink_begin(); |
| 538 | manifest_crosslink(rid, pTicket); |
| 539 | assert( blob_is_reset(pTicket) ); |
| 540 | manifest_crosslink_end(); |
| 541 | } |
| 542 | |
| 543 | /* |
| 544 | ** Subscript command: submit_ticket |
| 545 | ** |
| @@ -1344,11 +1346,14 @@ | |
| 1344 | } |
| 1345 | blob_appendf(&tktchng, "K %s\n", zTktUuid); |
| 1346 | blob_appendf(&tktchng, "U %F\n", zUser); |
| 1347 | md5sum_blob(&tktchng, &cksum); |
| 1348 | blob_appendf(&tktchng, "Z %b\n", &cksum); |
| 1349 | ticket_put(&tktchng, zTktUuid, 0); |
| 1350 | printf("ticket %s succeeded for %s\n", |
| 1351 | (eCmd==set?"set":"add"),zTktUuid); |
| 1352 | } |
| 1353 | } |
| 1354 | } |
| 1355 |
| --- src/tkt.c | |
| +++ src/tkt.c | |
| @@ -513,15 +513,16 @@ | |
| 513 | } |
| 514 | |
| 515 | /* |
| 516 | ** Write a ticket into the repository. |
| 517 | */ |
| 518 | static int ticket_put( |
| 519 | Blob *pTicket, /* The text of the ticket change record */ |
| 520 | const char *zTktId, /* The ticket to which this change is applied */ |
| 521 | int needMod /* True if moderation is needed */ |
| 522 | ){ |
| 523 | int result; |
| 524 | int rid = content_put_ex(pTicket, 0, 0, 0, needMod); |
| 525 | if( rid==0 ){ |
| 526 | fossil_fatal("trouble committing ticket: %s", g.zErrMsg); |
| 527 | } |
| 528 | if( needMod ){ |
| @@ -533,13 +534,14 @@ | |
| 534 | }else{ |
| 535 | db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d);", rid); |
| 536 | db_multi_exec("INSERT OR IGNORE INTO unclustered VALUES(%d);", rid); |
| 537 | } |
| 538 | manifest_crosslink_begin(); |
| 539 | result = (manifest_crosslink(rid, pTicket, MC_PERMIT_HOOKS)==0); |
| 540 | assert( blob_is_reset(pTicket) ); |
| 541 | manifest_crosslink_end(); |
| 542 | return result; |
| 543 | } |
| 544 | |
| 545 | /* |
| 546 | ** Subscript command: submit_ticket |
| 547 | ** |
| @@ -1344,11 +1346,14 @@ | |
| 1346 | } |
| 1347 | blob_appendf(&tktchng, "K %s\n", zTktUuid); |
| 1348 | blob_appendf(&tktchng, "U %F\n", zUser); |
| 1349 | md5sum_blob(&tktchng, &cksum); |
| 1350 | blob_appendf(&tktchng, "Z %b\n", &cksum); |
| 1351 | if( ticket_put(&tktchng, zTktUuid, 0) ){ |
| 1352 | fossil_fatal("%s\n", g.zErrMsg); |
| 1353 | }else{ |
| 1354 | fossil_print("ticket %s succeeded for %s\n", |
| 1355 | (eCmd==set?"set":"add"),zTktUuid); |
| 1356 | } |
| 1357 | } |
| 1358 | } |
| 1359 | } |
| 1360 |
+244
-184
| --- src/url.c | ||
| +++ src/url.c | ||
| @@ -39,10 +39,34 @@ | ||
| 39 | 39 | #define URL_REMEMBER 0x002 /* Remember the url for later reuse */ |
| 40 | 40 | #define URL_ASK_REMEMBER_PW 0x004 /* Ask whether to remember prompted pw */ |
| 41 | 41 | #define URL_REMEMBER_PW 0x008 /* Should remember pw */ |
| 42 | 42 | #define URL_PROMPTED 0x010 /* Prompted for PW already */ |
| 43 | 43 | |
| 44 | +/* | |
| 45 | +** The URL related data used with this subsystem. | |
| 46 | +*/ | |
| 47 | +struct UrlData { | |
| 48 | + /* | |
| 49 | + ** NOTE: These members MUST be kept in sync with the related ones in the | |
| 50 | + ** "Global" structure defined in "main.c". | |
| 51 | + */ | |
| 52 | + int isFile; /* True if a "file:" url */ | |
| 53 | + int isHttps; /* True if a "https:" url */ | |
| 54 | + int isSsh; /* True if an "ssh:" url */ | |
| 55 | + char *name; /* Hostname for http: or filename for file: */ | |
| 56 | + char *hostname; /* The HOST: parameter on http headers */ | |
| 57 | + char *protocol; /* "http" or "https" */ | |
| 58 | + int port; /* TCP port number for http: or https: */ | |
| 59 | + int dfltPort; /* The default port for the given protocol */ | |
| 60 | + char *path; /* Pathname for http: */ | |
| 61 | + char *user; /* User id for http: */ | |
| 62 | + char *passwd; /* Password for http: */ | |
| 63 | + char *canonical; /* Canonical representation of the URL */ | |
| 64 | + char *proxyAuth; /* Proxy-Authorizer: string */ | |
| 65 | + char *fossil; /* The fossil query parameter on ssh: */ | |
| 66 | + unsigned flags; /* Boolean flags controlling URL processing */ | |
| 67 | +}; | |
| 44 | 68 | #endif /* INTERFACE */ |
| 45 | 69 | |
| 46 | 70 | |
| 47 | 71 | /* |
| 48 | 72 | ** Convert a string to lower-case. |
| @@ -51,10 +75,201 @@ | ||
| 51 | 75 | while( *z ){ |
| 52 | 76 | *z = fossil_tolower(*z); |
| 53 | 77 | z++; |
| 54 | 78 | } |
| 55 | 79 | } |
| 80 | + | |
| 81 | +/* | |
| 82 | +** Parse the given URL. Populate members of the provided UrlData structure | |
| 83 | +** as follows: | |
| 84 | +** | |
| 85 | +** isFile True if FILE: | |
| 86 | +** isHttps True if HTTPS: | |
| 87 | +** isSsh True if SSH: | |
| 88 | +** protocol "http" or "https" or "file" | |
| 89 | +** name Hostname for HTTP:, HTTPS:, SSH:. Filename for FILE: | |
| 90 | +** port TCP port number for HTTP or HTTPS. | |
| 91 | +** dfltPort Default TCP port number (80 or 443). | |
| 92 | +** path Path name for HTTP or HTTPS. | |
| 93 | +** user Userid. | |
| 94 | +** passwd Password. | |
| 95 | +** hostname HOST:PORT or just HOST if port is the default. | |
| 96 | +** canonical The URL in canonical form, omitting the password | |
| 97 | +** | |
| 98 | +*/ | |
| 99 | +void url_parse_local( | |
| 100 | + const char *zUrl, | |
| 101 | + unsigned int urlFlags, | |
| 102 | + UrlData *pUrlData | |
| 103 | +){ | |
| 104 | + int i, j, c; | |
| 105 | + char *zFile = 0; | |
| 106 | + | |
| 107 | + if( zUrl==0 ){ | |
| 108 | + zUrl = db_get("last-sync-url", 0); | |
| 109 | + if( zUrl==0 ) return; | |
| 110 | + if( pUrlData->passwd==0 ){ | |
| 111 | + pUrlData->passwd = unobscure(db_get("last-sync-pw", 0)); | |
| 112 | + } | |
| 113 | + } | |
| 114 | + | |
| 115 | + if( strncmp(zUrl, "http://", 7)==0 | |
| 116 | + || strncmp(zUrl, "https://", 8)==0 | |
| 117 | + || strncmp(zUrl, "ssh://", 6)==0 | |
| 118 | + ){ | |
| 119 | + int iStart; | |
| 120 | + char *zLogin; | |
| 121 | + char *zExe; | |
| 122 | + char cQuerySep = '?'; | |
| 123 | + | |
| 124 | + pUrlData->isFile = 0; | |
| 125 | + if( zUrl[4]=='s' ){ | |
| 126 | + pUrlData->isHttps = 1; | |
| 127 | + pUrlData->protocol = "https"; | |
| 128 | + pUrlData->dfltPort = 443; | |
| 129 | + iStart = 8; | |
| 130 | + }else if( zUrl[0]=='s' ){ | |
| 131 | + pUrlData->isSsh = 1; | |
| 132 | + pUrlData->protocol = "ssh"; | |
| 133 | + pUrlData->dfltPort = 22; | |
| 134 | + pUrlData->fossil = "fossil"; | |
| 135 | + iStart = 6; | |
| 136 | + }else{ | |
| 137 | + pUrlData->isHttps = 0; | |
| 138 | + pUrlData->protocol = "http"; | |
| 139 | + pUrlData->dfltPort = 80; | |
| 140 | + iStart = 7; | |
| 141 | + } | |
| 142 | + for(i=iStart; (c=zUrl[i])!=0 && c!='/' && c!='@'; i++){} | |
| 143 | + if( c=='@' ){ | |
| 144 | + /* Parse up the user-id and password */ | |
| 145 | + for(j=iStart; j<i && zUrl[j]!=':'; j++){} | |
| 146 | + pUrlData->user = mprintf("%.*s", j-iStart, &zUrl[iStart]); | |
| 147 | + dehttpize(pUrlData->user); | |
| 148 | + if( j<i ){ | |
| 149 | + if( ( urlFlags & URL_REMEMBER ) && pUrlData->isSsh==0 ){ | |
| 150 | + urlFlags |= URL_ASK_REMEMBER_PW; | |
| 151 | + } | |
| 152 | + pUrlData->passwd = mprintf("%.*s", i-j-1, &zUrl[j+1]); | |
| 153 | + dehttpize(pUrlData->passwd); | |
| 154 | + } | |
| 155 | + if( pUrlData->isSsh ){ | |
| 156 | + urlFlags &= ~URL_ASK_REMEMBER_PW; | |
| 157 | + } | |
| 158 | + zLogin = mprintf("%t@", pUrlData->user); | |
| 159 | + for(j=i+1; (c=zUrl[j])!=0 && c!='/' && c!=':'; j++){} | |
| 160 | + pUrlData->name = mprintf("%.*s", j-i-1, &zUrl[i+1]); | |
| 161 | + i = j; | |
| 162 | + }else{ | |
| 163 | + for(i=iStart; (c=zUrl[i])!=0 && c!='/' && c!=':'; i++){} | |
| 164 | + pUrlData->name = mprintf("%.*s", i-iStart, &zUrl[iStart]); | |
| 165 | + zLogin = mprintf(""); | |
| 166 | + } | |
| 167 | + url_tolower(pUrlData->name); | |
| 168 | + if( c==':' ){ | |
| 169 | + pUrlData->port = 0; | |
| 170 | + i++; | |
| 171 | + while( (c = zUrl[i])!=0 && fossil_isdigit(c) ){ | |
| 172 | + pUrlData->port = pUrlData->port*10 + c - '0'; | |
| 173 | + i++; | |
| 174 | + } | |
| 175 | + pUrlData->hostname = mprintf("%s:%d", pUrlData->name, pUrlData->port); | |
| 176 | + }else{ | |
| 177 | + pUrlData->port = pUrlData->dfltPort; | |
| 178 | + pUrlData->hostname = pUrlData->name; | |
| 179 | + } | |
| 180 | + dehttpize(pUrlData->name); | |
| 181 | + pUrlData->path = mprintf("%s", &zUrl[i]); | |
| 182 | + for(i=0; pUrlData->path[i] && pUrlData->path[i]!='?'; i++){} | |
| 183 | + if( pUrlData->path[i] ){ | |
| 184 | + pUrlData->path[i] = 0; | |
| 185 | + i++; | |
| 186 | + } | |
| 187 | + zExe = mprintf(""); | |
| 188 | + while( pUrlData->path[i]!=0 ){ | |
| 189 | + char *zName, *zValue; | |
| 190 | + zName = &pUrlData->path[i]; | |
| 191 | + zValue = zName; | |
| 192 | + while( pUrlData->path[i] && pUrlData->path[i]!='=' ){ i++; } | |
| 193 | + if( pUrlData->path[i]=='=' ){ | |
| 194 | + pUrlData->path[i] = 0; | |
| 195 | + i++; | |
| 196 | + zValue = &pUrlData->path[i]; | |
| 197 | + while( pUrlData->path[i] && pUrlData->path[i]!='&' ){ i++; } | |
| 198 | + } | |
| 199 | + if( pUrlData->path[i] ){ | |
| 200 | + pUrlData->path[i] = 0; | |
| 201 | + i++; | |
| 202 | + } | |
| 203 | + if( fossil_strcmp(zName,"fossil")==0 ){ | |
| 204 | + pUrlData->fossil = zValue; | |
| 205 | + dehttpize(pUrlData->fossil); | |
| 206 | + zExe = mprintf("%cfossil=%T", cQuerySep, pUrlData->fossil); | |
| 207 | + cQuerySep = '&'; | |
| 208 | + } | |
| 209 | + } | |
| 210 | + | |
| 211 | + dehttpize(pUrlData->path); | |
| 212 | + if( pUrlData->dfltPort==pUrlData->port ){ | |
| 213 | + pUrlData->canonical = mprintf( | |
| 214 | + "%s://%s%T%T%s", | |
| 215 | + pUrlData->protocol, zLogin, pUrlData->name, pUrlData->path, zExe | |
| 216 | + ); | |
| 217 | + }else{ | |
| 218 | + pUrlData->canonical = mprintf( | |
| 219 | + "%s://%s%T:%d%T%s", | |
| 220 | + pUrlData->protocol, zLogin, pUrlData->name, pUrlData->port, | |
| 221 | + pUrlData->path, zExe | |
| 222 | + ); | |
| 223 | + } | |
| 224 | + if( pUrlData->isSsh && pUrlData->path[1] ) pUrlData->path++; | |
| 225 | + free(zLogin); | |
| 226 | + }else if( strncmp(zUrl, "file:", 5)==0 ){ | |
| 227 | + pUrlData->isFile = 1; | |
| 228 | + if( zUrl[5]=='/' && zUrl[6]=='/' ){ | |
| 229 | + i = 7; | |
| 230 | + }else{ | |
| 231 | + i = 5; | |
| 232 | + } | |
| 233 | + zFile = mprintf("%s", &zUrl[i]); | |
| 234 | + }else if( file_isfile(zUrl) ){ | |
| 235 | + pUrlData->isFile = 1; | |
| 236 | + zFile = mprintf("%s", zUrl); | |
| 237 | + }else if( file_isdir(zUrl)==1 ){ | |
| 238 | + zFile = mprintf("%s/FOSSIL", zUrl); | |
| 239 | + if( file_isfile(zFile) ){ | |
| 240 | + pUrlData->isFile = 1; | |
| 241 | + }else{ | |
| 242 | + free(zFile); | |
| 243 | + fossil_fatal("unknown repository: %s", zUrl); | |
| 244 | + } | |
| 245 | + }else{ | |
| 246 | + fossil_fatal("unknown repository: %s", zUrl); | |
| 247 | + } | |
| 248 | + if( urlFlags ) pUrlData->flags = urlFlags; | |
| 249 | + if( pUrlData->isFile ){ | |
| 250 | + Blob cfile; | |
| 251 | + dehttpize(zFile); | |
| 252 | + file_canonical_name(zFile, &cfile, 0); | |
| 253 | + free(zFile); | |
| 254 | + pUrlData->protocol = "file"; | |
| 255 | + pUrlData->path = ""; | |
| 256 | + pUrlData->name = mprintf("%b", &cfile); | |
| 257 | + pUrlData->canonical = mprintf("file://%T", pUrlData->name); | |
| 258 | + blob_reset(&cfile); | |
| 259 | + }else if( pUrlData->user!=0 && pUrlData->passwd==0 && (urlFlags & URL_PROMPT_PW) ){ | |
| 260 | + url_prompt_for_password_local(pUrlData); | |
| 261 | + }else if( pUrlData->user!=0 && ( urlFlags & URL_ASK_REMEMBER_PW ) ){ | |
| 262 | + if( isatty(fileno(stdin)) ){ | |
| 263 | + if( save_password_prompt(pUrlData->passwd) ){ | |
| 264 | + pUrlData->flags = urlFlags |= URL_REMEMBER_PW; | |
| 265 | + }else{ | |
| 266 | + pUrlData->flags = urlFlags &= ~URL_REMEMBER_PW; | |
| 267 | + } | |
| 268 | + } | |
| 269 | + } | |
| 270 | +} | |
| 56 | 271 | |
| 57 | 272 | /* |
| 58 | 273 | ** Parse the given URL, which describes a sync server. Populate variables |
| 59 | 274 | ** in the global "g" structure as follows: |
| 60 | 275 | ** |
| @@ -79,175 +294,11 @@ | ||
| 79 | 294 | ** |
| 80 | 295 | ** ssh://userid@host:port/path?fossil=path/to/fossil.exe |
| 81 | 296 | ** |
| 82 | 297 | */ |
| 83 | 298 | void url_parse(const char *zUrl, unsigned int urlFlags){ |
| 84 | - int i, j, c; | |
| 85 | - char *zFile = 0; | |
| 86 | - | |
| 87 | - if( zUrl==0 ){ | |
| 88 | - zUrl = db_get("last-sync-url", 0); | |
| 89 | - if( zUrl==0 ) return; | |
| 90 | - if( g.urlPasswd==0 ){ | |
| 91 | - g.urlPasswd = unobscure(db_get("last-sync-pw", 0)); | |
| 92 | - } | |
| 93 | - } | |
| 94 | - | |
| 95 | - if( strncmp(zUrl, "http://", 7)==0 | |
| 96 | - || strncmp(zUrl, "https://", 8)==0 | |
| 97 | - || strncmp(zUrl, "ssh://", 6)==0 | |
| 98 | - ){ | |
| 99 | - int iStart; | |
| 100 | - char *zLogin; | |
| 101 | - char *zExe; | |
| 102 | - char cQuerySep = '?'; | |
| 103 | - | |
| 104 | - g.urlIsFile = 0; | |
| 105 | - if( zUrl[4]=='s' ){ | |
| 106 | - g.urlIsHttps = 1; | |
| 107 | - g.urlProtocol = "https"; | |
| 108 | - g.urlDfltPort = 443; | |
| 109 | - iStart = 8; | |
| 110 | - }else if( zUrl[0]=='s' ){ | |
| 111 | - g.urlIsSsh = 1; | |
| 112 | - g.urlProtocol = "ssh"; | |
| 113 | - g.urlDfltPort = 22; | |
| 114 | - g.urlFossil = "fossil"; | |
| 115 | - iStart = 6; | |
| 116 | - }else{ | |
| 117 | - g.urlIsHttps = 0; | |
| 118 | - g.urlProtocol = "http"; | |
| 119 | - g.urlDfltPort = 80; | |
| 120 | - iStart = 7; | |
| 121 | - } | |
| 122 | - for(i=iStart; (c=zUrl[i])!=0 && c!='/' && c!='@'; i++){} | |
| 123 | - if( c=='@' ){ | |
| 124 | - /* Parse up the user-id and password */ | |
| 125 | - for(j=iStart; j<i && zUrl[j]!=':'; j++){} | |
| 126 | - g.urlUser = mprintf("%.*s", j-iStart, &zUrl[iStart]); | |
| 127 | - dehttpize(g.urlUser); | |
| 128 | - if( j<i ){ | |
| 129 | - if( ( urlFlags & URL_REMEMBER ) && g.urlIsSsh==0 ){ | |
| 130 | - urlFlags |= URL_ASK_REMEMBER_PW; | |
| 131 | - } | |
| 132 | - g.urlPasswd = mprintf("%.*s", i-j-1, &zUrl[j+1]); | |
| 133 | - dehttpize(g.urlPasswd); | |
| 134 | - } | |
| 135 | - if( g.urlIsSsh ){ | |
| 136 | - urlFlags &= ~URL_ASK_REMEMBER_PW; | |
| 137 | - } | |
| 138 | - zLogin = mprintf("%t@", g.urlUser); | |
| 139 | - for(j=i+1; (c=zUrl[j])!=0 && c!='/' && c!=':'; j++){} | |
| 140 | - g.urlName = mprintf("%.*s", j-i-1, &zUrl[i+1]); | |
| 141 | - i = j; | |
| 142 | - }else{ | |
| 143 | - for(i=iStart; (c=zUrl[i])!=0 && c!='/' && c!=':'; i++){} | |
| 144 | - g.urlName = mprintf("%.*s", i-iStart, &zUrl[iStart]); | |
| 145 | - zLogin = mprintf(""); | |
| 146 | - } | |
| 147 | - url_tolower(g.urlName); | |
| 148 | - if( c==':' ){ | |
| 149 | - g.urlPort = 0; | |
| 150 | - i++; | |
| 151 | - while( (c = zUrl[i])!=0 && fossil_isdigit(c) ){ | |
| 152 | - g.urlPort = g.urlPort*10 + c - '0'; | |
| 153 | - i++; | |
| 154 | - } | |
| 155 | - g.urlHostname = mprintf("%s:%d", g.urlName, g.urlPort); | |
| 156 | - }else{ | |
| 157 | - g.urlPort = g.urlDfltPort; | |
| 158 | - g.urlHostname = g.urlName; | |
| 159 | - } | |
| 160 | - dehttpize(g.urlName); | |
| 161 | - g.urlPath = mprintf("%s", &zUrl[i]); | |
| 162 | - for(i=0; g.urlPath[i] && g.urlPath[i]!='?'; i++){} | |
| 163 | - if( g.urlPath[i] ){ | |
| 164 | - g.urlPath[i] = 0; | |
| 165 | - i++; | |
| 166 | - } | |
| 167 | - zExe = mprintf(""); | |
| 168 | - while( g.urlPath[i]!=0 ){ | |
| 169 | - char *zName, *zValue; | |
| 170 | - zName = &g.urlPath[i]; | |
| 171 | - zValue = zName; | |
| 172 | - while( g.urlPath[i] && g.urlPath[i]!='=' ){ i++; } | |
| 173 | - if( g.urlPath[i]=='=' ){ | |
| 174 | - g.urlPath[i] = 0; | |
| 175 | - i++; | |
| 176 | - zValue = &g.urlPath[i]; | |
| 177 | - while( g.urlPath[i] && g.urlPath[i]!='&' ){ i++; } | |
| 178 | - } | |
| 179 | - if( g.urlPath[i] ){ | |
| 180 | - g.urlPath[i] = 0; | |
| 181 | - i++; | |
| 182 | - } | |
| 183 | - if( fossil_strcmp(zName,"fossil")==0 ){ | |
| 184 | - g.urlFossil = zValue; | |
| 185 | - dehttpize(g.urlFossil); | |
| 186 | - zExe = mprintf("%cfossil=%T", cQuerySep, g.urlFossil); | |
| 187 | - cQuerySep = '&'; | |
| 188 | - } | |
| 189 | - } | |
| 190 | - | |
| 191 | - dehttpize(g.urlPath); | |
| 192 | - if( g.urlDfltPort==g.urlPort ){ | |
| 193 | - g.urlCanonical = mprintf( | |
| 194 | - "%s://%s%T%T%s", | |
| 195 | - g.urlProtocol, zLogin, g.urlName, g.urlPath, zExe | |
| 196 | - ); | |
| 197 | - }else{ | |
| 198 | - g.urlCanonical = mprintf( | |
| 199 | - "%s://%s%T:%d%T%s", | |
| 200 | - g.urlProtocol, zLogin, g.urlName, g.urlPort, g.urlPath, zExe | |
| 201 | - ); | |
| 202 | - } | |
| 203 | - if( g.urlIsSsh && g.urlPath[1] ) g.urlPath++; | |
| 204 | - free(zLogin); | |
| 205 | - }else if( strncmp(zUrl, "file:", 5)==0 ){ | |
| 206 | - g.urlIsFile = 1; | |
| 207 | - if( zUrl[5]=='/' && zUrl[6]=='/' ){ | |
| 208 | - i = 7; | |
| 209 | - }else{ | |
| 210 | - i = 5; | |
| 211 | - } | |
| 212 | - zFile = mprintf("%s", &zUrl[i]); | |
| 213 | - }else if( file_isfile(zUrl) ){ | |
| 214 | - g.urlIsFile = 1; | |
| 215 | - zFile = mprintf("%s", zUrl); | |
| 216 | - }else if( file_isdir(zUrl)==1 ){ | |
| 217 | - zFile = mprintf("%s/FOSSIL", zUrl); | |
| 218 | - if( file_isfile(zFile) ){ | |
| 219 | - g.urlIsFile = 1; | |
| 220 | - }else{ | |
| 221 | - free(zFile); | |
| 222 | - fossil_fatal("unknown repository: %s", zUrl); | |
| 223 | - } | |
| 224 | - }else{ | |
| 225 | - fossil_fatal("unknown repository: %s", zUrl); | |
| 226 | - } | |
| 227 | - if( urlFlags ) g.urlFlags = urlFlags; | |
| 228 | - if( g.urlIsFile ){ | |
| 229 | - Blob cfile; | |
| 230 | - dehttpize(zFile); | |
| 231 | - file_canonical_name(zFile, &cfile, 0); | |
| 232 | - free(zFile); | |
| 233 | - g.urlProtocol = "file"; | |
| 234 | - g.urlPath = ""; | |
| 235 | - g.urlName = mprintf("%b", &cfile); | |
| 236 | - g.urlCanonical = mprintf("file://%T", g.urlName); | |
| 237 | - blob_reset(&cfile); | |
| 238 | - }else if( g.urlUser!=0 && g.urlPasswd==0 && (urlFlags & URL_PROMPT_PW) ){ | |
| 239 | - url_prompt_for_password(); | |
| 240 | - }else if( g.urlUser!=0 && ( urlFlags & URL_ASK_REMEMBER_PW ) ){ | |
| 241 | - if( isatty(fileno(stdin)) ){ | |
| 242 | - if( save_password_prompt() ){ | |
| 243 | - g.urlFlags = urlFlags |= URL_REMEMBER_PW; | |
| 244 | - }else{ | |
| 245 | - g.urlFlags = urlFlags &= ~URL_REMEMBER_PW; | |
| 246 | - } | |
| 247 | - } | |
| 248 | - } | |
| 299 | + url_parse_local(zUrl, urlFlags, GLOBAL_URL()); | |
| 249 | 300 | } |
| 250 | 301 | |
| 251 | 302 | /* |
| 252 | 303 | ** COMMAND: test-urlparser |
| 253 | 304 | ** |
| @@ -439,35 +490,44 @@ | ||
| 439 | 490 | } |
| 440 | 491 | return blob_str(&p->url); |
| 441 | 492 | } |
| 442 | 493 | |
| 443 | 494 | /* |
| 444 | -** Prompt the user for the password for g.urlUser. Store the result | |
| 445 | -** in g.urlPasswd. | |
| 446 | -*/ | |
| 447 | -void url_prompt_for_password(void){ | |
| 448 | - if( g.urlIsSsh || g.urlIsFile ) return; | |
| 449 | - if( isatty(fileno(stdin)) | |
| 450 | - && (g.urlFlags & URL_PROMPT_PW)!=0 | |
| 451 | - && (g.urlFlags & URL_PROMPTED)==0 | |
| 452 | - ){ | |
| 453 | - g.urlFlags |= URL_PROMPTED; | |
| 454 | - g.urlPasswd = prompt_for_user_password(g.urlUser); | |
| 455 | - if( g.urlPasswd[0] | |
| 456 | - && (g.urlFlags & (URL_REMEMBER|URL_ASK_REMEMBER_PW))!=0 | |
| 457 | - ){ | |
| 458 | - if( save_password_prompt() ){ | |
| 459 | - g.urlFlags |= URL_REMEMBER_PW; | |
| 460 | - }else{ | |
| 461 | - g.urlFlags &= ~URL_REMEMBER_PW; | |
| 495 | +** Prompt the user for the password that corresponds to the "user" member of | |
| 496 | +** the provided UrlData structure. Store the result into the "passwd" member | |
| 497 | +** of the provided UrlData structure. | |
| 498 | +*/ | |
| 499 | +void url_prompt_for_password_local(UrlData *pUrlData){ | |
| 500 | + if( pUrlData->isSsh || pUrlData->isFile ) return; | |
| 501 | + if( isatty(fileno(stdin)) | |
| 502 | + && (pUrlData->flags & URL_PROMPT_PW)!=0 | |
| 503 | + && (pUrlData->flags & URL_PROMPTED)==0 | |
| 504 | + ){ | |
| 505 | + pUrlData->flags |= URL_PROMPTED; | |
| 506 | + pUrlData->passwd = prompt_for_user_password(pUrlData->user); | |
| 507 | + if( pUrlData->passwd[0] | |
| 508 | + && (pUrlData->flags & (URL_REMEMBER|URL_ASK_REMEMBER_PW))!=0 | |
| 509 | + ){ | |
| 510 | + if( save_password_prompt(pUrlData->passwd) ){ | |
| 511 | + pUrlData->flags |= URL_REMEMBER_PW; | |
| 512 | + }else{ | |
| 513 | + pUrlData->flags &= ~URL_REMEMBER_PW; | |
| 462 | 514 | } |
| 463 | 515 | } |
| 464 | 516 | }else{ |
| 465 | 517 | fossil_fatal("missing or incorrect password for user \"%s\"", |
| 466 | - g.urlUser); | |
| 518 | + pUrlData->user); | |
| 467 | 519 | } |
| 468 | 520 | } |
| 521 | + | |
| 522 | +/* | |
| 523 | +** Prompt the user for the password for g.urlUser. Store the result | |
| 524 | +** in g.urlPasswd. | |
| 525 | +*/ | |
| 526 | +void url_prompt_for_password(void){ | |
| 527 | + url_prompt_for_password_local(GLOBAL_URL()); | |
| 528 | +} | |
| 469 | 529 | |
| 470 | 530 | /* |
| 471 | 531 | ** Remember the URL and password if requested. |
| 472 | 532 | */ |
| 473 | 533 | void url_remember(void){ |
| 474 | 534 |
| --- src/url.c | |
| +++ src/url.c | |
| @@ -39,10 +39,34 @@ | |
| 39 | #define URL_REMEMBER 0x002 /* Remember the url for later reuse */ |
| 40 | #define URL_ASK_REMEMBER_PW 0x004 /* Ask whether to remember prompted pw */ |
| 41 | #define URL_REMEMBER_PW 0x008 /* Should remember pw */ |
| 42 | #define URL_PROMPTED 0x010 /* Prompted for PW already */ |
| 43 | |
| 44 | #endif /* INTERFACE */ |
| 45 | |
| 46 | |
| 47 | /* |
| 48 | ** Convert a string to lower-case. |
| @@ -51,10 +75,201 @@ | |
| 51 | while( *z ){ |
| 52 | *z = fossil_tolower(*z); |
| 53 | z++; |
| 54 | } |
| 55 | } |
| 56 | |
| 57 | /* |
| 58 | ** Parse the given URL, which describes a sync server. Populate variables |
| 59 | ** in the global "g" structure as follows: |
| 60 | ** |
| @@ -79,175 +294,11 @@ | |
| 79 | ** |
| 80 | ** ssh://userid@host:port/path?fossil=path/to/fossil.exe |
| 81 | ** |
| 82 | */ |
| 83 | void url_parse(const char *zUrl, unsigned int urlFlags){ |
| 84 | int i, j, c; |
| 85 | char *zFile = 0; |
| 86 | |
| 87 | if( zUrl==0 ){ |
| 88 | zUrl = db_get("last-sync-url", 0); |
| 89 | if( zUrl==0 ) return; |
| 90 | if( g.urlPasswd==0 ){ |
| 91 | g.urlPasswd = unobscure(db_get("last-sync-pw", 0)); |
| 92 | } |
| 93 | } |
| 94 | |
| 95 | if( strncmp(zUrl, "http://", 7)==0 |
| 96 | || strncmp(zUrl, "https://", 8)==0 |
| 97 | || strncmp(zUrl, "ssh://", 6)==0 |
| 98 | ){ |
| 99 | int iStart; |
| 100 | char *zLogin; |
| 101 | char *zExe; |
| 102 | char cQuerySep = '?'; |
| 103 | |
| 104 | g.urlIsFile = 0; |
| 105 | if( zUrl[4]=='s' ){ |
| 106 | g.urlIsHttps = 1; |
| 107 | g.urlProtocol = "https"; |
| 108 | g.urlDfltPort = 443; |
| 109 | iStart = 8; |
| 110 | }else if( zUrl[0]=='s' ){ |
| 111 | g.urlIsSsh = 1; |
| 112 | g.urlProtocol = "ssh"; |
| 113 | g.urlDfltPort = 22; |
| 114 | g.urlFossil = "fossil"; |
| 115 | iStart = 6; |
| 116 | }else{ |
| 117 | g.urlIsHttps = 0; |
| 118 | g.urlProtocol = "http"; |
| 119 | g.urlDfltPort = 80; |
| 120 | iStart = 7; |
| 121 | } |
| 122 | for(i=iStart; (c=zUrl[i])!=0 && c!='/' && c!='@'; i++){} |
| 123 | if( c=='@' ){ |
| 124 | /* Parse up the user-id and password */ |
| 125 | for(j=iStart; j<i && zUrl[j]!=':'; j++){} |
| 126 | g.urlUser = mprintf("%.*s", j-iStart, &zUrl[iStart]); |
| 127 | dehttpize(g.urlUser); |
| 128 | if( j<i ){ |
| 129 | if( ( urlFlags & URL_REMEMBER ) && g.urlIsSsh==0 ){ |
| 130 | urlFlags |= URL_ASK_REMEMBER_PW; |
| 131 | } |
| 132 | g.urlPasswd = mprintf("%.*s", i-j-1, &zUrl[j+1]); |
| 133 | dehttpize(g.urlPasswd); |
| 134 | } |
| 135 | if( g.urlIsSsh ){ |
| 136 | urlFlags &= ~URL_ASK_REMEMBER_PW; |
| 137 | } |
| 138 | zLogin = mprintf("%t@", g.urlUser); |
| 139 | for(j=i+1; (c=zUrl[j])!=0 && c!='/' && c!=':'; j++){} |
| 140 | g.urlName = mprintf("%.*s", j-i-1, &zUrl[i+1]); |
| 141 | i = j; |
| 142 | }else{ |
| 143 | for(i=iStart; (c=zUrl[i])!=0 && c!='/' && c!=':'; i++){} |
| 144 | g.urlName = mprintf("%.*s", i-iStart, &zUrl[iStart]); |
| 145 | zLogin = mprintf(""); |
| 146 | } |
| 147 | url_tolower(g.urlName); |
| 148 | if( c==':' ){ |
| 149 | g.urlPort = 0; |
| 150 | i++; |
| 151 | while( (c = zUrl[i])!=0 && fossil_isdigit(c) ){ |
| 152 | g.urlPort = g.urlPort*10 + c - '0'; |
| 153 | i++; |
| 154 | } |
| 155 | g.urlHostname = mprintf("%s:%d", g.urlName, g.urlPort); |
| 156 | }else{ |
| 157 | g.urlPort = g.urlDfltPort; |
| 158 | g.urlHostname = g.urlName; |
| 159 | } |
| 160 | dehttpize(g.urlName); |
| 161 | g.urlPath = mprintf("%s", &zUrl[i]); |
| 162 | for(i=0; g.urlPath[i] && g.urlPath[i]!='?'; i++){} |
| 163 | if( g.urlPath[i] ){ |
| 164 | g.urlPath[i] = 0; |
| 165 | i++; |
| 166 | } |
| 167 | zExe = mprintf(""); |
| 168 | while( g.urlPath[i]!=0 ){ |
| 169 | char *zName, *zValue; |
| 170 | zName = &g.urlPath[i]; |
| 171 | zValue = zName; |
| 172 | while( g.urlPath[i] && g.urlPath[i]!='=' ){ i++; } |
| 173 | if( g.urlPath[i]=='=' ){ |
| 174 | g.urlPath[i] = 0; |
| 175 | i++; |
| 176 | zValue = &g.urlPath[i]; |
| 177 | while( g.urlPath[i] && g.urlPath[i]!='&' ){ i++; } |
| 178 | } |
| 179 | if( g.urlPath[i] ){ |
| 180 | g.urlPath[i] = 0; |
| 181 | i++; |
| 182 | } |
| 183 | if( fossil_strcmp(zName,"fossil")==0 ){ |
| 184 | g.urlFossil = zValue; |
| 185 | dehttpize(g.urlFossil); |
| 186 | zExe = mprintf("%cfossil=%T", cQuerySep, g.urlFossil); |
| 187 | cQuerySep = '&'; |
| 188 | } |
| 189 | } |
| 190 | |
| 191 | dehttpize(g.urlPath); |
| 192 | if( g.urlDfltPort==g.urlPort ){ |
| 193 | g.urlCanonical = mprintf( |
| 194 | "%s://%s%T%T%s", |
| 195 | g.urlProtocol, zLogin, g.urlName, g.urlPath, zExe |
| 196 | ); |
| 197 | }else{ |
| 198 | g.urlCanonical = mprintf( |
| 199 | "%s://%s%T:%d%T%s", |
| 200 | g.urlProtocol, zLogin, g.urlName, g.urlPort, g.urlPath, zExe |
| 201 | ); |
| 202 | } |
| 203 | if( g.urlIsSsh && g.urlPath[1] ) g.urlPath++; |
| 204 | free(zLogin); |
| 205 | }else if( strncmp(zUrl, "file:", 5)==0 ){ |
| 206 | g.urlIsFile = 1; |
| 207 | if( zUrl[5]=='/' && zUrl[6]=='/' ){ |
| 208 | i = 7; |
| 209 | }else{ |
| 210 | i = 5; |
| 211 | } |
| 212 | zFile = mprintf("%s", &zUrl[i]); |
| 213 | }else if( file_isfile(zUrl) ){ |
| 214 | g.urlIsFile = 1; |
| 215 | zFile = mprintf("%s", zUrl); |
| 216 | }else if( file_isdir(zUrl)==1 ){ |
| 217 | zFile = mprintf("%s/FOSSIL", zUrl); |
| 218 | if( file_isfile(zFile) ){ |
| 219 | g.urlIsFile = 1; |
| 220 | }else{ |
| 221 | free(zFile); |
| 222 | fossil_fatal("unknown repository: %s", zUrl); |
| 223 | } |
| 224 | }else{ |
| 225 | fossil_fatal("unknown repository: %s", zUrl); |
| 226 | } |
| 227 | if( urlFlags ) g.urlFlags = urlFlags; |
| 228 | if( g.urlIsFile ){ |
| 229 | Blob cfile; |
| 230 | dehttpize(zFile); |
| 231 | file_canonical_name(zFile, &cfile, 0); |
| 232 | free(zFile); |
| 233 | g.urlProtocol = "file"; |
| 234 | g.urlPath = ""; |
| 235 | g.urlName = mprintf("%b", &cfile); |
| 236 | g.urlCanonical = mprintf("file://%T", g.urlName); |
| 237 | blob_reset(&cfile); |
| 238 | }else if( g.urlUser!=0 && g.urlPasswd==0 && (urlFlags & URL_PROMPT_PW) ){ |
| 239 | url_prompt_for_password(); |
| 240 | }else if( g.urlUser!=0 && ( urlFlags & URL_ASK_REMEMBER_PW ) ){ |
| 241 | if( isatty(fileno(stdin)) ){ |
| 242 | if( save_password_prompt() ){ |
| 243 | g.urlFlags = urlFlags |= URL_REMEMBER_PW; |
| 244 | }else{ |
| 245 | g.urlFlags = urlFlags &= ~URL_REMEMBER_PW; |
| 246 | } |
| 247 | } |
| 248 | } |
| 249 | } |
| 250 | |
| 251 | /* |
| 252 | ** COMMAND: test-urlparser |
| 253 | ** |
| @@ -439,35 +490,44 @@ | |
| 439 | } |
| 440 | return blob_str(&p->url); |
| 441 | } |
| 442 | |
| 443 | /* |
| 444 | ** Prompt the user for the password for g.urlUser. Store the result |
| 445 | ** in g.urlPasswd. |
| 446 | */ |
| 447 | void url_prompt_for_password(void){ |
| 448 | if( g.urlIsSsh || g.urlIsFile ) return; |
| 449 | if( isatty(fileno(stdin)) |
| 450 | && (g.urlFlags & URL_PROMPT_PW)!=0 |
| 451 | && (g.urlFlags & URL_PROMPTED)==0 |
| 452 | ){ |
| 453 | g.urlFlags |= URL_PROMPTED; |
| 454 | g.urlPasswd = prompt_for_user_password(g.urlUser); |
| 455 | if( g.urlPasswd[0] |
| 456 | && (g.urlFlags & (URL_REMEMBER|URL_ASK_REMEMBER_PW))!=0 |
| 457 | ){ |
| 458 | if( save_password_prompt() ){ |
| 459 | g.urlFlags |= URL_REMEMBER_PW; |
| 460 | }else{ |
| 461 | g.urlFlags &= ~URL_REMEMBER_PW; |
| 462 | } |
| 463 | } |
| 464 | }else{ |
| 465 | fossil_fatal("missing or incorrect password for user \"%s\"", |
| 466 | g.urlUser); |
| 467 | } |
| 468 | } |
| 469 | |
| 470 | /* |
| 471 | ** Remember the URL and password if requested. |
| 472 | */ |
| 473 | void url_remember(void){ |
| 474 |
| --- src/url.c | |
| +++ src/url.c | |
| @@ -39,10 +39,34 @@ | |
| 39 | #define URL_REMEMBER 0x002 /* Remember the url for later reuse */ |
| 40 | #define URL_ASK_REMEMBER_PW 0x004 /* Ask whether to remember prompted pw */ |
| 41 | #define URL_REMEMBER_PW 0x008 /* Should remember pw */ |
| 42 | #define URL_PROMPTED 0x010 /* Prompted for PW already */ |
| 43 | |
| 44 | /* |
| 45 | ** The URL related data used with this subsystem. |
| 46 | */ |
| 47 | struct UrlData { |
| 48 | /* |
| 49 | ** NOTE: These members MUST be kept in sync with the related ones in the |
| 50 | ** "Global" structure defined in "main.c". |
| 51 | */ |
| 52 | int isFile; /* True if a "file:" url */ |
| 53 | int isHttps; /* True if a "https:" url */ |
| 54 | int isSsh; /* True if an "ssh:" url */ |
| 55 | char *name; /* Hostname for http: or filename for file: */ |
| 56 | char *hostname; /* The HOST: parameter on http headers */ |
| 57 | char *protocol; /* "http" or "https" */ |
| 58 | int port; /* TCP port number for http: or https: */ |
| 59 | int dfltPort; /* The default port for the given protocol */ |
| 60 | char *path; /* Pathname for http: */ |
| 61 | char *user; /* User id for http: */ |
| 62 | char *passwd; /* Password for http: */ |
| 63 | char *canonical; /* Canonical representation of the URL */ |
| 64 | char *proxyAuth; /* Proxy-Authorizer: string */ |
| 65 | char *fossil; /* The fossil query parameter on ssh: */ |
| 66 | unsigned flags; /* Boolean flags controlling URL processing */ |
| 67 | }; |
| 68 | #endif /* INTERFACE */ |
| 69 | |
| 70 | |
| 71 | /* |
| 72 | ** Convert a string to lower-case. |
| @@ -51,10 +75,201 @@ | |
| 75 | while( *z ){ |
| 76 | *z = fossil_tolower(*z); |
| 77 | z++; |
| 78 | } |
| 79 | } |
| 80 | |
| 81 | /* |
| 82 | ** Parse the given URL. Populate members of the provided UrlData structure |
| 83 | ** as follows: |
| 84 | ** |
| 85 | ** isFile True if FILE: |
| 86 | ** isHttps True if HTTPS: |
| 87 | ** isSsh True if SSH: |
| 88 | ** protocol "http" or "https" or "file" |
| 89 | ** name Hostname for HTTP:, HTTPS:, SSH:. Filename for FILE: |
| 90 | ** port TCP port number for HTTP or HTTPS. |
| 91 | ** dfltPort Default TCP port number (80 or 443). |
| 92 | ** path Path name for HTTP or HTTPS. |
| 93 | ** user Userid. |
| 94 | ** passwd Password. |
| 95 | ** hostname HOST:PORT or just HOST if port is the default. |
| 96 | ** canonical The URL in canonical form, omitting the password |
| 97 | ** |
| 98 | */ |
| 99 | void url_parse_local( |
| 100 | const char *zUrl, |
| 101 | unsigned int urlFlags, |
| 102 | UrlData *pUrlData |
| 103 | ){ |
| 104 | int i, j, c; |
| 105 | char *zFile = 0; |
| 106 | |
| 107 | if( zUrl==0 ){ |
| 108 | zUrl = db_get("last-sync-url", 0); |
| 109 | if( zUrl==0 ) return; |
| 110 | if( pUrlData->passwd==0 ){ |
| 111 | pUrlData->passwd = unobscure(db_get("last-sync-pw", 0)); |
| 112 | } |
| 113 | } |
| 114 | |
| 115 | if( strncmp(zUrl, "http://", 7)==0 |
| 116 | || strncmp(zUrl, "https://", 8)==0 |
| 117 | || strncmp(zUrl, "ssh://", 6)==0 |
| 118 | ){ |
| 119 | int iStart; |
| 120 | char *zLogin; |
| 121 | char *zExe; |
| 122 | char cQuerySep = '?'; |
| 123 | |
| 124 | pUrlData->isFile = 0; |
| 125 | if( zUrl[4]=='s' ){ |
| 126 | pUrlData->isHttps = 1; |
| 127 | pUrlData->protocol = "https"; |
| 128 | pUrlData->dfltPort = 443; |
| 129 | iStart = 8; |
| 130 | }else if( zUrl[0]=='s' ){ |
| 131 | pUrlData->isSsh = 1; |
| 132 | pUrlData->protocol = "ssh"; |
| 133 | pUrlData->dfltPort = 22; |
| 134 | pUrlData->fossil = "fossil"; |
| 135 | iStart = 6; |
| 136 | }else{ |
| 137 | pUrlData->isHttps = 0; |
| 138 | pUrlData->protocol = "http"; |
| 139 | pUrlData->dfltPort = 80; |
| 140 | iStart = 7; |
| 141 | } |
| 142 | for(i=iStart; (c=zUrl[i])!=0 && c!='/' && c!='@'; i++){} |
| 143 | if( c=='@' ){ |
| 144 | /* Parse up the user-id and password */ |
| 145 | for(j=iStart; j<i && zUrl[j]!=':'; j++){} |
| 146 | pUrlData->user = mprintf("%.*s", j-iStart, &zUrl[iStart]); |
| 147 | dehttpize(pUrlData->user); |
| 148 | if( j<i ){ |
| 149 | if( ( urlFlags & URL_REMEMBER ) && pUrlData->isSsh==0 ){ |
| 150 | urlFlags |= URL_ASK_REMEMBER_PW; |
| 151 | } |
| 152 | pUrlData->passwd = mprintf("%.*s", i-j-1, &zUrl[j+1]); |
| 153 | dehttpize(pUrlData->passwd); |
| 154 | } |
| 155 | if( pUrlData->isSsh ){ |
| 156 | urlFlags &= ~URL_ASK_REMEMBER_PW; |
| 157 | } |
| 158 | zLogin = mprintf("%t@", pUrlData->user); |
| 159 | for(j=i+1; (c=zUrl[j])!=0 && c!='/' && c!=':'; j++){} |
| 160 | pUrlData->name = mprintf("%.*s", j-i-1, &zUrl[i+1]); |
| 161 | i = j; |
| 162 | }else{ |
| 163 | for(i=iStart; (c=zUrl[i])!=0 && c!='/' && c!=':'; i++){} |
| 164 | pUrlData->name = mprintf("%.*s", i-iStart, &zUrl[iStart]); |
| 165 | zLogin = mprintf(""); |
| 166 | } |
| 167 | url_tolower(pUrlData->name); |
| 168 | if( c==':' ){ |
| 169 | pUrlData->port = 0; |
| 170 | i++; |
| 171 | while( (c = zUrl[i])!=0 && fossil_isdigit(c) ){ |
| 172 | pUrlData->port = pUrlData->port*10 + c - '0'; |
| 173 | i++; |
| 174 | } |
| 175 | pUrlData->hostname = mprintf("%s:%d", pUrlData->name, pUrlData->port); |
| 176 | }else{ |
| 177 | pUrlData->port = pUrlData->dfltPort; |
| 178 | pUrlData->hostname = pUrlData->name; |
| 179 | } |
| 180 | dehttpize(pUrlData->name); |
| 181 | pUrlData->path = mprintf("%s", &zUrl[i]); |
| 182 | for(i=0; pUrlData->path[i] && pUrlData->path[i]!='?'; i++){} |
| 183 | if( pUrlData->path[i] ){ |
| 184 | pUrlData->path[i] = 0; |
| 185 | i++; |
| 186 | } |
| 187 | zExe = mprintf(""); |
| 188 | while( pUrlData->path[i]!=0 ){ |
| 189 | char *zName, *zValue; |
| 190 | zName = &pUrlData->path[i]; |
| 191 | zValue = zName; |
| 192 | while( pUrlData->path[i] && pUrlData->path[i]!='=' ){ i++; } |
| 193 | if( pUrlData->path[i]=='=' ){ |
| 194 | pUrlData->path[i] = 0; |
| 195 | i++; |
| 196 | zValue = &pUrlData->path[i]; |
| 197 | while( pUrlData->path[i] && pUrlData->path[i]!='&' ){ i++; } |
| 198 | } |
| 199 | if( pUrlData->path[i] ){ |
| 200 | pUrlData->path[i] = 0; |
| 201 | i++; |
| 202 | } |
| 203 | if( fossil_strcmp(zName,"fossil")==0 ){ |
| 204 | pUrlData->fossil = zValue; |
| 205 | dehttpize(pUrlData->fossil); |
| 206 | zExe = mprintf("%cfossil=%T", cQuerySep, pUrlData->fossil); |
| 207 | cQuerySep = '&'; |
| 208 | } |
| 209 | } |
| 210 | |
| 211 | dehttpize(pUrlData->path); |
| 212 | if( pUrlData->dfltPort==pUrlData->port ){ |
| 213 | pUrlData->canonical = mprintf( |
| 214 | "%s://%s%T%T%s", |
| 215 | pUrlData->protocol, zLogin, pUrlData->name, pUrlData->path, zExe |
| 216 | ); |
| 217 | }else{ |
| 218 | pUrlData->canonical = mprintf( |
| 219 | "%s://%s%T:%d%T%s", |
| 220 | pUrlData->protocol, zLogin, pUrlData->name, pUrlData->port, |
| 221 | pUrlData->path, zExe |
| 222 | ); |
| 223 | } |
| 224 | if( pUrlData->isSsh && pUrlData->path[1] ) pUrlData->path++; |
| 225 | free(zLogin); |
| 226 | }else if( strncmp(zUrl, "file:", 5)==0 ){ |
| 227 | pUrlData->isFile = 1; |
| 228 | if( zUrl[5]=='/' && zUrl[6]=='/' ){ |
| 229 | i = 7; |
| 230 | }else{ |
| 231 | i = 5; |
| 232 | } |
| 233 | zFile = mprintf("%s", &zUrl[i]); |
| 234 | }else if( file_isfile(zUrl) ){ |
| 235 | pUrlData->isFile = 1; |
| 236 | zFile = mprintf("%s", zUrl); |
| 237 | }else if( file_isdir(zUrl)==1 ){ |
| 238 | zFile = mprintf("%s/FOSSIL", zUrl); |
| 239 | if( file_isfile(zFile) ){ |
| 240 | pUrlData->isFile = 1; |
| 241 | }else{ |
| 242 | free(zFile); |
| 243 | fossil_fatal("unknown repository: %s", zUrl); |
| 244 | } |
| 245 | }else{ |
| 246 | fossil_fatal("unknown repository: %s", zUrl); |
| 247 | } |
| 248 | if( urlFlags ) pUrlData->flags = urlFlags; |
| 249 | if( pUrlData->isFile ){ |
| 250 | Blob cfile; |
| 251 | dehttpize(zFile); |
| 252 | file_canonical_name(zFile, &cfile, 0); |
| 253 | free(zFile); |
| 254 | pUrlData->protocol = "file"; |
| 255 | pUrlData->path = ""; |
| 256 | pUrlData->name = mprintf("%b", &cfile); |
| 257 | pUrlData->canonical = mprintf("file://%T", pUrlData->name); |
| 258 | blob_reset(&cfile); |
| 259 | }else if( pUrlData->user!=0 && pUrlData->passwd==0 && (urlFlags & URL_PROMPT_PW) ){ |
| 260 | url_prompt_for_password_local(pUrlData); |
| 261 | }else if( pUrlData->user!=0 && ( urlFlags & URL_ASK_REMEMBER_PW ) ){ |
| 262 | if( isatty(fileno(stdin)) ){ |
| 263 | if( save_password_prompt(pUrlData->passwd) ){ |
| 264 | pUrlData->flags = urlFlags |= URL_REMEMBER_PW; |
| 265 | }else{ |
| 266 | pUrlData->flags = urlFlags &= ~URL_REMEMBER_PW; |
| 267 | } |
| 268 | } |
| 269 | } |
| 270 | } |
| 271 | |
| 272 | /* |
| 273 | ** Parse the given URL, which describes a sync server. Populate variables |
| 274 | ** in the global "g" structure as follows: |
| 275 | ** |
| @@ -79,175 +294,11 @@ | |
| 294 | ** |
| 295 | ** ssh://userid@host:port/path?fossil=path/to/fossil.exe |
| 296 | ** |
| 297 | */ |
| 298 | void url_parse(const char *zUrl, unsigned int urlFlags){ |
| 299 | url_parse_local(zUrl, urlFlags, GLOBAL_URL()); |
| 300 | } |
| 301 | |
| 302 | /* |
| 303 | ** COMMAND: test-urlparser |
| 304 | ** |
| @@ -439,35 +490,44 @@ | |
| 490 | } |
| 491 | return blob_str(&p->url); |
| 492 | } |
| 493 | |
| 494 | /* |
| 495 | ** Prompt the user for the password that corresponds to the "user" member of |
| 496 | ** the provided UrlData structure. Store the result into the "passwd" member |
| 497 | ** of the provided UrlData structure. |
| 498 | */ |
| 499 | void url_prompt_for_password_local(UrlData *pUrlData){ |
| 500 | if( pUrlData->isSsh || pUrlData->isFile ) return; |
| 501 | if( isatty(fileno(stdin)) |
| 502 | && (pUrlData->flags & URL_PROMPT_PW)!=0 |
| 503 | && (pUrlData->flags & URL_PROMPTED)==0 |
| 504 | ){ |
| 505 | pUrlData->flags |= URL_PROMPTED; |
| 506 | pUrlData->passwd = prompt_for_user_password(pUrlData->user); |
| 507 | if( pUrlData->passwd[0] |
| 508 | && (pUrlData->flags & (URL_REMEMBER|URL_ASK_REMEMBER_PW))!=0 |
| 509 | ){ |
| 510 | if( save_password_prompt(pUrlData->passwd) ){ |
| 511 | pUrlData->flags |= URL_REMEMBER_PW; |
| 512 | }else{ |
| 513 | pUrlData->flags &= ~URL_REMEMBER_PW; |
| 514 | } |
| 515 | } |
| 516 | }else{ |
| 517 | fossil_fatal("missing or incorrect password for user \"%s\"", |
| 518 | pUrlData->user); |
| 519 | } |
| 520 | } |
| 521 | |
| 522 | /* |
| 523 | ** Prompt the user for the password for g.urlUser. Store the result |
| 524 | ** in g.urlPasswd. |
| 525 | */ |
| 526 | void url_prompt_for_password(void){ |
| 527 | url_prompt_for_password_local(GLOBAL_URL()); |
| 528 | } |
| 529 | |
| 530 | /* |
| 531 | ** Remember the URL and password if requested. |
| 532 | */ |
| 533 | void url_remember(void){ |
| 534 |
+2
-2
| --- src/user.c | ||
| +++ src/user.c | ||
| @@ -132,15 +132,15 @@ | ||
| 132 | 132 | } |
| 133 | 133 | |
| 134 | 134 | /* |
| 135 | 135 | ** Prompt to save Fossil user password |
| 136 | 136 | */ |
| 137 | -int save_password_prompt(){ | |
| 137 | +int save_password_prompt(const char *passwd){ | |
| 138 | 138 | Blob x; |
| 139 | 139 | char c; |
| 140 | 140 | const char *old = db_get("last-sync-pw", 0); |
| 141 | - if( (old!=0) && fossil_strcmp(unobscure(old), g.urlPasswd)==0 ){ | |
| 141 | + if( (old!=0) && fossil_strcmp(unobscure(old), passwd)==0 ){ | |
| 142 | 142 | return 0; |
| 143 | 143 | } |
| 144 | 144 | prompt_user("remember password (Y/n)? ", &x); |
| 145 | 145 | c = blob_str(&x)[0]; |
| 146 | 146 | blob_reset(&x); |
| 147 | 147 |
| --- src/user.c | |
| +++ src/user.c | |
| @@ -132,15 +132,15 @@ | |
| 132 | } |
| 133 | |
| 134 | /* |
| 135 | ** Prompt to save Fossil user password |
| 136 | */ |
| 137 | int save_password_prompt(){ |
| 138 | Blob x; |
| 139 | char c; |
| 140 | const char *old = db_get("last-sync-pw", 0); |
| 141 | if( (old!=0) && fossil_strcmp(unobscure(old), g.urlPasswd)==0 ){ |
| 142 | return 0; |
| 143 | } |
| 144 | prompt_user("remember password (Y/n)? ", &x); |
| 145 | c = blob_str(&x)[0]; |
| 146 | blob_reset(&x); |
| 147 |
| --- src/user.c | |
| +++ src/user.c | |
| @@ -132,15 +132,15 @@ | |
| 132 | } |
| 133 | |
| 134 | /* |
| 135 | ** Prompt to save Fossil user password |
| 136 | */ |
| 137 | int save_password_prompt(const char *passwd){ |
| 138 | Blob x; |
| 139 | char c; |
| 140 | const char *old = db_get("last-sync-pw", 0); |
| 141 | if( (old!=0) && fossil_strcmp(unobscure(old), passwd)==0 ){ |
| 142 | return 0; |
| 143 | } |
| 144 | prompt_user("remember password (Y/n)? ", &x); |
| 145 | c = blob_str(&x)[0]; |
| 146 | blob_reset(&x); |
| 147 |
+1
-1
| --- src/wiki.c | ||
| +++ src/wiki.c | ||
| @@ -294,11 +294,11 @@ | ||
| 294 | 294 | moderation_table_create(); |
| 295 | 295 | db_multi_exec("INSERT INTO modreq(objid) VALUES(%d)", nrid); |
| 296 | 296 | } |
| 297 | 297 | db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nrid); |
| 298 | 298 | db_multi_exec("INSERT OR IGNORE INTO unclustered VALUES(%d);", nrid); |
| 299 | - manifest_crosslink(nrid, pWiki); | |
| 299 | + manifest_crosslink(nrid, pWiki, MC_NONE); | |
| 300 | 300 | } |
| 301 | 301 | |
| 302 | 302 | /* |
| 303 | 303 | ** Formal names and common names for the various wiki styles. |
| 304 | 304 | */ |
| 305 | 305 |
| --- src/wiki.c | |
| +++ src/wiki.c | |
| @@ -294,11 +294,11 @@ | |
| 294 | moderation_table_create(); |
| 295 | db_multi_exec("INSERT INTO modreq(objid) VALUES(%d)", nrid); |
| 296 | } |
| 297 | db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nrid); |
| 298 | db_multi_exec("INSERT OR IGNORE INTO unclustered VALUES(%d);", nrid); |
| 299 | manifest_crosslink(nrid, pWiki); |
| 300 | } |
| 301 | |
| 302 | /* |
| 303 | ** Formal names and common names for the various wiki styles. |
| 304 | */ |
| 305 |
| --- src/wiki.c | |
| +++ src/wiki.c | |
| @@ -294,11 +294,11 @@ | |
| 294 | moderation_table_create(); |
| 295 | db_multi_exec("INSERT INTO modreq(objid) VALUES(%d)", nrid); |
| 296 | } |
| 297 | db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nrid); |
| 298 | db_multi_exec("INSERT OR IGNORE INTO unclustered VALUES(%d);", nrid); |
| 299 | manifest_crosslink(nrid, pWiki, MC_NONE); |
| 300 | } |
| 301 | |
| 302 | /* |
| 303 | ** Formal names and common names for the various wiki styles. |
| 304 | */ |
| 305 |
+77
-32
| --- src/xfer.c | ||
| +++ src/xfer.c | ||
| @@ -191,11 +191,11 @@ | ||
| 191 | 191 | if( rid==0 ){ |
| 192 | 192 | blob_appendf(&pXfer->err, "%s", g.zErrMsg); |
| 193 | 193 | blob_reset(&content); |
| 194 | 194 | }else{ |
| 195 | 195 | if( !isPriv ) content_make_public(rid); |
| 196 | - manifest_crosslink(rid, &content); | |
| 196 | + manifest_crosslink(rid, &content, MC_NONE); | |
| 197 | 197 | } |
| 198 | 198 | assert( blob_is_reset(&content) ); |
| 199 | 199 | remote_has(rid); |
| 200 | 200 | } |
| 201 | 201 | |
| @@ -820,33 +820,73 @@ | ||
| 820 | 820 | static void server_private_xfer_not_authorized(void){ |
| 821 | 821 | @ error not\sauthorized\sto\ssync\sprivate\scontent |
| 822 | 822 | } |
| 823 | 823 | |
| 824 | 824 | /* |
| 825 | -** Run the specified TH1 script, if any, and returns the return code or TH_OK | |
| 826 | -** when there is no script. | |
| 827 | -*/ | |
| 828 | -static int run_script(const char *zScript){ | |
| 829 | - if( !zScript ){ | |
| 830 | - return TH_OK; /* No script, return success. */ | |
| 831 | - } | |
| 832 | - Th_FossilInit(TH_INIT_DEFAULT); /* Make sure TH1 is ready. */ | |
| 833 | - return Th_Eval(g.interp, 0, zScript, -1); | |
| 834 | -} | |
| 835 | - | |
| 836 | -/* | |
| 837 | -** Run the pre-transfer TH1 script, if any, and returns the return code. | |
| 838 | -*/ | |
| 839 | -static int run_common_script(void){ | |
| 840 | - return run_script(db_get("xfer-common-script", 0)); | |
| 841 | -} | |
| 842 | - | |
| 843 | -/* | |
| 844 | -** Run the post-push TH1 script, if any, and returns the return code. | |
| 845 | -*/ | |
| 846 | -static int run_push_script(void){ | |
| 847 | - return run_script(db_get("xfer-push-script", 0)); | |
| 825 | +** Return the common TH1 code to evaluate prior to evaluating any other | |
| 826 | +** TH1 transfer notification scripts. | |
| 827 | +*/ | |
| 828 | +const char *xfer_common_code(void){ | |
| 829 | + return db_get("xfer-common-script", 0); | |
| 830 | +} | |
| 831 | + | |
| 832 | +/* | |
| 833 | +** Return the TH1 code to evaluate when a push is processed. | |
| 834 | +*/ | |
| 835 | +const char *xfer_push_code(void){ | |
| 836 | + return db_get("xfer-push-script", 0); | |
| 837 | +} | |
| 838 | + | |
| 839 | +/* | |
| 840 | +** Return the TH1 code to evaluate when a commit is processed. | |
| 841 | +*/ | |
| 842 | +const char *xfer_commit_code(void){ | |
| 843 | + return db_get("xfer-commit-script", 0); | |
| 844 | +} | |
| 845 | + | |
| 846 | +/* | |
| 847 | +** Return the TH1 code to evaluate when a ticket change is processed. | |
| 848 | +*/ | |
| 849 | +const char *xfer_ticket_code(void){ | |
| 850 | + return db_get("xfer-ticket-script", 0); | |
| 851 | +} | |
| 852 | + | |
| 853 | +/* | |
| 854 | +** Run the specified TH1 script, if any, and returns 1 on error. | |
| 855 | +*/ | |
| 856 | +int xfer_run_script(const char *zScript, const char *zUuid){ | |
| 857 | + int result; | |
| 858 | + if( !zScript ) return TH_OK; | |
| 859 | + Th_FossilInit(TH_INIT_DEFAULT); | |
| 860 | + if( zUuid ){ | |
| 861 | + result = Th_SetVar(g.interp, "uuid", -1, zUuid, -1); | |
| 862 | + if( result!=TH_OK ){ | |
| 863 | + fossil_error(1, "%s", Th_GetResult(g.interp, 0)); | |
| 864 | + return result; | |
| 865 | + } | |
| 866 | + } | |
| 867 | + result = Th_Eval(g.interp, 0, zScript, -1); | |
| 868 | + if( result!=TH_OK ){ | |
| 869 | + fossil_error(1, "%s", Th_GetResult(g.interp, 0)); | |
| 870 | + } | |
| 871 | + return result; | |
| 872 | +} | |
| 873 | + | |
| 874 | +/* | |
| 875 | +** Runs the pre-transfer TH1 script, if any, and returns its return code. | |
| 876 | +** This script may be run multiple times. If the script performs actions | |
| 877 | +** that cannot be redone, it should use an internal [if] guard similar to | |
| 878 | +** the following: | |
| 879 | +** | |
| 880 | +** if {![info exists common_done]} { | |
| 881 | +** # ... code here | |
| 882 | +** set common_done 1 | |
| 883 | +** } | |
| 884 | +*/ | |
| 885 | +int xfer_run_common_script(void){ | |
| 886 | + Th_FossilInit(TH_INIT_DEFAULT); | |
| 887 | + return xfer_run_script(xfer_common_code(), 0); | |
| 848 | 888 | } |
| 849 | 889 | |
| 850 | 890 | /* |
| 851 | 891 | ** If this variable is set, disable login checks. Used for debugging |
| 852 | 892 | ** only. |
| @@ -875,10 +915,11 @@ | ||
| 875 | 915 | int isClone = 0; |
| 876 | 916 | int nGimme = 0; |
| 877 | 917 | int size; |
| 878 | 918 | int recvConfig = 0; |
| 879 | 919 | char *zNow; |
| 920 | + int result; | |
| 880 | 921 | |
| 881 | 922 | if( fossil_strcmp(PD("REQUEST_METHOD","POST"),"POST") ){ |
| 882 | 923 | fossil_redirect_home(); |
| 883 | 924 | } |
| 884 | 925 | g.zLogin = "anonymous"; |
| @@ -904,13 +945,14 @@ | ||
| 904 | 945 | db_begin_transaction(); |
| 905 | 946 | db_multi_exec( |
| 906 | 947 | "CREATE TEMP TABLE onremote(rid INTEGER PRIMARY KEY);" |
| 907 | 948 | ); |
| 908 | 949 | manifest_crosslink_begin(); |
| 909 | - if( run_common_script()==TH_ERROR ){ | |
| 950 | + result = xfer_run_common_script(); | |
| 951 | + if( result==TH_ERROR ){ | |
| 910 | 952 | cgi_reset_content(); |
| 911 | - @ error common\sscript\sfailed:\s%F(Th_GetResult(g.interp, 0)) | |
| 953 | + @ error common\sscript\sfailed:\s%F(g.zErrMsg) | |
| 912 | 954 | nErr++; |
| 913 | 955 | } |
| 914 | 956 | while( blob_line(xfer.pIn, &xfer.line) ){ |
| 915 | 957 | if( blob_buffer(&xfer.line)[0]=='#' ) continue; |
| 916 | 958 | if( blob_size(&xfer.line)==0 ) continue; |
| @@ -1231,14 +1273,17 @@ | ||
| 1231 | 1273 | } |
| 1232 | 1274 | blobarray_reset(xfer.aToken, xfer.nToken); |
| 1233 | 1275 | blob_reset(&xfer.line); |
| 1234 | 1276 | } |
| 1235 | 1277 | if( isPush ){ |
| 1236 | - if( run_push_script()==TH_ERROR ){ | |
| 1237 | - cgi_reset_content(); | |
| 1238 | - @ error push\sscript\sfailed:\s%F(Th_GetResult(g.interp, 0)) | |
| 1239 | - nErr++; | |
| 1278 | + if( result==TH_OK ){ | |
| 1279 | + result = xfer_run_script(xfer_push_code(), 0); | |
| 1280 | + if( result==TH_ERROR ){ | |
| 1281 | + cgi_reset_content(); | |
| 1282 | + @ error push\sscript\sfailed:\s%F(g.zErrMsg) | |
| 1283 | + nErr++; | |
| 1284 | + } | |
| 1240 | 1285 | } |
| 1241 | 1286 | request_phantoms(&xfer, 500); |
| 1242 | 1287 | } |
| 1243 | 1288 | if( isClone && nGimme==0 ){ |
| 1244 | 1289 | /* The initial "clone" message from client to server contains no |
| @@ -1881,13 +1926,13 @@ | ||
| 1881 | 1926 | |
| 1882 | 1927 | fossil_force_newline(); |
| 1883 | 1928 | fossil_print( |
| 1884 | 1929 | "%s finished with %lld bytes sent, %lld bytes received\n", |
| 1885 | 1930 | zOpType, nSent, nRcvd); |
| 1886 | - transport_close(); | |
| 1887 | - transport_global_shutdown(); | |
| 1931 | + transport_close(GLOBAL_URL()); | |
| 1932 | + transport_global_shutdown(GLOBAL_URL()); | |
| 1888 | 1933 | db_multi_exec("DROP TABLE onremote"); |
| 1889 | 1934 | manifest_crosslink_end(); |
| 1890 | 1935 | content_enable_dephantomize(1); |
| 1891 | 1936 | db_end_transaction(0); |
| 1892 | 1937 | return nErr; |
| 1893 | 1938 | } |
| 1894 | 1939 |
| --- src/xfer.c | |
| +++ src/xfer.c | |
| @@ -191,11 +191,11 @@ | |
| 191 | if( rid==0 ){ |
| 192 | blob_appendf(&pXfer->err, "%s", g.zErrMsg); |
| 193 | blob_reset(&content); |
| 194 | }else{ |
| 195 | if( !isPriv ) content_make_public(rid); |
| 196 | manifest_crosslink(rid, &content); |
| 197 | } |
| 198 | assert( blob_is_reset(&content) ); |
| 199 | remote_has(rid); |
| 200 | } |
| 201 | |
| @@ -820,33 +820,73 @@ | |
| 820 | static void server_private_xfer_not_authorized(void){ |
| 821 | @ error not\sauthorized\sto\ssync\sprivate\scontent |
| 822 | } |
| 823 | |
| 824 | /* |
| 825 | ** Run the specified TH1 script, if any, and returns the return code or TH_OK |
| 826 | ** when there is no script. |
| 827 | */ |
| 828 | static int run_script(const char *zScript){ |
| 829 | if( !zScript ){ |
| 830 | return TH_OK; /* No script, return success. */ |
| 831 | } |
| 832 | Th_FossilInit(TH_INIT_DEFAULT); /* Make sure TH1 is ready. */ |
| 833 | return Th_Eval(g.interp, 0, zScript, -1); |
| 834 | } |
| 835 | |
| 836 | /* |
| 837 | ** Run the pre-transfer TH1 script, if any, and returns the return code. |
| 838 | */ |
| 839 | static int run_common_script(void){ |
| 840 | return run_script(db_get("xfer-common-script", 0)); |
| 841 | } |
| 842 | |
| 843 | /* |
| 844 | ** Run the post-push TH1 script, if any, and returns the return code. |
| 845 | */ |
| 846 | static int run_push_script(void){ |
| 847 | return run_script(db_get("xfer-push-script", 0)); |
| 848 | } |
| 849 | |
| 850 | /* |
| 851 | ** If this variable is set, disable login checks. Used for debugging |
| 852 | ** only. |
| @@ -875,10 +915,11 @@ | |
| 875 | int isClone = 0; |
| 876 | int nGimme = 0; |
| 877 | int size; |
| 878 | int recvConfig = 0; |
| 879 | char *zNow; |
| 880 | |
| 881 | if( fossil_strcmp(PD("REQUEST_METHOD","POST"),"POST") ){ |
| 882 | fossil_redirect_home(); |
| 883 | } |
| 884 | g.zLogin = "anonymous"; |
| @@ -904,13 +945,14 @@ | |
| 904 | db_begin_transaction(); |
| 905 | db_multi_exec( |
| 906 | "CREATE TEMP TABLE onremote(rid INTEGER PRIMARY KEY);" |
| 907 | ); |
| 908 | manifest_crosslink_begin(); |
| 909 | if( run_common_script()==TH_ERROR ){ |
| 910 | cgi_reset_content(); |
| 911 | @ error common\sscript\sfailed:\s%F(Th_GetResult(g.interp, 0)) |
| 912 | nErr++; |
| 913 | } |
| 914 | while( blob_line(xfer.pIn, &xfer.line) ){ |
| 915 | if( blob_buffer(&xfer.line)[0]=='#' ) continue; |
| 916 | if( blob_size(&xfer.line)==0 ) continue; |
| @@ -1231,14 +1273,17 @@ | |
| 1231 | } |
| 1232 | blobarray_reset(xfer.aToken, xfer.nToken); |
| 1233 | blob_reset(&xfer.line); |
| 1234 | } |
| 1235 | if( isPush ){ |
| 1236 | if( run_push_script()==TH_ERROR ){ |
| 1237 | cgi_reset_content(); |
| 1238 | @ error push\sscript\sfailed:\s%F(Th_GetResult(g.interp, 0)) |
| 1239 | nErr++; |
| 1240 | } |
| 1241 | request_phantoms(&xfer, 500); |
| 1242 | } |
| 1243 | if( isClone && nGimme==0 ){ |
| 1244 | /* The initial "clone" message from client to server contains no |
| @@ -1881,13 +1926,13 @@ | |
| 1881 | |
| 1882 | fossil_force_newline(); |
| 1883 | fossil_print( |
| 1884 | "%s finished with %lld bytes sent, %lld bytes received\n", |
| 1885 | zOpType, nSent, nRcvd); |
| 1886 | transport_close(); |
| 1887 | transport_global_shutdown(); |
| 1888 | db_multi_exec("DROP TABLE onremote"); |
| 1889 | manifest_crosslink_end(); |
| 1890 | content_enable_dephantomize(1); |
| 1891 | db_end_transaction(0); |
| 1892 | return nErr; |
| 1893 | } |
| 1894 |
| --- src/xfer.c | |
| +++ src/xfer.c | |
| @@ -191,11 +191,11 @@ | |
| 191 | if( rid==0 ){ |
| 192 | blob_appendf(&pXfer->err, "%s", g.zErrMsg); |
| 193 | blob_reset(&content); |
| 194 | }else{ |
| 195 | if( !isPriv ) content_make_public(rid); |
| 196 | manifest_crosslink(rid, &content, MC_NONE); |
| 197 | } |
| 198 | assert( blob_is_reset(&content) ); |
| 199 | remote_has(rid); |
| 200 | } |
| 201 | |
| @@ -820,33 +820,73 @@ | |
| 820 | static void server_private_xfer_not_authorized(void){ |
| 821 | @ error not\sauthorized\sto\ssync\sprivate\scontent |
| 822 | } |
| 823 | |
| 824 | /* |
| 825 | ** Return the common TH1 code to evaluate prior to evaluating any other |
| 826 | ** TH1 transfer notification scripts. |
| 827 | */ |
| 828 | const char *xfer_common_code(void){ |
| 829 | return db_get("xfer-common-script", 0); |
| 830 | } |
| 831 | |
| 832 | /* |
| 833 | ** Return the TH1 code to evaluate when a push is processed. |
| 834 | */ |
| 835 | const char *xfer_push_code(void){ |
| 836 | return db_get("xfer-push-script", 0); |
| 837 | } |
| 838 | |
| 839 | /* |
| 840 | ** Return the TH1 code to evaluate when a commit is processed. |
| 841 | */ |
| 842 | const char *xfer_commit_code(void){ |
| 843 | return db_get("xfer-commit-script", 0); |
| 844 | } |
| 845 | |
| 846 | /* |
| 847 | ** Return the TH1 code to evaluate when a ticket change is processed. |
| 848 | */ |
| 849 | const char *xfer_ticket_code(void){ |
| 850 | return db_get("xfer-ticket-script", 0); |
| 851 | } |
| 852 | |
| 853 | /* |
| 854 | ** Run the specified TH1 script, if any, and returns 1 on error. |
| 855 | */ |
| 856 | int xfer_run_script(const char *zScript, const char *zUuid){ |
| 857 | int result; |
| 858 | if( !zScript ) return TH_OK; |
| 859 | Th_FossilInit(TH_INIT_DEFAULT); |
| 860 | if( zUuid ){ |
| 861 | result = Th_SetVar(g.interp, "uuid", -1, zUuid, -1); |
| 862 | if( result!=TH_OK ){ |
| 863 | fossil_error(1, "%s", Th_GetResult(g.interp, 0)); |
| 864 | return result; |
| 865 | } |
| 866 | } |
| 867 | result = Th_Eval(g.interp, 0, zScript, -1); |
| 868 | if( result!=TH_OK ){ |
| 869 | fossil_error(1, "%s", Th_GetResult(g.interp, 0)); |
| 870 | } |
| 871 | return result; |
| 872 | } |
| 873 | |
| 874 | /* |
| 875 | ** Runs the pre-transfer TH1 script, if any, and returns its return code. |
| 876 | ** This script may be run multiple times. If the script performs actions |
| 877 | ** that cannot be redone, it should use an internal [if] guard similar to |
| 878 | ** the following: |
| 879 | ** |
| 880 | ** if {![info exists common_done]} { |
| 881 | ** # ... code here |
| 882 | ** set common_done 1 |
| 883 | ** } |
| 884 | */ |
| 885 | int xfer_run_common_script(void){ |
| 886 | Th_FossilInit(TH_INIT_DEFAULT); |
| 887 | return xfer_run_script(xfer_common_code(), 0); |
| 888 | } |
| 889 | |
| 890 | /* |
| 891 | ** If this variable is set, disable login checks. Used for debugging |
| 892 | ** only. |
| @@ -875,10 +915,11 @@ | |
| 915 | int isClone = 0; |
| 916 | int nGimme = 0; |
| 917 | int size; |
| 918 | int recvConfig = 0; |
| 919 | char *zNow; |
| 920 | int result; |
| 921 | |
| 922 | if( fossil_strcmp(PD("REQUEST_METHOD","POST"),"POST") ){ |
| 923 | fossil_redirect_home(); |
| 924 | } |
| 925 | g.zLogin = "anonymous"; |
| @@ -904,13 +945,14 @@ | |
| 945 | db_begin_transaction(); |
| 946 | db_multi_exec( |
| 947 | "CREATE TEMP TABLE onremote(rid INTEGER PRIMARY KEY);" |
| 948 | ); |
| 949 | manifest_crosslink_begin(); |
| 950 | result = xfer_run_common_script(); |
| 951 | if( result==TH_ERROR ){ |
| 952 | cgi_reset_content(); |
| 953 | @ error common\sscript\sfailed:\s%F(g.zErrMsg) |
| 954 | nErr++; |
| 955 | } |
| 956 | while( blob_line(xfer.pIn, &xfer.line) ){ |
| 957 | if( blob_buffer(&xfer.line)[0]=='#' ) continue; |
| 958 | if( blob_size(&xfer.line)==0 ) continue; |
| @@ -1231,14 +1273,17 @@ | |
| 1273 | } |
| 1274 | blobarray_reset(xfer.aToken, xfer.nToken); |
| 1275 | blob_reset(&xfer.line); |
| 1276 | } |
| 1277 | if( isPush ){ |
| 1278 | if( result==TH_OK ){ |
| 1279 | result = xfer_run_script(xfer_push_code(), 0); |
| 1280 | if( result==TH_ERROR ){ |
| 1281 | cgi_reset_content(); |
| 1282 | @ error push\sscript\sfailed:\s%F(g.zErrMsg) |
| 1283 | nErr++; |
| 1284 | } |
| 1285 | } |
| 1286 | request_phantoms(&xfer, 500); |
| 1287 | } |
| 1288 | if( isClone && nGimme==0 ){ |
| 1289 | /* The initial "clone" message from client to server contains no |
| @@ -1881,13 +1926,13 @@ | |
| 1926 | |
| 1927 | fossil_force_newline(); |
| 1928 | fossil_print( |
| 1929 | "%s finished with %lld bytes sent, %lld bytes received\n", |
| 1930 | zOpType, nSent, nRcvd); |
| 1931 | transport_close(GLOBAL_URL()); |
| 1932 | transport_global_shutdown(GLOBAL_URL()); |
| 1933 | db_multi_exec("DROP TABLE onremote"); |
| 1934 | manifest_crosslink_end(); |
| 1935 | content_enable_dephantomize(1); |
| 1936 | db_end_transaction(0); |
| 1937 | return nErr; |
| 1938 | } |
| 1939 |
+87
| --- src/xfersetup.c | ||
| +++ src/xfersetup.c | ||
| @@ -31,16 +31,63 @@ | ||
| 31 | 31 | if( !g.perm.Setup ){ |
| 32 | 32 | login_needed(); |
| 33 | 33 | } |
| 34 | 34 | |
| 35 | 35 | style_header("Transfer Setup"); |
| 36 | + | |
| 36 | 37 | @ <table border="0" cellspacing="20"> |
| 37 | 38 | setup_menu_entry("Common", "xfersetup_com", |
| 38 | 39 | "Common TH1 code run before all transfer request processing."); |
| 39 | 40 | setup_menu_entry("Push", "xfersetup_push", |
| 40 | 41 | "Specific TH1 code to run after \"push\" transfer requests."); |
| 42 | + setup_menu_entry("Commit", "xfersetup_commit", | |
| 43 | + "Specific TH1 code to run after processing a commit."); | |
| 44 | + setup_menu_entry("Ticket", "xfersetup_ticket", | |
| 45 | + "Specific TH1 code to run after processing a ticket change."); | |
| 41 | 46 | @ </table> |
| 47 | + | |
| 48 | + url_parse(0, 0); | |
| 49 | + if( g.urlProtocol ){ | |
| 50 | + unsigned syncFlags; | |
| 51 | + const char *zButton; | |
| 52 | + char *zWarning; | |
| 53 | + | |
| 54 | + if( db_get_boolean("dont-push", 0) ){ | |
| 55 | + syncFlags = SYNC_PULL; | |
| 56 | + zButton = "Pull"; | |
| 57 | + zWarning = 0; | |
| 58 | + }else{ | |
| 59 | + syncFlags = SYNC_PUSH | SYNC_PULL; | |
| 60 | + zButton = "Synchronize"; | |
| 61 | + zWarning = mprintf("WARNING: Pushing to \"%s\" is enabled.", | |
| 62 | + g.urlCanonical); | |
| 63 | + } | |
| 64 | + if( P("sync") ){ | |
| 65 | + user_select(); | |
| 66 | + url_enable_proxy(0); | |
| 67 | + client_sync(syncFlags, 0, 0); | |
| 68 | + } | |
| 69 | + @ <p>Press the %h(zButton) button below to synchronize with the | |
| 70 | + @ "%h(g.urlCanonical)" repository now. This may be useful when | |
| 71 | + @ testing the various transfer scripts.</p> | |
| 72 | + @ <p>You can use the "http -async" command in your scripts, but | |
| 73 | + @ make sure the "th1-uri-regexp" setting is set first.</p> | |
| 74 | + if( zWarning ){ | |
| 75 | + @ | |
| 76 | + @ <big><b>%h(zWarning)</b></big> | |
| 77 | + free(zWarning); | |
| 78 | + } | |
| 79 | + @ | |
| 80 | + @ <blockquote> | |
| 81 | + @ <form method="post" action="%s(g.zTop)/%s(g.zPath)"><div> | |
| 82 | + login_insert_csrf_secret(); | |
| 83 | + @ <input type="submit" name="sync" value="%h(zButton)" /> | |
| 84 | + @ </div></form> | |
| 85 | + @ </blockquote> | |
| 86 | + @ | |
| 87 | + } | |
| 88 | + | |
| 42 | 89 | style_footer(); |
| 43 | 90 | } |
| 44 | 91 | |
| 45 | 92 | /* |
| 46 | 93 | ** Common implementation for the transfer setup editor pages. |
| @@ -140,9 +187,49 @@ | ||
| 140 | 187 | "Transfer Push Script", |
| 141 | 188 | "xfer-push-script", |
| 142 | 189 | zDefaultXferPush, |
| 143 | 190 | zDesc, |
| 144 | 191 | 0, |
| 192 | + 0, | |
| 193 | + 30 | |
| 194 | + ); | |
| 195 | +} | |
| 196 | + | |
| 197 | +static const char *zDefaultXferCommit = 0; | |
| 198 | + | |
| 199 | +/* | |
| 200 | +** WEBPAGE: xfersetup_commit | |
| 201 | +*/ | |
| 202 | +void xfersetup_commit_page(void){ | |
| 203 | + static const char zDesc[] = | |
| 204 | + @ Enter TH1 script that runs when a commit is processed. | |
| 205 | + ; | |
| 206 | + xfersetup_generic( | |
| 207 | + "Transfer Commit Script", | |
| 208 | + "xfer-commit-script", | |
| 209 | + zDefaultXferCommit, | |
| 210 | + zDesc, | |
| 211 | + 0, | |
| 212 | + 0, | |
| 213 | + 30 | |
| 214 | + ); | |
| 215 | +} | |
| 216 | + | |
| 217 | +static const char *zDefaultXferTicket = 0; | |
| 218 | + | |
| 219 | +/* | |
| 220 | +** WEBPAGE: xfersetup_ticket | |
| 221 | +*/ | |
| 222 | +void xfersetup_ticket_page(void){ | |
| 223 | + static const char zDesc[] = | |
| 224 | + @ Enter TH1 script that runs when a ticket change is processed. | |
| 225 | + ; | |
| 226 | + xfersetup_generic( | |
| 227 | + "Transfer Ticket Script", | |
| 228 | + "xfer-ticket-script", | |
| 229 | + zDefaultXferTicket, | |
| 230 | + zDesc, | |
| 231 | + 0, | |
| 145 | 232 | 0, |
| 146 | 233 | 30 |
| 147 | 234 | ); |
| 148 | 235 | } |
| 149 | 236 |
| --- src/xfersetup.c | |
| +++ src/xfersetup.c | |
| @@ -31,16 +31,63 @@ | |
| 31 | if( !g.perm.Setup ){ |
| 32 | login_needed(); |
| 33 | } |
| 34 | |
| 35 | style_header("Transfer Setup"); |
| 36 | @ <table border="0" cellspacing="20"> |
| 37 | setup_menu_entry("Common", "xfersetup_com", |
| 38 | "Common TH1 code run before all transfer request processing."); |
| 39 | setup_menu_entry("Push", "xfersetup_push", |
| 40 | "Specific TH1 code to run after \"push\" transfer requests."); |
| 41 | @ </table> |
| 42 | style_footer(); |
| 43 | } |
| 44 | |
| 45 | /* |
| 46 | ** Common implementation for the transfer setup editor pages. |
| @@ -140,9 +187,49 @@ | |
| 140 | "Transfer Push Script", |
| 141 | "xfer-push-script", |
| 142 | zDefaultXferPush, |
| 143 | zDesc, |
| 144 | 0, |
| 145 | 0, |
| 146 | 30 |
| 147 | ); |
| 148 | } |
| 149 |
| --- src/xfersetup.c | |
| +++ src/xfersetup.c | |
| @@ -31,16 +31,63 @@ | |
| 31 | if( !g.perm.Setup ){ |
| 32 | login_needed(); |
| 33 | } |
| 34 | |
| 35 | style_header("Transfer Setup"); |
| 36 | |
| 37 | @ <table border="0" cellspacing="20"> |
| 38 | setup_menu_entry("Common", "xfersetup_com", |
| 39 | "Common TH1 code run before all transfer request processing."); |
| 40 | setup_menu_entry("Push", "xfersetup_push", |
| 41 | "Specific TH1 code to run after \"push\" transfer requests."); |
| 42 | setup_menu_entry("Commit", "xfersetup_commit", |
| 43 | "Specific TH1 code to run after processing a commit."); |
| 44 | setup_menu_entry("Ticket", "xfersetup_ticket", |
| 45 | "Specific TH1 code to run after processing a ticket change."); |
| 46 | @ </table> |
| 47 | |
| 48 | url_parse(0, 0); |
| 49 | if( g.urlProtocol ){ |
| 50 | unsigned syncFlags; |
| 51 | const char *zButton; |
| 52 | char *zWarning; |
| 53 | |
| 54 | if( db_get_boolean("dont-push", 0) ){ |
| 55 | syncFlags = SYNC_PULL; |
| 56 | zButton = "Pull"; |
| 57 | zWarning = 0; |
| 58 | }else{ |
| 59 | syncFlags = SYNC_PUSH | SYNC_PULL; |
| 60 | zButton = "Synchronize"; |
| 61 | zWarning = mprintf("WARNING: Pushing to \"%s\" is enabled.", |
| 62 | g.urlCanonical); |
| 63 | } |
| 64 | if( P("sync") ){ |
| 65 | user_select(); |
| 66 | url_enable_proxy(0); |
| 67 | client_sync(syncFlags, 0, 0); |
| 68 | } |
| 69 | @ <p>Press the %h(zButton) button below to synchronize with the |
| 70 | @ "%h(g.urlCanonical)" repository now. This may be useful when |
| 71 | @ testing the various transfer scripts.</p> |
| 72 | @ <p>You can use the "http -async" command in your scripts, but |
| 73 | @ make sure the "th1-uri-regexp" setting is set first.</p> |
| 74 | if( zWarning ){ |
| 75 | @ |
| 76 | @ <big><b>%h(zWarning)</b></big> |
| 77 | free(zWarning); |
| 78 | } |
| 79 | @ |
| 80 | @ <blockquote> |
| 81 | @ <form method="post" action="%s(g.zTop)/%s(g.zPath)"><div> |
| 82 | login_insert_csrf_secret(); |
| 83 | @ <input type="submit" name="sync" value="%h(zButton)" /> |
| 84 | @ </div></form> |
| 85 | @ </blockquote> |
| 86 | @ |
| 87 | } |
| 88 | |
| 89 | style_footer(); |
| 90 | } |
| 91 | |
| 92 | /* |
| 93 | ** Common implementation for the transfer setup editor pages. |
| @@ -140,9 +187,49 @@ | |
| 187 | "Transfer Push Script", |
| 188 | "xfer-push-script", |
| 189 | zDefaultXferPush, |
| 190 | zDesc, |
| 191 | 0, |
| 192 | 0, |
| 193 | 30 |
| 194 | ); |
| 195 | } |
| 196 | |
| 197 | static const char *zDefaultXferCommit = 0; |
| 198 | |
| 199 | /* |
| 200 | ** WEBPAGE: xfersetup_commit |
| 201 | */ |
| 202 | void xfersetup_commit_page(void){ |
| 203 | static const char zDesc[] = |
| 204 | @ Enter TH1 script that runs when a commit is processed. |
| 205 | ; |
| 206 | xfersetup_generic( |
| 207 | "Transfer Commit Script", |
| 208 | "xfer-commit-script", |
| 209 | zDefaultXferCommit, |
| 210 | zDesc, |
| 211 | 0, |
| 212 | 0, |
| 213 | 30 |
| 214 | ); |
| 215 | } |
| 216 | |
| 217 | static const char *zDefaultXferTicket = 0; |
| 218 | |
| 219 | /* |
| 220 | ** WEBPAGE: xfersetup_ticket |
| 221 | */ |
| 222 | void xfersetup_ticket_page(void){ |
| 223 | static const char zDesc[] = |
| 224 | @ Enter TH1 script that runs when a ticket change is processed. |
| 225 | ; |
| 226 | xfersetup_generic( |
| 227 | "Transfer Ticket Script", |
| 228 | "xfer-ticket-script", |
| 229 | zDefaultXferTicket, |
| 230 | zDesc, |
| 231 | 0, |
| 232 | 0, |
| 233 | 30 |
| 234 | ); |
| 235 | } |
| 236 |