Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Limit the amount of unversioned content sent with each HTTP request. Also: fix up source code lines in xfer.c to be less than 80 characters long. |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | unversioned-files |
Files: | files | file ages | folders |
SHA1: | faa73ae0c4a82af20e48134750dddf21 |
User & Date: | drh 2016-08-17 13:37:43 |
Context
2016-08-17
| ||
14:40 | Update the sync protocol documentation to describe the unversioned file sync. check-in: d7809493 user: drh tags: unversioned-files | |
13:37 | Limit the amount of unversioned content sent with each HTTP request. Also: fix up source code lines in xfer.c to be less than 80 characters long. check-in: faa73ae0 user: drh tags: unversioned-files | |
11:53 | The admin and setup users automatically get the write-unversioned ('y') permission. check-in: 0480400c user: drh tags: unversioned-files | |
Changes
Changes to src/xfer.c.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 ... 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 ... 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 ... 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 ... 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 .... 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 .... 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 .... 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 .... 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 .... 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 .... 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 .... 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 .... 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 .... 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 .... 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 |
/* ** Copyright (c) 2007 D. Richard Hipp ** ** This program is free software; you can redistribute it and/or ** modify it under the terms of the Simplified BSD License (also ** known as the "2-Clause License" or "FreeBSD License".) ** This program is distributed in the hope that it will be useful, ** but without any warranty; without even the implied warranty of ** merchantability or fitness for a particular purpose. ** ** Author contact information: ** drh@hwaci.com ** http://www.hwaci.com/drh/ ................................................................................ ** ** If the 0x0004 bit of FLAGS is set, that means the CONTENT is omitted. ** The sender might have omitted the content because it is too big to ** transmit, or because it is unchanged and this record exists purely ** to update the MTIME. */ static void xfer_accept_unversioned_file(Xfer *pXfer, int isWriter){ sqlite3_int64 mtime; /* The MTIME */ Blob *pHash; /* The HASH value */ int sz; /* The SIZE */ int flags; /* The FLAGS */ Blob content; /* The CONTENT */ Blob hash; /* Hash computed from CONTENT to compare with HASH */ Blob x; /* Compressed content */ Stmt q; /* SQL statements for comparison and insert */ int isDelete; /* HASH is "-" indicating this is a delete operation */ int nullContent; /* True of CONTENT is NULL */ int iStatus; /* Result from unversioned_status() */ pHash = &pXfer->aToken[3]; if( pXfer->nToken==5 || !blob_is_filename(&pXfer->aToken[1]) || !blob_is_int64(&pXfer->aToken[2], &mtime) || (!blob_eq(pHash,"-") && !blob_is_uuid(pHash)) || !blob_is_int(&pXfer->aToken[4], &sz) ................................................................................ /* The isWriter flag must be true in order to land the new file */ if( !isWriter ) goto end_accept_unversioned_file; /* Check to see if current content really should be overwritten. Ideally, ** a uvfile card should never have been sent unless the overwrite should ** occur. But do not trust the sender. Double-check. */ iStatus = unversioned_status(blob_str(&pXfer->aToken[1]), mtime, blob_str(pHash)); if( iStatus>=3 ) goto end_accept_unversioned_file; /* Store the content */ isDelete = blob_eq(pHash, "-"); if( isDelete ){ db_prepare(&q, "UPDATE unversioned" " SET rcvid=:rcvid, mtime=:mtime, hash=NULL, sz=0, encoding=0, content=NULL" " WHERE name=:name" ); db_bind_int(&q, ":rcvid", g.rcvid); }else if( iStatus==4 ){ db_prepare(&q, "UPDATE unversioned SET mtime=:mtime WHERE name=:name"); }else{ db_prepare(&q, "REPLACE INTO unversioned(name, rcvid, mtime, hash, sz, encoding, content)" " VALUES(:name,:rcvid,:mtime,:hash,:sz,:encoding,:content)" ); db_bind_int(&q, ":rcvid", g.rcvid); db_bind_text(&q, ":hash", blob_str(pHash)); db_bind_int(&q, ":sz", blob_size(&content)); if( !nullContent ){ blob_compress(&content, &x); ................................................................................ /* ** Send the unversioned file identified by zName by generating the ** appropriate "uvfile" card. ** ** uvfile NAME MTIME HASH SIZE FLAGS \n CONTENT ** ** If the noContent flag is set, omit the CONTENT and set the 0x0004 flag in FLAGS. */ static void send_unversioned_file(Xfer *pXfer, const char *zName, int noContent){ Stmt q1; if( noContent ){ db_prepare(&q1, "SELECT mtime, hash, encoding, sz FROM unversioned WHERE name=%Q", zName ); }else{ db_prepare(&q1, "SELECT mtime, hash, encoding, sz, content FROM unversioned WHERE name=%Q", zName ); } if( db_step(&q1)==SQLITE_ROW ){ sqlite3_int64 mtime = db_column_int64(&q1, 0); const char *zHash = db_column_text(&q1, 1); blob_appendf(pXfer->pOut, "uvfile %s %lld", zName, mtime); if( zHash==0 ){ blob_append(pXfer->pOut, " - 0 1\n", -1); }else if( noContent ){ blob_appendf(pXfer->pOut, " %s %d 4\n", zHash, db_column_int(&q1,3)); }else{ Blob content; blob_init(&content, 0, 0); db_column_blob(&q1, 4, &content); if( db_column_int(&q1, 2) ){ blob_uncompress(&content, &content); } blob_appendf(pXfer->pOut, " %s %d 0\n", zHash, blob_size(&content)); blob_append(pXfer->pOut, blob_buffer(&content), blob_size(&content)); #if 0 if( blob_buffer(pXfer->pOut)[blob_size(pXfer->pOut)-1]!='\n' ){ blob_append(pXfer->pOut, "\n", 1); } #endif blob_reset(&content); } } db_finalize(&q1); } /* ** Send a gimme message for every phantom. ................................................................................ */ int check_login(Blob *pLogin, Blob *pNonce, Blob *pSig){ Stmt q; int rc = -1; char *zLogin = blob_terminate(pLogin); defossilize(zLogin); if( fossil_strcmp(zLogin, "nobody")==0 || fossil_strcmp(zLogin,"anonymous")==0 ){ return 0; /* Anybody is allowed to sync as "nobody" or "anonymous" */ } if( fossil_strcmp(P("REMOTE_USER"), zLogin)==0 && db_get_boolean("remote_user_ok",0) ){ return 0; /* Accept Basic Authorization */ } db_prepare(&q, ................................................................................ /* pragma uv-hash HASH ** ** The client wants to make sure that unversioned files are all synced. ** If the HASH does not match, send a complete catalog of ** "uvigot" cards. */ if( blob_eq(&xfer.aToken[1], "uv-hash") && blob_is_uuid(&xfer.aToken[2]) ){ if( g.perm.Read && g.perm.WrUnver ){ @ pragma uv-push-ok send_unversioned_catalog(&xfer); }else if( g.perm.Read ){ @ pragma uv-pull-only send_unversioned_catalog(&xfer); } ................................................................................ int nRoundtrip= 0; /* Number of HTTP requests */ int nArtifactSent = 0; /* Total artifacts sent */ int nArtifactRcvd = 0; /* Total artifacts received */ const char *zOpType = 0;/* Push, Pull, Sync, Clone */ double rSkew = 0.0; /* Maximum time skew */ int uvHashSent = 0; /* The "pragma uv-hash" message has been sent */ int uvStatus = 0; /* 0: no I/O. 1: pull-only 2: push-and-pull */ int uvDoPush = 0; /* If true, generate uvfile messages to send to server */ sqlite3_int64 mtime; /* Modification time on a UV file */ if( db_get_boolean("dont-push", 0) ) syncFlags &= ~SYNC_PUSH; if( (syncFlags & (SYNC_PUSH|SYNC_PULL|SYNC_CLONE|SYNC_UNVERSIONED))==0 && configRcvMask==0 && configSendMask==0 ) return 0; if( syncFlags & SYNC_FROMPARENT ){ configRcvMask = 0; ................................................................................ } /* Send unversioned files present here on the client but missing or ** obsolete on the server. */ if( uvDoPush ){ Stmt uvq; assert( (syncFlags & SYNC_UNVERSIONED)!=0 ); assert( uvStatus==2 ); db_prepare(&uvq, "SELECT name, mtimeOnly FROM uv_tosend"); while( db_step(&uvq)==SQLITE_ROW ){ send_unversioned_file(&xfer, db_column_text(&uvq,0), db_column_int(&uvq,1)); nCardSent++; nArtifactSent++; } db_finalize(&uvq); uvDoPush = 0; } /* Append randomness to the end of the message. This makes all ** messages unique so that that the login-card nonce will always ** be unique. */ zRandomness = db_text(0, "SELECT hex(randomblob(20))"); ................................................................................ nCardSent++; } if( syncFlags & SYNC_PUSH ){ blob_appendf(&send, "push %s %s\n", zSCode, zPCode); nCardSent++; } go = 0; /* Process the reply that came back from the server */ while( blob_line(&recv, &xfer.line) ){ if( blob_buffer(&xfer.line)[0]=='#' ){ const char *zLine = blob_buffer(&xfer.line); if( memcmp(zLine, "# timestamp ", 12)==0 ){ char zTime[20]; double rDiff; sqlite3_snprintf(sizeof(zTime), zTime, "%.19s", &zLine[12]); rDiff = db_double(9e99, "SELECT julianday('%q') - %.17g", zTime, rArrivalTime); if( rDiff>9e98 || rDiff<-9e98 ) rDiff = 0.0; if( rDiff*24.0*3600.0 >= -(blob_size(&recv)/5000.0 + 20) ) rDiff = 0.0; if( fossil_fabs(rDiff)>fossil_fabs(rSkew) ) rSkew = rDiff; } nCardRcvd++; continue; } xfer.nToken = blob_tokenize(&xfer.line, xfer.aToken, count(xfer.aToken)); nCardRcvd++; ................................................................................ /* uvfile NAME MTIME HASH SIZE FLAGS \n CONTENT ** ** Accept an unversioned file from the client. */ if( blob_eq(&xfer.aToken[0], "uvfile") ){ xfer_accept_unversioned_file(&xfer, 1); nArtifactRcvd++; }else /* gimme UUID ** ** Server is requesting a file. If the file is a manifest, assume ** that the server will also want to know all of the content files ** associated with the manifest and send those too. ................................................................................ /* uvigot NAME MTIME HASH SIZE ** ** Server announces that it has a particular unversioned file. The ** server will only send this card if the client had previously sent ** a "pragma uv-hash" card with a hash that does not match. ** ** If the identified file needs to be transferred, then setup for the ** transfer. Generate a "uvgimme" card in the reply if the server version ** is newer than the client. Generate a "uvfile" card if the client version ** is newer than the server. If HASH is "-" (indicating that the file has ** been deleted) and MTIME is newer, then do the deletion. */ if( xfer.nToken==5 && blob_eq(&xfer.aToken[0], "uvigot") && blob_is_filename(&xfer.aToken[1]) && blob_is_int64(&xfer.aToken[2], &mtime) && blob_is_int(&xfer.aToken[4], &size) && (blob_eq(&xfer.aToken[3],"-") || blob_is_uuid(&xfer.aToken[3])) ................................................................................ int iStatus; if( uvStatus==0 ) uvStatus = 2; iStatus = unversioned_status(zName, mtime, zHash); if( iStatus<=1 ){ if( zHash[0]!='-' ){ blob_appendf(xfer.pOut, "uvgimme %s\n", zName); nCardSent++; }else if( iStatus==1 ){ db_multi_exec( "UPDATE unversioned" " SET mtime=%lld, hash=NULL, sz=0, encoding=0, content=NULL" " WHERE name=%Q", mtime, zName ); db_unset("uv-hash", 0); ................................................................................ "UPDATE unversioned SET mtime=%lld WHERE name=%Q", mtime, zName ); db_unset("uv-hash", 0); } if( iStatus<=3 ){ db_multi_exec("DELETE FROM uv_tosend WHERE name=%Q", zName); }else if( iStatus==4 ){ db_multi_exec("UPDATE uv_tosend SET mtimeOnly=1 WHERE name=%Q", zName); }else if( iStatus==5 ){ db_multi_exec("REPLACE INTO uv_tosend(name,mtimeOnly) VALUES(%Q,0)", zName); } }else /* push SERVERCODE PRODUCTCODE ** ** Should only happen in response to a clone. This message tells ** the client what product to use for the new database. ................................................................................ ** ** If the "login failed" message is seen, clear the sync password prior ** to the next cycle. */ if( blob_eq(&xfer.aToken[0],"message") && xfer.nToken==2 ){ char *zMsg = blob_terminate(&xfer.aToken[1]); defossilize(zMsg); if( (syncFlags & SYNC_PUSH) && zMsg && sqlite3_strglob("pull only *", zMsg)==0 ){ syncFlags &= ~SYNC_PUSH; zMsg = 0; } if( zMsg && zMsg[0] ){ fossil_force_newline(); fossil_print("Server says: %s\n", zMsg); } ................................................................................ /* If we have one or more files queued to send, then go ** another round */ if( xfer.nFileSent+xfer.nDeltaSent>0 || uvDoPush ){ go = 1; } /* If this is a clone, the go at least two rounds */ if( (syncFlags & SYNC_CLONE)!=0 && nCycle==1 ) go = 1; /* Stop the cycle if the server sends a "clone_seqno 0" card and ** we have gone at least two rounds. Always go at least two rounds ** on a clone in order to be sure to retrieve the configuration |
| | | | | | | | | | | | | > | > | | > | > > > > > | > > > > > > > > > | | | | | | | | | | | | | | < < < < < | > | > > | > > | > > > | > | > > | > > | > > > | | | | > > | | > > | > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 ... 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 ... 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 ... 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 ... 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 .... 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 .... 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 .... 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 .... 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 .... 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 .... 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 .... 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 .... 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 .... 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 .... 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 |
/* ** Copyright (c) 2007 D. Richard Hipp ** ** This program is free software; you can redistribute it and/or ** modify it under the terms of the Simplified BSD License (also ** known as the "2-Clause License" or "FreeBSD License".) ** ** This program is distributed in the hope that it will be useful, ** but without any warranty; without even the implied warranty of ** merchantability or fitness for a particular purpose. ** ** Author contact information: ** drh@hwaci.com ** http://www.hwaci.com/drh/ ................................................................................ ** ** If the 0x0004 bit of FLAGS is set, that means the CONTENT is omitted. ** The sender might have omitted the content because it is too big to ** transmit, or because it is unchanged and this record exists purely ** to update the MTIME. */ static void xfer_accept_unversioned_file(Xfer *pXfer, int isWriter){ sqlite3_int64 mtime; /* The MTIME */ Blob *pHash; /* The HASH value */ int sz; /* The SIZE */ int flags; /* The FLAGS */ Blob content; /* The CONTENT */ Blob hash; /* Hash computed from CONTENT to compare with HASH */ Blob x; /* Compressed content */ Stmt q; /* SQL statements for comparison and insert */ int isDelete; /* HASH is "-" indicating this is a delete */ int nullContent; /* True of CONTENT is NULL */ int iStatus; /* Result from unversioned_status() */ pHash = &pXfer->aToken[3]; if( pXfer->nToken==5 || !blob_is_filename(&pXfer->aToken[1]) || !blob_is_int64(&pXfer->aToken[2], &mtime) || (!blob_eq(pHash,"-") && !blob_is_uuid(pHash)) || !blob_is_int(&pXfer->aToken[4], &sz) ................................................................................ /* The isWriter flag must be true in order to land the new file */ if( !isWriter ) goto end_accept_unversioned_file; /* Check to see if current content really should be overwritten. Ideally, ** a uvfile card should never have been sent unless the overwrite should ** occur. But do not trust the sender. Double-check. */ iStatus = unversioned_status(blob_str(&pXfer->aToken[1]), mtime, blob_str(pHash)); if( iStatus>=3 ) goto end_accept_unversioned_file; /* Store the content */ isDelete = blob_eq(pHash, "-"); if( isDelete ){ db_prepare(&q, "UPDATE unversioned" " SET rcvid=:rcvid, mtime=:mtime, hash=NULL," " sz=0, encoding=0, content=NULL" " WHERE name=:name" ); db_bind_int(&q, ":rcvid", g.rcvid); }else if( iStatus==4 ){ db_prepare(&q, "UPDATE unversioned SET mtime=:mtime WHERE name=:name"); }else{ db_prepare(&q, "REPLACE INTO unversioned(name,rcvid,mtime,hash,sz,encoding,content)" " VALUES(:name,:rcvid,:mtime,:hash,:sz,:encoding,:content)" ); db_bind_int(&q, ":rcvid", g.rcvid); db_bind_text(&q, ":hash", blob_str(pHash)); db_bind_int(&q, ":sz", blob_size(&content)); if( !nullContent ){ blob_compress(&content, &x); ................................................................................ /* ** Send the unversioned file identified by zName by generating the ** appropriate "uvfile" card. ** ** uvfile NAME MTIME HASH SIZE FLAGS \n CONTENT ** ** If the noContent flag is set, omit the CONTENT and set the 0x0004 ** flag in FLAGS. */ static void send_unversioned_file( Xfer *pXfer, /* Transfer context */ const char *zName, /* Name of unversioned file to be sent */ int noContent /* True to omit the content */ ){ Stmt q1; if( blob_size(pXfer->pOut)>=pXfer->mxSend ) noContent = 1; if( noContent ){ db_prepare(&q1, "SELECT mtime, hash, encoding, sz FROM unversioned WHERE name=%Q", zName ); }else{ db_prepare(&q1, "SELECT mtime, hash, encoding, sz, content FROM unversioned" " WHERE name=%Q", zName ); } if( db_step(&q1)==SQLITE_ROW ){ sqlite3_int64 mtime = db_column_int64(&q1, 0); const char *zHash = db_column_text(&q1, 1); if( blob_size(pXfer->pOut)>=pXfer->mxSend ){ /* If we have already reached the send size limit, send a (short) ** uvigot card rather than a uvfile card. This only happens on the ** server side. The uvigot card will provoke the client to resend ** another uvgimme on the next cycle. */ blob_appendf(pXfer->pOut, "uvigot %s %lld %s %d\n", zName, mtime, zHash, db_column_int(&q1,3)); }else{ blob_appendf(pXfer->pOut, "uvfile %s %lld", zName, mtime); if( zHash==0 ){ blob_append(pXfer->pOut, " - 0 1\n", -1); }else if( noContent ){ blob_appendf(pXfer->pOut, " %s %d 4\n", zHash, db_column_int(&q1,3)); }else{ Blob content; blob_init(&content, 0, 0); db_column_blob(&q1, 4, &content); if( db_column_int(&q1, 2) ){ blob_uncompress(&content, &content); } blob_appendf(pXfer->pOut, " %s %d 0\n", zHash, blob_size(&content)); blob_append(pXfer->pOut, blob_buffer(&content), blob_size(&content)); blob_reset(&content); } } } db_finalize(&q1); } /* ** Send a gimme message for every phantom. ................................................................................ */ int check_login(Blob *pLogin, Blob *pNonce, Blob *pSig){ Stmt q; int rc = -1; char *zLogin = blob_terminate(pLogin); defossilize(zLogin); if( fossil_strcmp(zLogin, "nobody")==0 || fossil_strcmp(zLogin,"anonymous")==0 ){ return 0; /* Anybody is allowed to sync as "nobody" or "anonymous" */ } if( fossil_strcmp(P("REMOTE_USER"), zLogin)==0 && db_get_boolean("remote_user_ok",0) ){ return 0; /* Accept Basic Authorization */ } db_prepare(&q, ................................................................................ /* pragma uv-hash HASH ** ** The client wants to make sure that unversioned files are all synced. ** If the HASH does not match, send a complete catalog of ** "uvigot" cards. */ if( blob_eq(&xfer.aToken[1], "uv-hash") && blob_is_uuid(&xfer.aToken[2]) ){ if( g.perm.Read && g.perm.WrUnver ){ @ pragma uv-push-ok send_unversioned_catalog(&xfer); }else if( g.perm.Read ){ @ pragma uv-pull-only send_unversioned_catalog(&xfer); } ................................................................................ int nRoundtrip= 0; /* Number of HTTP requests */ int nArtifactSent = 0; /* Total artifacts sent */ int nArtifactRcvd = 0; /* Total artifacts received */ const char *zOpType = 0;/* Push, Pull, Sync, Clone */ double rSkew = 0.0; /* Maximum time skew */ int uvHashSent = 0; /* The "pragma uv-hash" message has been sent */ int uvStatus = 0; /* 0: no I/O. 1: pull-only 2: push-and-pull */ int uvDoPush = 0; /* Generate uvfile messages to send to server */ int nUvGimmeSent = 0; /* Number of uvgimme cards sent on this cycle */ int nUvFileRcvd = 0; /* Number of uvfile cards received on this cycle */ sqlite3_int64 mtime; /* Modification time on a UV file */ if( db_get_boolean("dont-push", 0) ) syncFlags &= ~SYNC_PUSH; if( (syncFlags & (SYNC_PUSH|SYNC_PULL|SYNC_CLONE|SYNC_UNVERSIONED))==0 && configRcvMask==0 && configSendMask==0 ) return 0; if( syncFlags & SYNC_FROMPARENT ){ configRcvMask = 0; ................................................................................ } /* Send unversioned files present here on the client but missing or ** obsolete on the server. */ if( uvDoPush ){ Stmt uvq; int rc = SQLITE_OK; assert( (syncFlags & SYNC_UNVERSIONED)!=0 ); assert( uvStatus==2 ); db_prepare(&uvq, "SELECT name, mtimeOnly FROM uv_tosend"); while( (rc = db_step(&uvq))==SQLITE_ROW ){ const char *zName = db_column_text(&uvq, 0); send_unversioned_file(&xfer, zName, db_column_int(&uvq,1)); nCardSent++; nArtifactSent++; db_multi_exec("DELETE FROM uv_tosend WHERE name=%Q", zName); if( blob_size(xfer.pOut)>xfer.mxSend ) break; } db_finalize(&uvq); if( rc==SQLITE_DONE ) uvDoPush = 0; } /* Append randomness to the end of the message. This makes all ** messages unique so that that the login-card nonce will always ** be unique. */ zRandomness = db_text(0, "SELECT hex(randomblob(20))"); ................................................................................ nCardSent++; } if( syncFlags & SYNC_PUSH ){ blob_appendf(&send, "push %s %s\n", zSCode, zPCode); nCardSent++; } go = 0; nUvGimmeSent = 0; nUvFileRcvd = 0; /* Process the reply that came back from the server */ while( blob_line(&recv, &xfer.line) ){ if( blob_buffer(&xfer.line)[0]=='#' ){ const char *zLine = blob_buffer(&xfer.line); if( memcmp(zLine, "# timestamp ", 12)==0 ){ char zTime[20]; double rDiff; sqlite3_snprintf(sizeof(zTime), zTime, "%.19s", &zLine[12]); rDiff = db_double(9e99, "SELECT julianday('%q') - %.17g", zTime, rArrivalTime); if( rDiff>9e98 || rDiff<-9e98 ) rDiff = 0.0; if( rDiff*24.0*3600.0 >= -(blob_size(&recv)/5000.0 + 20) ){ rDiff = 0.0; } if( fossil_fabs(rDiff)>fossil_fabs(rSkew) ) rSkew = rDiff; } nCardRcvd++; continue; } xfer.nToken = blob_tokenize(&xfer.line, xfer.aToken, count(xfer.aToken)); nCardRcvd++; ................................................................................ /* uvfile NAME MTIME HASH SIZE FLAGS \n CONTENT ** ** Accept an unversioned file from the client. */ if( blob_eq(&xfer.aToken[0], "uvfile") ){ xfer_accept_unversioned_file(&xfer, 1); nArtifactRcvd++; nUvFileRcvd++; }else /* gimme UUID ** ** Server is requesting a file. If the file is a manifest, assume ** that the server will also want to know all of the content files ** associated with the manifest and send those too. ................................................................................ /* uvigot NAME MTIME HASH SIZE ** ** Server announces that it has a particular unversioned file. The ** server will only send this card if the client had previously sent ** a "pragma uv-hash" card with a hash that does not match. ** ** If the identified file needs to be transferred, then setup for the ** transfer. Generate a "uvgimme" card in the reply if the server ** version is newer than the client. Generate a "uvfile" card if ** the client version is newer than the server. If HASH is "-" ** (indicating that the file has been deleted) and MTIME is newer, ** then do the deletion. */ if( xfer.nToken==5 && blob_eq(&xfer.aToken[0], "uvigot") && blob_is_filename(&xfer.aToken[1]) && blob_is_int64(&xfer.aToken[2], &mtime) && blob_is_int(&xfer.aToken[4], &size) && (blob_eq(&xfer.aToken[3],"-") || blob_is_uuid(&xfer.aToken[3])) ................................................................................ int iStatus; if( uvStatus==0 ) uvStatus = 2; iStatus = unversioned_status(zName, mtime, zHash); if( iStatus<=1 ){ if( zHash[0]!='-' ){ blob_appendf(xfer.pOut, "uvgimme %s\n", zName); nCardSent++; nUvGimmeSent++; }else if( iStatus==1 ){ db_multi_exec( "UPDATE unversioned" " SET mtime=%lld, hash=NULL, sz=0, encoding=0, content=NULL" " WHERE name=%Q", mtime, zName ); db_unset("uv-hash", 0); ................................................................................ "UPDATE unversioned SET mtime=%lld WHERE name=%Q", mtime, zName ); db_unset("uv-hash", 0); } if( iStatus<=3 ){ db_multi_exec("DELETE FROM uv_tosend WHERE name=%Q", zName); }else if( iStatus==4 ){ db_multi_exec("UPDATE uv_tosend SET mtimeOnly=1 WHERE name=%Q",zName); }else if( iStatus==5 ){ db_multi_exec("REPLACE INTO uv_tosend(name,mtimeOnly) VALUES(%Q,0)", zName); } }else /* push SERVERCODE PRODUCTCODE ** ** Should only happen in response to a clone. This message tells ** the client what product to use for the new database. ................................................................................ ** ** If the "login failed" message is seen, clear the sync password prior ** to the next cycle. */ if( blob_eq(&xfer.aToken[0],"message") && xfer.nToken==2 ){ char *zMsg = blob_terminate(&xfer.aToken[1]); defossilize(zMsg); if( (syncFlags & SYNC_PUSH) && zMsg && sqlite3_strglob("pull only *", zMsg)==0 ){ syncFlags &= ~SYNC_PUSH; zMsg = 0; } if( zMsg && zMsg[0] ){ fossil_force_newline(); fossil_print("Server says: %s\n", zMsg); } ................................................................................ /* If we have one or more files queued to send, then go ** another round */ if( xfer.nFileSent+xfer.nDeltaSent>0 || uvDoPush ){ go = 1; } /* Continue looping as long as new uvfile cards are being received ** and uvgimme cards are being sent. */ if( nUvGimmeSent>0 && nUvFileRcvd>0 ) go = 1; /* If this is a clone, the go at least two rounds */ if( (syncFlags & SYNC_CLONE)!=0 && nCycle==1 ) go = 1; /* Stop the cycle if the server sends a "clone_seqno 0" card and ** we have gone at least two rounds. Always go at least two rounds ** on a clone in order to be sure to retrieve the configuration |