Fossil SCM
The --share-links option on "fossil sync" and similar causes the server to reply with "pragma link" lines that identify other repositories with which the server has interacted within the past month. Those links are recorded in "link:URL" entries of the CONFIG table on the client.
Commit
12d2f70bdff55411e8a0df1a62d50215185e1ab3200aa350ce78959b3d8c133a
Parent
4ff45df4294c178…
2 files changed
+7
+107
-25
+7
| --- src/sync.c | ||
| +++ src/sync.c | ||
| @@ -232,10 +232,15 @@ | ||
| 232 | 232 | *pSyncFlags |= SYNC_NOHTTPCOMPRESS; |
| 233 | 233 | } |
| 234 | 234 | if( find_option("all",0,0)!=0 ){ |
| 235 | 235 | *pSyncFlags |= SYNC_ALLURL; |
| 236 | 236 | } |
| 237 | + if( ((*pSyncFlags) & SYNC_PULL)!=0 | |
| 238 | + && find_option("share-links",0,0)!=0 | |
| 239 | + ){ | |
| 240 | + *pSyncFlags |= SYNC_SHARE_LINKS; | |
| 241 | + } | |
| 237 | 242 | url_proxy_options(); |
| 238 | 243 | clone_ssh_find_options(); |
| 239 | 244 | if( !uvOnly ) db_find_and_open_repository(0, 0); |
| 240 | 245 | db_open_config(0, 1); |
| 241 | 246 | if( g.argc==2 ){ |
| @@ -294,10 +299,11 @@ | ||
| 294 | 299 | ** --once Do not remember URL for subsequent syncs |
| 295 | 300 | ** --private Pull private branches too |
| 296 | 301 | ** --project-code CODE Use CODE as the project code |
| 297 | 302 | ** --proxy PROXY Use the specified HTTP proxy |
| 298 | 303 | ** -R|--repository REPO Local repository to pull into |
| 304 | +** --share-links Share links to mirror repos | |
| 299 | 305 | ** --ssl-identity FILE Local SSL credentials, if requested by remote |
| 300 | 306 | ** --ssh-command SSH Use SSH as the "ssh" command |
| 301 | 307 | ** -v|--verbose Additional (debugging) output |
| 302 | 308 | ** --verily Exchange extra information with the remote |
| 303 | 309 | ** to ensure no content is overlooked |
| @@ -392,10 +398,11 @@ | ||
| 392 | 398 | ** --no-http-compression Do not compress HTTP traffic |
| 393 | 399 | ** --once Do not remember URL for subsequent syncs |
| 394 | 400 | ** --proxy PROXY Use the specified HTTP proxy |
| 395 | 401 | ** --private Sync private branches too |
| 396 | 402 | ** -R|--repository REPO Local repository to sync with |
| 403 | +** --share-links Share links to mirror repos | |
| 397 | 404 | ** --ssl-identity FILE Local SSL credentials, if requested by remote |
| 398 | 405 | ** --ssh-command SSH Use SSH as the "ssh" command |
| 399 | 406 | ** -u|--unversioned Also sync unversioned content |
| 400 | 407 | ** -v|--verbose Additional (debugging) output |
| 401 | 408 | ** --verily Exchange extra information with the remote |
| 402 | 409 |
| --- src/sync.c | |
| +++ src/sync.c | |
| @@ -232,10 +232,15 @@ | |
| 232 | *pSyncFlags |= SYNC_NOHTTPCOMPRESS; |
| 233 | } |
| 234 | if( find_option("all",0,0)!=0 ){ |
| 235 | *pSyncFlags |= SYNC_ALLURL; |
| 236 | } |
| 237 | url_proxy_options(); |
| 238 | clone_ssh_find_options(); |
| 239 | if( !uvOnly ) db_find_and_open_repository(0, 0); |
| 240 | db_open_config(0, 1); |
| 241 | if( g.argc==2 ){ |
| @@ -294,10 +299,11 @@ | |
| 294 | ** --once Do not remember URL for subsequent syncs |
| 295 | ** --private Pull private branches too |
| 296 | ** --project-code CODE Use CODE as the project code |
| 297 | ** --proxy PROXY Use the specified HTTP proxy |
| 298 | ** -R|--repository REPO Local repository to pull into |
| 299 | ** --ssl-identity FILE Local SSL credentials, if requested by remote |
| 300 | ** --ssh-command SSH Use SSH as the "ssh" command |
| 301 | ** -v|--verbose Additional (debugging) output |
| 302 | ** --verily Exchange extra information with the remote |
| 303 | ** to ensure no content is overlooked |
| @@ -392,10 +398,11 @@ | |
| 392 | ** --no-http-compression Do not compress HTTP traffic |
| 393 | ** --once Do not remember URL for subsequent syncs |
| 394 | ** --proxy PROXY Use the specified HTTP proxy |
| 395 | ** --private Sync private branches too |
| 396 | ** -R|--repository REPO Local repository to sync with |
| 397 | ** --ssl-identity FILE Local SSL credentials, if requested by remote |
| 398 | ** --ssh-command SSH Use SSH as the "ssh" command |
| 399 | ** -u|--unversioned Also sync unversioned content |
| 400 | ** -v|--verbose Additional (debugging) output |
| 401 | ** --verily Exchange extra information with the remote |
| 402 |
| --- src/sync.c | |
| +++ src/sync.c | |
| @@ -232,10 +232,15 @@ | |
| 232 | *pSyncFlags |= SYNC_NOHTTPCOMPRESS; |
| 233 | } |
| 234 | if( find_option("all",0,0)!=0 ){ |
| 235 | *pSyncFlags |= SYNC_ALLURL; |
| 236 | } |
| 237 | if( ((*pSyncFlags) & SYNC_PULL)!=0 |
| 238 | && find_option("share-links",0,0)!=0 |
| 239 | ){ |
| 240 | *pSyncFlags |= SYNC_SHARE_LINKS; |
| 241 | } |
| 242 | url_proxy_options(); |
| 243 | clone_ssh_find_options(); |
| 244 | if( !uvOnly ) db_find_and_open_repository(0, 0); |
| 245 | db_open_config(0, 1); |
| 246 | if( g.argc==2 ){ |
| @@ -294,10 +299,11 @@ | |
| 299 | ** --once Do not remember URL for subsequent syncs |
| 300 | ** --private Pull private branches too |
| 301 | ** --project-code CODE Use CODE as the project code |
| 302 | ** --proxy PROXY Use the specified HTTP proxy |
| 303 | ** -R|--repository REPO Local repository to pull into |
| 304 | ** --share-links Share links to mirror repos |
| 305 | ** --ssl-identity FILE Local SSL credentials, if requested by remote |
| 306 | ** --ssh-command SSH Use SSH as the "ssh" command |
| 307 | ** -v|--verbose Additional (debugging) output |
| 308 | ** --verily Exchange extra information with the remote |
| 309 | ** to ensure no content is overlooked |
| @@ -392,10 +398,11 @@ | |
| 398 | ** --no-http-compression Do not compress HTTP traffic |
| 399 | ** --once Do not remember URL for subsequent syncs |
| 400 | ** --proxy PROXY Use the specified HTTP proxy |
| 401 | ** --private Sync private branches too |
| 402 | ** -R|--repository REPO Local repository to sync with |
| 403 | ** --share-links Share links to mirror repos |
| 404 | ** --ssl-identity FILE Local SSL credentials, if requested by remote |
| 405 | ** --ssh-command SSH Use SSH as the "ssh" command |
| 406 | ** -u|--unversioned Also sync unversioned content |
| 407 | ** -v|--verbose Additional (debugging) output |
| 408 | ** --verily Exchange extra information with the remote |
| 409 |
+107
-25
| --- src/xfer.c | ||
| +++ src/xfer.c | ||
| @@ -1209,10 +1209,11 @@ | ||
| 1209 | 1209 | char *zUuidList = 0; |
| 1210 | 1210 | int nUuidList = 0; |
| 1211 | 1211 | char **pzUuidList = 0; |
| 1212 | 1212 | int *pnUuidList = 0; |
| 1213 | 1213 | int uvCatalogSent = 0; |
| 1214 | + int bSendLinks = 0; | |
| 1214 | 1215 | |
| 1215 | 1216 | if( fossil_strcmp(PD("REQUEST_METHOD","POST"),"POST") ){ |
| 1216 | 1217 | fossil_redirect_home(); |
| 1217 | 1218 | } |
| 1218 | 1219 | g.zLogin = "anonymous"; |
| @@ -1599,21 +1600,21 @@ | ||
| 1599 | 1600 | if( !g.perm.Private ){ |
| 1600 | 1601 | server_private_xfer_not_authorized(); |
| 1601 | 1602 | }else{ |
| 1602 | 1603 | xfer.syncPrivate = 1; |
| 1603 | 1604 | } |
| 1604 | - } | |
| 1605 | + }else | |
| 1605 | 1606 | |
| 1606 | 1607 | /* pragma send-catalog |
| 1607 | 1608 | ** |
| 1608 | 1609 | ** The client wants to see igot cards for all known artifacts. |
| 1609 | 1610 | ** This is used as part of "sync --verily" to help ensure that |
| 1610 | 1611 | ** no artifacts have been missed on prior syncs. |
| 1611 | 1612 | */ |
| 1612 | 1613 | if( blob_eq(&xfer.aToken[1], "send-catalog") ){ |
| 1613 | 1614 | xfer.resync = 0x7fffffff; |
| 1614 | - } | |
| 1615 | + }else | |
| 1615 | 1616 | |
| 1616 | 1617 | /* pragma client-version VERSION ?DATE? ?TIME? |
| 1617 | 1618 | ** |
| 1618 | 1619 | ** The client announces to the server what version of Fossil it |
| 1619 | 1620 | ** is running. The DATE and TIME are a pure numeric ISO8601 time |
| @@ -1625,11 +1626,11 @@ | ||
| 1625 | 1626 | xfer.remoteDate = atoi(blob_str(&xfer.aToken[3])); |
| 1626 | 1627 | xfer.remoteTime = atoi(blob_str(&xfer.aToken[4])); |
| 1627 | 1628 | @ pragma server-version %d(RELEASE_VERSION_NUMBER) \ |
| 1628 | 1629 | @ %d(MANIFEST_NUMERIC_DATE) %d(MANIFEST_NUMERIC_TIME) |
| 1629 | 1630 | } |
| 1630 | - } | |
| 1631 | + }else | |
| 1631 | 1632 | |
| 1632 | 1633 | /* pragma uv-hash HASH |
| 1633 | 1634 | ** |
| 1634 | 1635 | ** The client wants to make sure that unversioned files are all synced. |
| 1635 | 1636 | ** If the HASH does not match, send a complete catalog of |
| @@ -1648,11 +1649,11 @@ | ||
| 1648 | 1649 | @ pragma uv-pull-only |
| 1649 | 1650 | } |
| 1650 | 1651 | send_unversioned_catalog(&xfer); |
| 1651 | 1652 | } |
| 1652 | 1653 | uvCatalogSent = 1; |
| 1653 | - } | |
| 1654 | + }else | |
| 1654 | 1655 | |
| 1655 | 1656 | /* pragma ci-lock CHECKIN-HASH CLIENT-ID |
| 1656 | 1657 | ** |
| 1657 | 1658 | ** The client wants to make non-branch commit against the check-in |
| 1658 | 1659 | ** identified by CHECKIN-HASH. The server will remember this and |
| @@ -1708,11 +1709,11 @@ | ||
| 1708 | 1709 | db_protect_pop(); |
| 1709 | 1710 | } |
| 1710 | 1711 | if( db_get_boolean("forbid-delta-manifests",0) ){ |
| 1711 | 1712 | @ pragma avoid-delta-manifests |
| 1712 | 1713 | } |
| 1713 | - } | |
| 1714 | + }else | |
| 1714 | 1715 | |
| 1715 | 1716 | /* pragma ci-unlock CLIENT-ID |
| 1716 | 1717 | ** |
| 1717 | 1718 | ** Remove any locks previously held by CLIENT-ID. Clients send this |
| 1718 | 1719 | ** pragma with their own ID whenever they know that they no longer |
| @@ -1728,11 +1729,11 @@ | ||
| 1728 | 1729 | " WHERE name GLOB 'ci-lock-*'" |
| 1729 | 1730 | " AND json_extract(value,'$.clientid')=%Q", |
| 1730 | 1731 | blob_str(&xfer.aToken[2]) |
| 1731 | 1732 | ); |
| 1732 | 1733 | db_protect_pop(); |
| 1733 | - } | |
| 1734 | + }else | |
| 1734 | 1735 | |
| 1735 | 1736 | /* pragma client-url URL |
| 1736 | 1737 | ** |
| 1737 | 1738 | ** This pragma is an informational notification to the server that |
| 1738 | 1739 | ** their relationship could, in theory, be inverted by having the |
| @@ -1741,10 +1742,19 @@ | ||
| 1741 | 1742 | if( blob_eq(&xfer.aToken[1], "client-url") |
| 1742 | 1743 | && xfer.nToken==3 |
| 1743 | 1744 | && g.perm.Write |
| 1744 | 1745 | ){ |
| 1745 | 1746 | xfer_syncwith(blob_str(&xfer.aToken[2]), 1); |
| 1747 | + }else | |
| 1748 | + | |
| 1749 | + /* pragma req-links | |
| 1750 | + ** | |
| 1751 | + ** The client sends this message to the server to ask the server | |
| 1752 | + ** to tell it about alternative repositories in the reply. | |
| 1753 | + */ | |
| 1754 | + if( blob_eq(&xfer.aToken[1], "req-links") ){ | |
| 1755 | + bSendLinks = 1; | |
| 1746 | 1756 | } |
| 1747 | 1757 | |
| 1748 | 1758 | }else |
| 1749 | 1759 | |
| 1750 | 1760 | /* Unknown message |
| @@ -1786,10 +1796,45 @@ | ||
| 1786 | 1796 | if( xfer.syncPrivate ) send_private(&xfer); |
| 1787 | 1797 | } |
| 1788 | 1798 | hook_expecting_more_artifacts(xfer.nGimmeSent?60:0); |
| 1789 | 1799 | db_multi_exec("DROP TABLE onremote; DROP TABLE unk;"); |
| 1790 | 1800 | manifest_crosslink_end(MC_PERMIT_HOOKS); |
| 1801 | + | |
| 1802 | + /* Send URLs for alternative repositories for the same project, | |
| 1803 | + ** if requested by the client. */ | |
| 1804 | + if( bSendLinks && g.zBaseURL ){ | |
| 1805 | + Stmt q; | |
| 1806 | + db_prepare(&q, | |
| 1807 | + "WITH remote(mtime, url, arg) AS (\n" | |
| 1808 | + " SELECT mtime, substr(name,10), '{}' FROM config\n" | |
| 1809 | + " WHERE name GLOB 'syncwith:http*'\n" | |
| 1810 | + " UNION ALL\n" | |
| 1811 | + " SELECT mtime, substr(name,10), '{}' FROM config\n" | |
| 1812 | + " WHERE name GLOB 'syncfrom:http*'\n" | |
| 1813 | + " UNION ALL\n" | |
| 1814 | + " SELECT mtime, substr(name,9), '{\"type\":\"git\"}' FROM config\n" | |
| 1815 | + " WHERE name GLOB 'gitpush:*'\n" | |
| 1816 | + ")\n" | |
| 1817 | + "SELECT url, json_insert(arg,'$.src',%Q), max(mtime)\n" | |
| 1818 | + " FROM remote WHERE mtime>unixepoch('now','-1 month')\n" | |
| 1819 | + " GROUP BY url;", | |
| 1820 | + g.zBaseURL | |
| 1821 | + ); | |
| 1822 | + while( db_step(&q)==SQLITE_ROW ){ | |
| 1823 | + UrlData x; | |
| 1824 | + const char *zUrl = db_column_text(&q, 0); | |
| 1825 | + const char *zArg = db_column_text(&q, 1); | |
| 1826 | + i64 iMtime = db_column_int64(&q, 2); | |
| 1827 | + memset(&x, 0, sizeof(x)); | |
| 1828 | + url_parse_local(zUrl, URL_OMIT_USER, &x); | |
| 1829 | + if( x.name!=0 && sqlite3_strlike("%localhost%", x.name, 0)!=0 ){ | |
| 1830 | + @ pragma link %F(x.canonical) %F(zArg) %lld(iMtime) | |
| 1831 | + } | |
| 1832 | + url_unparse(&x); | |
| 1833 | + } | |
| 1834 | + db_finalize(&q); | |
| 1835 | + } | |
| 1791 | 1836 | |
| 1792 | 1837 | /* Send the server timestamp last, in case prior processing happened |
| 1793 | 1838 | ** to use up a significant fraction of our time window. |
| 1794 | 1839 | */ |
| 1795 | 1840 | zNow = db_text(0, "SELECT strftime('%%Y-%%m-%%dT%%H:%%M:%%S', 'now')"); |
| @@ -1842,25 +1887,26 @@ | ||
| 1842 | 1887 | |
| 1843 | 1888 | #if INTERFACE |
| 1844 | 1889 | /* |
| 1845 | 1890 | ** Flag options for controlling client_sync() |
| 1846 | 1891 | */ |
| 1847 | -#define SYNC_PUSH 0x0001 /* push content client to server */ | |
| 1848 | -#define SYNC_PULL 0x0002 /* pull content server to client */ | |
| 1849 | -#define SYNC_CLONE 0x0004 /* clone the repository */ | |
| 1850 | -#define SYNC_PRIVATE 0x0008 /* Also transfer private content */ | |
| 1851 | -#define SYNC_VERBOSE 0x0010 /* Extra diagnostics */ | |
| 1852 | -#define SYNC_RESYNC 0x0020 /* --verily */ | |
| 1853 | -#define SYNC_FROMPARENT 0x0040 /* Pull from the parent project */ | |
| 1854 | -#define SYNC_UNVERSIONED 0x0100 /* Sync unversioned content */ | |
| 1855 | -#define SYNC_UV_REVERT 0x0200 /* Copy server unversioned to client */ | |
| 1856 | -#define SYNC_UV_TRACE 0x0400 /* Describe UV activities */ | |
| 1857 | -#define SYNC_UV_DRYRUN 0x0800 /* Do not actually exchange files */ | |
| 1858 | -#define SYNC_IFABLE 0x1000 /* Inability to sync is not fatal */ | |
| 1859 | -#define SYNC_CKIN_LOCK 0x2000 /* Lock the current check-in */ | |
| 1860 | -#define SYNC_NOHTTPCOMPRESS 0x4000 /* Do not compression HTTP messages */ | |
| 1861 | -#define SYNC_ALLURL 0x8000 /* The --all flag - sync to all URLs */ | |
| 1892 | +#define SYNC_PUSH 0x00001 /* push content client to server */ | |
| 1893 | +#define SYNC_PULL 0x00002 /* pull content server to client */ | |
| 1894 | +#define SYNC_CLONE 0x00004 /* clone the repository */ | |
| 1895 | +#define SYNC_PRIVATE 0x00008 /* Also transfer private content */ | |
| 1896 | +#define SYNC_VERBOSE 0x00010 /* Extra diagnostics */ | |
| 1897 | +#define SYNC_RESYNC 0x00020 /* --verily */ | |
| 1898 | +#define SYNC_FROMPARENT 0x00040 /* Pull from the parent project */ | |
| 1899 | +#define SYNC_UNVERSIONED 0x00100 /* Sync unversioned content */ | |
| 1900 | +#define SYNC_UV_REVERT 0x00200 /* Copy server unversioned to client */ | |
| 1901 | +#define SYNC_UV_TRACE 0x00400 /* Describe UV activities */ | |
| 1902 | +#define SYNC_UV_DRYRUN 0x00800 /* Do not actually exchange files */ | |
| 1903 | +#define SYNC_IFABLE 0x01000 /* Inability to sync is not fatal */ | |
| 1904 | +#define SYNC_CKIN_LOCK 0x02000 /* Lock the current check-in */ | |
| 1905 | +#define SYNC_NOHTTPCOMPRESS 0x04000 /* Do not compression HTTP messages */ | |
| 1906 | +#define SYNC_ALLURL 0x08000 /* The --all flag - sync to all URLs */ | |
| 1907 | +#define SYNC_SHARE_LINKS 0x10000 /* Request alternate repo links */ | |
| 1862 | 1908 | #endif |
| 1863 | 1909 | |
| 1864 | 1910 | /* |
| 1865 | 1911 | ** Floating-point absolute value |
| 1866 | 1912 | */ |
| @@ -2052,14 +2098,14 @@ | ||
| 2052 | 2098 | if( zSelfUrl ){ |
| 2053 | 2099 | blob_appendf(&send, "pragma client-url %s\n", zSelfUrl); |
| 2054 | 2100 | } |
| 2055 | 2101 | } |
| 2056 | 2102 | |
| 2057 | - /* Request names of alternative repositories | |
| 2103 | + /* Request URLs of alternative repositories | |
| 2058 | 2104 | */ |
| 2059 | - if( zAltPCode==0 ){ | |
| 2060 | - blob_appendf(&send, "pragma req-alt-repo\n"); | |
| 2105 | + if( zAltPCode==0 && (syncFlags & SYNC_SHARE_LINKS)!=0 ){ | |
| 2106 | + blob_appendf(&send, "pragma req-links\n"); | |
| 2061 | 2107 | } |
| 2062 | 2108 | |
| 2063 | 2109 | while( go ){ |
| 2064 | 2110 | int newPhantom = 0; |
| 2065 | 2111 | char *zRandomness; |
| @@ -2590,11 +2636,11 @@ | ||
| 2590 | 2636 | ** this client lacks the necessary permissions) then it sends a |
| 2591 | 2637 | ** "uv-pull-only" pragma so that the client will know not to waste |
| 2592 | 2638 | ** bandwidth trying to upload unversioned content. If the server |
| 2593 | 2639 | ** does accept new unversioned content, it sends "uv-push-ok". |
| 2594 | 2640 | */ |
| 2595 | - if( syncFlags & SYNC_UNVERSIONED ){ | |
| 2641 | + else if( syncFlags & SYNC_UNVERSIONED ){ | |
| 2596 | 2642 | if( blob_eq(&xfer.aToken[1], "uv-pull-only") ){ |
| 2597 | 2643 | uvPullOnly = 1; |
| 2598 | 2644 | if( syncFlags & SYNC_UV_REVERT ) uvDoPush = 1; |
| 2599 | 2645 | }else if( blob_eq(&xfer.aToken[1], "uv-push-ok") ){ |
| 2600 | 2646 | uvDoPush = 1; |
| @@ -2630,10 +2676,46 @@ | ||
| 2630 | 2676 | ** this pragma when its forbid-delta-manifests setting is true. |
| 2631 | 2677 | */ |
| 2632 | 2678 | else if( blob_eq(&xfer.aToken[1], "avoid-delta-manifests") ){ |
| 2633 | 2679 | g.bAvoidDeltaManifests = 1; |
| 2634 | 2680 | } |
| 2681 | + | |
| 2682 | + /* pragma link URL ARG MTIME | |
| 2683 | + ** | |
| 2684 | + ** The server has sent the URL for a link to another repository. | |
| 2685 | + ** Record this as a link:URL entry in the config table. | |
| 2686 | + */ | |
| 2687 | + else if( blob_eq(&xfer.aToken[1], "link") | |
| 2688 | + && xfer.nToken==5 | |
| 2689 | + && (syncFlags & SYNC_SHARE_LINKS)!=0 | |
| 2690 | + ){ | |
| 2691 | + UrlData x; | |
| 2692 | + char *zUrl = blob_str(&xfer.aToken[2]); | |
| 2693 | + char *zArg = blob_str(&xfer.aToken[3]); | |
| 2694 | + i64 iTime = strtoll(blob_str(&xfer.aToken[4]),0,0); | |
| 2695 | + memset(&x, 0, sizeof(x)); | |
| 2696 | + defossilize(zUrl); | |
| 2697 | + defossilize(zArg); | |
| 2698 | + url_parse_local(zUrl, URL_OMIT_USER, &x); | |
| 2699 | + if( x.protocol | |
| 2700 | + && strncmp(x.protocol,"http",4)==0 | |
| 2701 | + && iTime>0 | |
| 2702 | + ){ | |
| 2703 | + db_unprotect(PROTECT_CONFIG); | |
| 2704 | + db_multi_exec( | |
| 2705 | + "INSERT INTO config(name,value,mtime)\n" | |
| 2706 | + " VALUES('link:%q',%Q,%lld)\n" | |
| 2707 | + " ON CONFLICT DO UPDATE\n" | |
| 2708 | + " SET value=excluded.value, mtime=excluded.mtime\n" | |
| 2709 | + " WHERE mtime<excluded.mtime;", | |
| 2710 | + zUrl, zArg, iTime | |
| 2711 | + ); | |
| 2712 | + db_protect_pop(); | |
| 2713 | + } | |
| 2714 | + url_unparse(&x); | |
| 2715 | + } | |
| 2716 | + | |
| 2635 | 2717 | }else |
| 2636 | 2718 | |
| 2637 | 2719 | /* error MESSAGE |
| 2638 | 2720 | ** |
| 2639 | 2721 | ** The server is reporting an error. The client will abandon |
| 2640 | 2722 |
| --- src/xfer.c | |
| +++ src/xfer.c | |
| @@ -1209,10 +1209,11 @@ | |
| 1209 | char *zUuidList = 0; |
| 1210 | int nUuidList = 0; |
| 1211 | char **pzUuidList = 0; |
| 1212 | int *pnUuidList = 0; |
| 1213 | int uvCatalogSent = 0; |
| 1214 | |
| 1215 | if( fossil_strcmp(PD("REQUEST_METHOD","POST"),"POST") ){ |
| 1216 | fossil_redirect_home(); |
| 1217 | } |
| 1218 | g.zLogin = "anonymous"; |
| @@ -1599,21 +1600,21 @@ | |
| 1599 | if( !g.perm.Private ){ |
| 1600 | server_private_xfer_not_authorized(); |
| 1601 | }else{ |
| 1602 | xfer.syncPrivate = 1; |
| 1603 | } |
| 1604 | } |
| 1605 | |
| 1606 | /* pragma send-catalog |
| 1607 | ** |
| 1608 | ** The client wants to see igot cards for all known artifacts. |
| 1609 | ** This is used as part of "sync --verily" to help ensure that |
| 1610 | ** no artifacts have been missed on prior syncs. |
| 1611 | */ |
| 1612 | if( blob_eq(&xfer.aToken[1], "send-catalog") ){ |
| 1613 | xfer.resync = 0x7fffffff; |
| 1614 | } |
| 1615 | |
| 1616 | /* pragma client-version VERSION ?DATE? ?TIME? |
| 1617 | ** |
| 1618 | ** The client announces to the server what version of Fossil it |
| 1619 | ** is running. The DATE and TIME are a pure numeric ISO8601 time |
| @@ -1625,11 +1626,11 @@ | |
| 1625 | xfer.remoteDate = atoi(blob_str(&xfer.aToken[3])); |
| 1626 | xfer.remoteTime = atoi(blob_str(&xfer.aToken[4])); |
| 1627 | @ pragma server-version %d(RELEASE_VERSION_NUMBER) \ |
| 1628 | @ %d(MANIFEST_NUMERIC_DATE) %d(MANIFEST_NUMERIC_TIME) |
| 1629 | } |
| 1630 | } |
| 1631 | |
| 1632 | /* pragma uv-hash HASH |
| 1633 | ** |
| 1634 | ** The client wants to make sure that unversioned files are all synced. |
| 1635 | ** If the HASH does not match, send a complete catalog of |
| @@ -1648,11 +1649,11 @@ | |
| 1648 | @ pragma uv-pull-only |
| 1649 | } |
| 1650 | send_unversioned_catalog(&xfer); |
| 1651 | } |
| 1652 | uvCatalogSent = 1; |
| 1653 | } |
| 1654 | |
| 1655 | /* pragma ci-lock CHECKIN-HASH CLIENT-ID |
| 1656 | ** |
| 1657 | ** The client wants to make non-branch commit against the check-in |
| 1658 | ** identified by CHECKIN-HASH. The server will remember this and |
| @@ -1708,11 +1709,11 @@ | |
| 1708 | db_protect_pop(); |
| 1709 | } |
| 1710 | if( db_get_boolean("forbid-delta-manifests",0) ){ |
| 1711 | @ pragma avoid-delta-manifests |
| 1712 | } |
| 1713 | } |
| 1714 | |
| 1715 | /* pragma ci-unlock CLIENT-ID |
| 1716 | ** |
| 1717 | ** Remove any locks previously held by CLIENT-ID. Clients send this |
| 1718 | ** pragma with their own ID whenever they know that they no longer |
| @@ -1728,11 +1729,11 @@ | |
| 1728 | " WHERE name GLOB 'ci-lock-*'" |
| 1729 | " AND json_extract(value,'$.clientid')=%Q", |
| 1730 | blob_str(&xfer.aToken[2]) |
| 1731 | ); |
| 1732 | db_protect_pop(); |
| 1733 | } |
| 1734 | |
| 1735 | /* pragma client-url URL |
| 1736 | ** |
| 1737 | ** This pragma is an informational notification to the server that |
| 1738 | ** their relationship could, in theory, be inverted by having the |
| @@ -1741,10 +1742,19 @@ | |
| 1741 | if( blob_eq(&xfer.aToken[1], "client-url") |
| 1742 | && xfer.nToken==3 |
| 1743 | && g.perm.Write |
| 1744 | ){ |
| 1745 | xfer_syncwith(blob_str(&xfer.aToken[2]), 1); |
| 1746 | } |
| 1747 | |
| 1748 | }else |
| 1749 | |
| 1750 | /* Unknown message |
| @@ -1786,10 +1796,45 @@ | |
| 1786 | if( xfer.syncPrivate ) send_private(&xfer); |
| 1787 | } |
| 1788 | hook_expecting_more_artifacts(xfer.nGimmeSent?60:0); |
| 1789 | db_multi_exec("DROP TABLE onremote; DROP TABLE unk;"); |
| 1790 | manifest_crosslink_end(MC_PERMIT_HOOKS); |
| 1791 | |
| 1792 | /* Send the server timestamp last, in case prior processing happened |
| 1793 | ** to use up a significant fraction of our time window. |
| 1794 | */ |
| 1795 | zNow = db_text(0, "SELECT strftime('%%Y-%%m-%%dT%%H:%%M:%%S', 'now')"); |
| @@ -1842,25 +1887,26 @@ | |
| 1842 | |
| 1843 | #if INTERFACE |
| 1844 | /* |
| 1845 | ** Flag options for controlling client_sync() |
| 1846 | */ |
| 1847 | #define SYNC_PUSH 0x0001 /* push content client to server */ |
| 1848 | #define SYNC_PULL 0x0002 /* pull content server to client */ |
| 1849 | #define SYNC_CLONE 0x0004 /* clone the repository */ |
| 1850 | #define SYNC_PRIVATE 0x0008 /* Also transfer private content */ |
| 1851 | #define SYNC_VERBOSE 0x0010 /* Extra diagnostics */ |
| 1852 | #define SYNC_RESYNC 0x0020 /* --verily */ |
| 1853 | #define SYNC_FROMPARENT 0x0040 /* Pull from the parent project */ |
| 1854 | #define SYNC_UNVERSIONED 0x0100 /* Sync unversioned content */ |
| 1855 | #define SYNC_UV_REVERT 0x0200 /* Copy server unversioned to client */ |
| 1856 | #define SYNC_UV_TRACE 0x0400 /* Describe UV activities */ |
| 1857 | #define SYNC_UV_DRYRUN 0x0800 /* Do not actually exchange files */ |
| 1858 | #define SYNC_IFABLE 0x1000 /* Inability to sync is not fatal */ |
| 1859 | #define SYNC_CKIN_LOCK 0x2000 /* Lock the current check-in */ |
| 1860 | #define SYNC_NOHTTPCOMPRESS 0x4000 /* Do not compression HTTP messages */ |
| 1861 | #define SYNC_ALLURL 0x8000 /* The --all flag - sync to all URLs */ |
| 1862 | #endif |
| 1863 | |
| 1864 | /* |
| 1865 | ** Floating-point absolute value |
| 1866 | */ |
| @@ -2052,14 +2098,14 @@ | |
| 2052 | if( zSelfUrl ){ |
| 2053 | blob_appendf(&send, "pragma client-url %s\n", zSelfUrl); |
| 2054 | } |
| 2055 | } |
| 2056 | |
| 2057 | /* Request names of alternative repositories |
| 2058 | */ |
| 2059 | if( zAltPCode==0 ){ |
| 2060 | blob_appendf(&send, "pragma req-alt-repo\n"); |
| 2061 | } |
| 2062 | |
| 2063 | while( go ){ |
| 2064 | int newPhantom = 0; |
| 2065 | char *zRandomness; |
| @@ -2590,11 +2636,11 @@ | |
| 2590 | ** this client lacks the necessary permissions) then it sends a |
| 2591 | ** "uv-pull-only" pragma so that the client will know not to waste |
| 2592 | ** bandwidth trying to upload unversioned content. If the server |
| 2593 | ** does accept new unversioned content, it sends "uv-push-ok". |
| 2594 | */ |
| 2595 | if( syncFlags & SYNC_UNVERSIONED ){ |
| 2596 | if( blob_eq(&xfer.aToken[1], "uv-pull-only") ){ |
| 2597 | uvPullOnly = 1; |
| 2598 | if( syncFlags & SYNC_UV_REVERT ) uvDoPush = 1; |
| 2599 | }else if( blob_eq(&xfer.aToken[1], "uv-push-ok") ){ |
| 2600 | uvDoPush = 1; |
| @@ -2630,10 +2676,46 @@ | |
| 2630 | ** this pragma when its forbid-delta-manifests setting is true. |
| 2631 | */ |
| 2632 | else if( blob_eq(&xfer.aToken[1], "avoid-delta-manifests") ){ |
| 2633 | g.bAvoidDeltaManifests = 1; |
| 2634 | } |
| 2635 | }else |
| 2636 | |
| 2637 | /* error MESSAGE |
| 2638 | ** |
| 2639 | ** The server is reporting an error. The client will abandon |
| 2640 |
| --- src/xfer.c | |
| +++ src/xfer.c | |
| @@ -1209,10 +1209,11 @@ | |
| 1209 | char *zUuidList = 0; |
| 1210 | int nUuidList = 0; |
| 1211 | char **pzUuidList = 0; |
| 1212 | int *pnUuidList = 0; |
| 1213 | int uvCatalogSent = 0; |
| 1214 | int bSendLinks = 0; |
| 1215 | |
| 1216 | if( fossil_strcmp(PD("REQUEST_METHOD","POST"),"POST") ){ |
| 1217 | fossil_redirect_home(); |
| 1218 | } |
| 1219 | g.zLogin = "anonymous"; |
| @@ -1599,21 +1600,21 @@ | |
| 1600 | if( !g.perm.Private ){ |
| 1601 | server_private_xfer_not_authorized(); |
| 1602 | }else{ |
| 1603 | xfer.syncPrivate = 1; |
| 1604 | } |
| 1605 | }else |
| 1606 | |
| 1607 | /* pragma send-catalog |
| 1608 | ** |
| 1609 | ** The client wants to see igot cards for all known artifacts. |
| 1610 | ** This is used as part of "sync --verily" to help ensure that |
| 1611 | ** no artifacts have been missed on prior syncs. |
| 1612 | */ |
| 1613 | if( blob_eq(&xfer.aToken[1], "send-catalog") ){ |
| 1614 | xfer.resync = 0x7fffffff; |
| 1615 | }else |
| 1616 | |
| 1617 | /* pragma client-version VERSION ?DATE? ?TIME? |
| 1618 | ** |
| 1619 | ** The client announces to the server what version of Fossil it |
| 1620 | ** is running. The DATE and TIME are a pure numeric ISO8601 time |
| @@ -1625,11 +1626,11 @@ | |
| 1626 | xfer.remoteDate = atoi(blob_str(&xfer.aToken[3])); |
| 1627 | xfer.remoteTime = atoi(blob_str(&xfer.aToken[4])); |
| 1628 | @ pragma server-version %d(RELEASE_VERSION_NUMBER) \ |
| 1629 | @ %d(MANIFEST_NUMERIC_DATE) %d(MANIFEST_NUMERIC_TIME) |
| 1630 | } |
| 1631 | }else |
| 1632 | |
| 1633 | /* pragma uv-hash HASH |
| 1634 | ** |
| 1635 | ** The client wants to make sure that unversioned files are all synced. |
| 1636 | ** If the HASH does not match, send a complete catalog of |
| @@ -1648,11 +1649,11 @@ | |
| 1649 | @ pragma uv-pull-only |
| 1650 | } |
| 1651 | send_unversioned_catalog(&xfer); |
| 1652 | } |
| 1653 | uvCatalogSent = 1; |
| 1654 | }else |
| 1655 | |
| 1656 | /* pragma ci-lock CHECKIN-HASH CLIENT-ID |
| 1657 | ** |
| 1658 | ** The client wants to make non-branch commit against the check-in |
| 1659 | ** identified by CHECKIN-HASH. The server will remember this and |
| @@ -1708,11 +1709,11 @@ | |
| 1709 | db_protect_pop(); |
| 1710 | } |
| 1711 | if( db_get_boolean("forbid-delta-manifests",0) ){ |
| 1712 | @ pragma avoid-delta-manifests |
| 1713 | } |
| 1714 | }else |
| 1715 | |
| 1716 | /* pragma ci-unlock CLIENT-ID |
| 1717 | ** |
| 1718 | ** Remove any locks previously held by CLIENT-ID. Clients send this |
| 1719 | ** pragma with their own ID whenever they know that they no longer |
| @@ -1728,11 +1729,11 @@ | |
| 1729 | " WHERE name GLOB 'ci-lock-*'" |
| 1730 | " AND json_extract(value,'$.clientid')=%Q", |
| 1731 | blob_str(&xfer.aToken[2]) |
| 1732 | ); |
| 1733 | db_protect_pop(); |
| 1734 | }else |
| 1735 | |
| 1736 | /* pragma client-url URL |
| 1737 | ** |
| 1738 | ** This pragma is an informational notification to the server that |
| 1739 | ** their relationship could, in theory, be inverted by having the |
| @@ -1741,10 +1742,19 @@ | |
| 1742 | if( blob_eq(&xfer.aToken[1], "client-url") |
| 1743 | && xfer.nToken==3 |
| 1744 | && g.perm.Write |
| 1745 | ){ |
| 1746 | xfer_syncwith(blob_str(&xfer.aToken[2]), 1); |
| 1747 | }else |
| 1748 | |
| 1749 | /* pragma req-links |
| 1750 | ** |
| 1751 | ** The client sends this message to the server to ask the server |
| 1752 | ** to tell it about alternative repositories in the reply. |
| 1753 | */ |
| 1754 | if( blob_eq(&xfer.aToken[1], "req-links") ){ |
| 1755 | bSendLinks = 1; |
| 1756 | } |
| 1757 | |
| 1758 | }else |
| 1759 | |
| 1760 | /* Unknown message |
| @@ -1786,10 +1796,45 @@ | |
| 1796 | if( xfer.syncPrivate ) send_private(&xfer); |
| 1797 | } |
| 1798 | hook_expecting_more_artifacts(xfer.nGimmeSent?60:0); |
| 1799 | db_multi_exec("DROP TABLE onremote; DROP TABLE unk;"); |
| 1800 | manifest_crosslink_end(MC_PERMIT_HOOKS); |
| 1801 | |
| 1802 | /* Send URLs for alternative repositories for the same project, |
| 1803 | ** if requested by the client. */ |
| 1804 | if( bSendLinks && g.zBaseURL ){ |
| 1805 | Stmt q; |
| 1806 | db_prepare(&q, |
| 1807 | "WITH remote(mtime, url, arg) AS (\n" |
| 1808 | " SELECT mtime, substr(name,10), '{}' FROM config\n" |
| 1809 | " WHERE name GLOB 'syncwith:http*'\n" |
| 1810 | " UNION ALL\n" |
| 1811 | " SELECT mtime, substr(name,10), '{}' FROM config\n" |
| 1812 | " WHERE name GLOB 'syncfrom:http*'\n" |
| 1813 | " UNION ALL\n" |
| 1814 | " SELECT mtime, substr(name,9), '{\"type\":\"git\"}' FROM config\n" |
| 1815 | " WHERE name GLOB 'gitpush:*'\n" |
| 1816 | ")\n" |
| 1817 | "SELECT url, json_insert(arg,'$.src',%Q), max(mtime)\n" |
| 1818 | " FROM remote WHERE mtime>unixepoch('now','-1 month')\n" |
| 1819 | " GROUP BY url;", |
| 1820 | g.zBaseURL |
| 1821 | ); |
| 1822 | while( db_step(&q)==SQLITE_ROW ){ |
| 1823 | UrlData x; |
| 1824 | const char *zUrl = db_column_text(&q, 0); |
| 1825 | const char *zArg = db_column_text(&q, 1); |
| 1826 | i64 iMtime = db_column_int64(&q, 2); |
| 1827 | memset(&x, 0, sizeof(x)); |
| 1828 | url_parse_local(zUrl, URL_OMIT_USER, &x); |
| 1829 | if( x.name!=0 && sqlite3_strlike("%localhost%", x.name, 0)!=0 ){ |
| 1830 | @ pragma link %F(x.canonical) %F(zArg) %lld(iMtime) |
| 1831 | } |
| 1832 | url_unparse(&x); |
| 1833 | } |
| 1834 | db_finalize(&q); |
| 1835 | } |
| 1836 | |
| 1837 | /* Send the server timestamp last, in case prior processing happened |
| 1838 | ** to use up a significant fraction of our time window. |
| 1839 | */ |
| 1840 | zNow = db_text(0, "SELECT strftime('%%Y-%%m-%%dT%%H:%%M:%%S', 'now')"); |
| @@ -1842,25 +1887,26 @@ | |
| 1887 | |
| 1888 | #if INTERFACE |
| 1889 | /* |
| 1890 | ** Flag options for controlling client_sync() |
| 1891 | */ |
| 1892 | #define SYNC_PUSH 0x00001 /* push content client to server */ |
| 1893 | #define SYNC_PULL 0x00002 /* pull content server to client */ |
| 1894 | #define SYNC_CLONE 0x00004 /* clone the repository */ |
| 1895 | #define SYNC_PRIVATE 0x00008 /* Also transfer private content */ |
| 1896 | #define SYNC_VERBOSE 0x00010 /* Extra diagnostics */ |
| 1897 | #define SYNC_RESYNC 0x00020 /* --verily */ |
| 1898 | #define SYNC_FROMPARENT 0x00040 /* Pull from the parent project */ |
| 1899 | #define SYNC_UNVERSIONED 0x00100 /* Sync unversioned content */ |
| 1900 | #define SYNC_UV_REVERT 0x00200 /* Copy server unversioned to client */ |
| 1901 | #define SYNC_UV_TRACE 0x00400 /* Describe UV activities */ |
| 1902 | #define SYNC_UV_DRYRUN 0x00800 /* Do not actually exchange files */ |
| 1903 | #define SYNC_IFABLE 0x01000 /* Inability to sync is not fatal */ |
| 1904 | #define SYNC_CKIN_LOCK 0x02000 /* Lock the current check-in */ |
| 1905 | #define SYNC_NOHTTPCOMPRESS 0x04000 /* Do not compression HTTP messages */ |
| 1906 | #define SYNC_ALLURL 0x08000 /* The --all flag - sync to all URLs */ |
| 1907 | #define SYNC_SHARE_LINKS 0x10000 /* Request alternate repo links */ |
| 1908 | #endif |
| 1909 | |
| 1910 | /* |
| 1911 | ** Floating-point absolute value |
| 1912 | */ |
| @@ -2052,14 +2098,14 @@ | |
| 2098 | if( zSelfUrl ){ |
| 2099 | blob_appendf(&send, "pragma client-url %s\n", zSelfUrl); |
| 2100 | } |
| 2101 | } |
| 2102 | |
| 2103 | /* Request URLs of alternative repositories |
| 2104 | */ |
| 2105 | if( zAltPCode==0 && (syncFlags & SYNC_SHARE_LINKS)!=0 ){ |
| 2106 | blob_appendf(&send, "pragma req-links\n"); |
| 2107 | } |
| 2108 | |
| 2109 | while( go ){ |
| 2110 | int newPhantom = 0; |
| 2111 | char *zRandomness; |
| @@ -2590,11 +2636,11 @@ | |
| 2636 | ** this client lacks the necessary permissions) then it sends a |
| 2637 | ** "uv-pull-only" pragma so that the client will know not to waste |
| 2638 | ** bandwidth trying to upload unversioned content. If the server |
| 2639 | ** does accept new unversioned content, it sends "uv-push-ok". |
| 2640 | */ |
| 2641 | else if( syncFlags & SYNC_UNVERSIONED ){ |
| 2642 | if( blob_eq(&xfer.aToken[1], "uv-pull-only") ){ |
| 2643 | uvPullOnly = 1; |
| 2644 | if( syncFlags & SYNC_UV_REVERT ) uvDoPush = 1; |
| 2645 | }else if( blob_eq(&xfer.aToken[1], "uv-push-ok") ){ |
| 2646 | uvDoPush = 1; |
| @@ -2630,10 +2676,46 @@ | |
| 2676 | ** this pragma when its forbid-delta-manifests setting is true. |
| 2677 | */ |
| 2678 | else if( blob_eq(&xfer.aToken[1], "avoid-delta-manifests") ){ |
| 2679 | g.bAvoidDeltaManifests = 1; |
| 2680 | } |
| 2681 | |
| 2682 | /* pragma link URL ARG MTIME |
| 2683 | ** |
| 2684 | ** The server has sent the URL for a link to another repository. |
| 2685 | ** Record this as a link:URL entry in the config table. |
| 2686 | */ |
| 2687 | else if( blob_eq(&xfer.aToken[1], "link") |
| 2688 | && xfer.nToken==5 |
| 2689 | && (syncFlags & SYNC_SHARE_LINKS)!=0 |
| 2690 | ){ |
| 2691 | UrlData x; |
| 2692 | char *zUrl = blob_str(&xfer.aToken[2]); |
| 2693 | char *zArg = blob_str(&xfer.aToken[3]); |
| 2694 | i64 iTime = strtoll(blob_str(&xfer.aToken[4]),0,0); |
| 2695 | memset(&x, 0, sizeof(x)); |
| 2696 | defossilize(zUrl); |
| 2697 | defossilize(zArg); |
| 2698 | url_parse_local(zUrl, URL_OMIT_USER, &x); |
| 2699 | if( x.protocol |
| 2700 | && strncmp(x.protocol,"http",4)==0 |
| 2701 | && iTime>0 |
| 2702 | ){ |
| 2703 | db_unprotect(PROTECT_CONFIG); |
| 2704 | db_multi_exec( |
| 2705 | "INSERT INTO config(name,value,mtime)\n" |
| 2706 | " VALUES('link:%q',%Q,%lld)\n" |
| 2707 | " ON CONFLICT DO UPDATE\n" |
| 2708 | " SET value=excluded.value, mtime=excluded.mtime\n" |
| 2709 | " WHERE mtime<excluded.mtime;", |
| 2710 | zUrl, zArg, iTime |
| 2711 | ); |
| 2712 | db_protect_pop(); |
| 2713 | } |
| 2714 | url_unparse(&x); |
| 2715 | } |
| 2716 | |
| 2717 | }else |
| 2718 | |
| 2719 | /* error MESSAGE |
| 2720 | ** |
| 2721 | ** The server is reporting an error. The client will abandon |
| 2722 |