Fossil SCM

If either side sends a gimme for a private artifact, reply with a private igot card to let the requestor know that the artifact is private. Other changes to help make this work are the new server-version pragma and adding date and time numbers to the client-version and server-version pragmas. The auto-shun setting now defaults to off.

drh 2020-04-13 12:39 sync-improvements
Commit 050cd0194324fde35e80ca2acfe232da7e07f1de7116e49a060a53d556a2a19a
+18 -3
--- src/mkversion.c
+++ src/mkversion.c
@@ -8,10 +8,11 @@
88
** Note that the manifest.uuid and manifest files are generated by Fossil.
99
*/
1010
#include <stdio.h>
1111
#include <string.h>
1212
#include <stdlib.h>
13
+#include <ctype.h>
1314
1415
static FILE *open_for_reading(const char *zFilename){
1516
FILE *f = fopen(zFilename, "r");
1617
if( f==0 ){
1718
fprintf(stderr, "cannot open \"%s\" for reading\n", zFilename);
@@ -46,14 +47,28 @@
4647
*z = 0;
4748
printf("#define MANIFEST_UUID \"%s\"\n",b);
4849
printf("#define MANIFEST_VERSION \"[%10.10s]\"\n",b);
4950
m = open_for_reading(argv[2]);
5051
while(b == fgets(b, sizeof(b)-1,m)){
51
- if(0 == strncmp("D ",b,2)){
52
- printf("#define MANIFEST_DATE \"%.10s %.8s\"\n",b+2,b+13);
53
- printf("#define MANIFEST_YEAR \"%.4s\"\n",b+2);
52
+ if(0 == strncmp("D ",b,2)){
53
+ int k, n;
54
+ char zDateNum[30];
55
+ printf("#define MANIFEST_DATE \"%.10s %.8s\"\n",b+2,b+13);
56
+ printf("#define MANIFEST_YEAR \"%.4s\"\n",b+2);
57
+ n = 0;
58
+ for(k=0; k<10; k++){
59
+ if( isdigit(b[k+2]) ) zDateNum[n++] = b[k+2];
60
+ }
61
+ zDateNum[n] = 0;
62
+ printf("#define MANIFEST_NUMERIC_DATE %s\n", zDateNum);
63
+ n = 0;
64
+ for(k=0; k<8; k++){
65
+ if( isdigit(b[k+13]) ) zDateNum[n++] = b[k+13];
5466
}
67
+ zDateNum[n] = 0;
68
+ printf("#define MANIFEST_NUMERIC_TIME %s\n", zDateNum);
69
+ }
5570
}
5671
fclose(m);
5772
v = open_for_reading(argv[3]);
5873
if( fgets(b, sizeof(b)-1,v)==0 ){
5974
fprintf(stderr, "malformed VERSION file: %s\n", argv[3]);
6075
--- src/mkversion.c
+++ src/mkversion.c
@@ -8,10 +8,11 @@
8 ** Note that the manifest.uuid and manifest files are generated by Fossil.
9 */
10 #include <stdio.h>
11 #include <string.h>
12 #include <stdlib.h>
 
13
14 static FILE *open_for_reading(const char *zFilename){
15 FILE *f = fopen(zFilename, "r");
16 if( f==0 ){
17 fprintf(stderr, "cannot open \"%s\" for reading\n", zFilename);
@@ -46,14 +47,28 @@
46 *z = 0;
47 printf("#define MANIFEST_UUID \"%s\"\n",b);
48 printf("#define MANIFEST_VERSION \"[%10.10s]\"\n",b);
49 m = open_for_reading(argv[2]);
50 while(b == fgets(b, sizeof(b)-1,m)){
51 if(0 == strncmp("D ",b,2)){
52 printf("#define MANIFEST_DATE \"%.10s %.8s\"\n",b+2,b+13);
53 printf("#define MANIFEST_YEAR \"%.4s\"\n",b+2);
 
 
 
 
 
 
 
 
 
 
 
54 }
 
 
 
55 }
56 fclose(m);
57 v = open_for_reading(argv[3]);
58 if( fgets(b, sizeof(b)-1,v)==0 ){
59 fprintf(stderr, "malformed VERSION file: %s\n", argv[3]);
60
--- src/mkversion.c
+++ src/mkversion.c
@@ -8,10 +8,11 @@
8 ** Note that the manifest.uuid and manifest files are generated by Fossil.
9 */
10 #include <stdio.h>
11 #include <string.h>
12 #include <stdlib.h>
13 #include <ctype.h>
14
15 static FILE *open_for_reading(const char *zFilename){
16 FILE *f = fopen(zFilename, "r");
17 if( f==0 ){
18 fprintf(stderr, "cannot open \"%s\" for reading\n", zFilename);
@@ -46,14 +47,28 @@
47 *z = 0;
48 printf("#define MANIFEST_UUID \"%s\"\n",b);
49 printf("#define MANIFEST_VERSION \"[%10.10s]\"\n",b);
50 m = open_for_reading(argv[2]);
51 while(b == fgets(b, sizeof(b)-1,m)){
52 if(0 == strncmp("D ",b,2)){
53 int k, n;
54 char zDateNum[30];
55 printf("#define MANIFEST_DATE \"%.10s %.8s\"\n",b+2,b+13);
56 printf("#define MANIFEST_YEAR \"%.4s\"\n",b+2);
57 n = 0;
58 for(k=0; k<10; k++){
59 if( isdigit(b[k+2]) ) zDateNum[n++] = b[k+2];
60 }
61 zDateNum[n] = 0;
62 printf("#define MANIFEST_NUMERIC_DATE %s\n", zDateNum);
63 n = 0;
64 for(k=0; k<8; k++){
65 if( isdigit(b[k+13]) ) zDateNum[n++] = b[k+13];
66 }
67 zDateNum[n] = 0;
68 printf("#define MANIFEST_NUMERIC_TIME %s\n", zDateNum);
69 }
70 }
71 fclose(m);
72 v = open_for_reading(argv[3]);
73 if( fgets(b, sizeof(b)-1,v)==0 ){
74 fprintf(stderr, "malformed VERSION file: %s\n", argv[3]);
75
--- src/schema.c
+++ src/schema.c
@@ -158,10 +158,16 @@
158158
@ );
159159
@
160160
@ -- Artifacts that should not be pushed are stored in the "private"
161161
@ -- table. Private artifacts are omitted from the "unclustered" and
162162
@ -- "unsent" tables.
163
+@ --
164
+@ -- A phantom artifact (that is, an artifact with BLOB.SIZE<0 - an artifact
165
+@ -- for which we do not know the content) might also be marked as private.
166
+@ -- This comes about when an artifact is named in a manifest or tag but
167
+@ -- the content of that artifact is held privately by some other peer
168
+@ -- repository.
163169
@ --
164170
@ CREATE TABLE private(rid INTEGER PRIMARY KEY);
165171
@
166172
@ -- An entry in this table describes a database query that generates a
167173
@ -- table of tickets.
168174
--- src/schema.c
+++ src/schema.c
@@ -158,10 +158,16 @@
158 @ );
159 @
160 @ -- Artifacts that should not be pushed are stored in the "private"
161 @ -- table. Private artifacts are omitted from the "unclustered" and
162 @ -- "unsent" tables.
 
 
 
 
 
 
163 @ --
164 @ CREATE TABLE private(rid INTEGER PRIMARY KEY);
165 @
166 @ -- An entry in this table describes a database query that generates a
167 @ -- table of tickets.
168
--- src/schema.c
+++ src/schema.c
@@ -158,10 +158,16 @@
158 @ );
159 @
160 @ -- Artifacts that should not be pushed are stored in the "private"
161 @ -- table. Private artifacts are omitted from the "unclustered" and
162 @ -- "unsent" tables.
163 @ --
164 @ -- A phantom artifact (that is, an artifact with BLOB.SIZE<0 - an artifact
165 @ -- for which we do not know the content) might also be marked as private.
166 @ -- This comes about when an artifact is named in a manifest or tag but
167 @ -- the content of that artifact is held privately by some other peer
168 @ -- repository.
169 @ --
170 @ CREATE TABLE private(rid INTEGER PRIMARY KEY);
171 @
172 @ -- An entry in this table describes a database query that generates a
173 @ -- table of tickets.
174
+1 -13
--- src/sync.c
+++ src/sync.c
@@ -61,22 +61,10 @@
6161
g.url.flags |= URL_PROMPT_PW;
6262
url_prompt_for_password();
6363
}
6464
g.zHttpAuth = get_httpauth();
6565
url_remember();
66
-#if 0 /* Disabled for now */
67
- if( (flags & AUTOSYNC_PULL)!=0 && db_get_boolean("auto-shun",1) ){
68
- /* When doing an automatic pull, also automatically pull shuns from
69
- ** the server if pull_shuns is enabled.
70
- **
71
- ** TODO: What happens if the shun list gets really big?
72
- ** Maybe the shunning list should only be pulled on every 10th
73
- ** autosync, or something?
74
- */
75
- configSync = CONFIGSET_SHUN;
76
- }
77
-#endif
7866
if( find_option("verbose","v",0)!=0 ) flags |= SYNC_VERBOSE;
7967
fossil_print("Autosync: %s\n", g.url.canonical);
8068
url_enable_proxy("via proxy: ");
8169
rc = client_sync(flags, configSync, 0, 0);
8270
return rc;
@@ -163,11 +151,11 @@
163151
url_proxy_options();
164152
clone_ssh_find_options();
165153
if( !uvOnly ) db_find_and_open_repository(0, 0);
166154
db_open_config(0, 1);
167155
if( g.argc==2 ){
168
- if( db_get_boolean("auto-shun",1) ) configSync = CONFIGSET_SHUN;
156
+ if( db_get_boolean("auto-shun",0) ) configSync = CONFIGSET_SHUN;
169157
}else if( g.argc==3 ){
170158
zUrl = g.argv[2];
171159
}
172160
if( ((*pSyncFlags) & (SYNC_PUSH|SYNC_PULL))==(SYNC_PUSH|SYNC_PULL)
173161
&& db_get_boolean("uv-sync",0)
174162
--- src/sync.c
+++ src/sync.c
@@ -61,22 +61,10 @@
61 g.url.flags |= URL_PROMPT_PW;
62 url_prompt_for_password();
63 }
64 g.zHttpAuth = get_httpauth();
65 url_remember();
66 #if 0 /* Disabled for now */
67 if( (flags & AUTOSYNC_PULL)!=0 && db_get_boolean("auto-shun",1) ){
68 /* When doing an automatic pull, also automatically pull shuns from
69 ** the server if pull_shuns is enabled.
70 **
71 ** TODO: What happens if the shun list gets really big?
72 ** Maybe the shunning list should only be pulled on every 10th
73 ** autosync, or something?
74 */
75 configSync = CONFIGSET_SHUN;
76 }
77 #endif
78 if( find_option("verbose","v",0)!=0 ) flags |= SYNC_VERBOSE;
79 fossil_print("Autosync: %s\n", g.url.canonical);
80 url_enable_proxy("via proxy: ");
81 rc = client_sync(flags, configSync, 0, 0);
82 return rc;
@@ -163,11 +151,11 @@
163 url_proxy_options();
164 clone_ssh_find_options();
165 if( !uvOnly ) db_find_and_open_repository(0, 0);
166 db_open_config(0, 1);
167 if( g.argc==2 ){
168 if( db_get_boolean("auto-shun",1) ) configSync = CONFIGSET_SHUN;
169 }else if( g.argc==3 ){
170 zUrl = g.argv[2];
171 }
172 if( ((*pSyncFlags) & (SYNC_PUSH|SYNC_PULL))==(SYNC_PUSH|SYNC_PULL)
173 && db_get_boolean("uv-sync",0)
174
--- src/sync.c
+++ src/sync.c
@@ -61,22 +61,10 @@
61 g.url.flags |= URL_PROMPT_PW;
62 url_prompt_for_password();
63 }
64 g.zHttpAuth = get_httpauth();
65 url_remember();
 
 
 
 
 
 
 
 
 
 
 
 
66 if( find_option("verbose","v",0)!=0 ) flags |= SYNC_VERBOSE;
67 fossil_print("Autosync: %s\n", g.url.canonical);
68 url_enable_proxy("via proxy: ");
69 rc = client_sync(flags, configSync, 0, 0);
70 return rc;
@@ -163,11 +151,11 @@
151 url_proxy_options();
152 clone_ssh_find_options();
153 if( !uvOnly ) db_find_and_open_repository(0, 0);
154 db_open_config(0, 1);
155 if( g.argc==2 ){
156 if( db_get_boolean("auto-shun",0) ) configSync = CONFIGSET_SHUN;
157 }else if( g.argc==3 ){
158 zUrl = g.argv[2];
159 }
160 if( ((*pSyncFlags) & (SYNC_PUSH|SYNC_PULL))==(SYNC_PUSH|SYNC_PULL)
161 && db_get_boolean("uv-sync",0)
162
+66 -14
--- src/xfer.c
+++ src/xfer.c
@@ -49,11 +49,13 @@
4949
int nDanglingFile; /* Number of dangling deltas received */
5050
int mxSend; /* Stop sending "file" when pOut reaches this size */
5151
int resync; /* Send igot cards for all holdings */
5252
u8 syncPrivate; /* True to enable syncing private content */
5353
u8 nextIsPrivate; /* If true, next "file" received is a private */
54
- u32 clientVersion; /* Version of the client software */
54
+ u32 remoteVersion; /* Version of fossil running on the other side */
55
+ u32 remoteDate; /* Date for specific client software edition */
56
+ u32 remoteTime; /* Time of date correspoding on remoteDate */
5557
time_t maxTime; /* Time when this transfer should be finished */
5658
};
5759
5860
5961
/*
@@ -524,20 +526,19 @@
524526
static void send_file(Xfer *pXfer, int rid, Blob *pUuid, int nativeDelta){
525527
Blob content, uuid;
526528
int size = 0;
527529
int isPriv = content_is_private(rid);
528530
529
- if( pXfer->syncPrivate==0 && isPriv ) return;
530531
if( db_exists("SELECT 1 FROM onremote WHERE rid=%d", rid) ){
531532
return;
532533
}
533534
blob_zero(&uuid);
534535
db_blob(&uuid, "SELECT uuid FROM blob WHERE rid=%d AND size>=0", rid);
535536
if( blob_size(&uuid)==0 ){
536537
return;
537538
}
538
- if( blob_size(&uuid)>HNAME_LEN_SHA1 && pXfer->clientVersion<20000 ){
539
+ if( blob_size(&uuid)>HNAME_LEN_SHA1 && pXfer->remoteVersion<20000 ){
539540
xfer_cannot_send_sha3_error(pXfer);
540541
return;
541542
}
542543
if( pUuid ){
543544
if( blob_compare(pUuid, &uuid)!=0 ){
@@ -548,10 +549,21 @@
548549
pUuid = &uuid;
549550
}
550551
if( uuid_is_shunned(blob_str(pUuid)) ){
551552
blob_reset(&uuid);
552553
return;
554
+ }
555
+ if( isPriv && pXfer->syncPrivate==0 ){
556
+ if( pXfer->remoteDate>=20200413 ){
557
+ /* If the artifact is private and we are not doing a private sync,
558
+ ** at least tell the other side that the artifact exists and is
559
+ ** known to be private. But only do this for newer clients since
560
+ ** older ones will throw an error if they get a private igot card
561
+ ** and private syncing is disallowed */
562
+ blob_appendf(pXfer->pOut, "igot %b 1\n", pUuid);
563
+ }
564
+ return;
553565
}
554566
if( (pXfer->maxTime != -1 && time(NULL) >= pXfer->maxTime) ||
555567
pXfer->mxSend<=blob_size(pXfer->pOut) ){
556568
const char *zFormat = isPriv ? "igot %b 1\n" : "igot %b\n";
557569
blob_appendf(pXfer->pOut, zFormat /*works-like:"%b"*/, pUuid);
@@ -626,11 +638,11 @@
626638
szC = db_column_bytes(&q1, 2);
627639
zContent = db_column_raw(&q1, 2);
628640
srcIsPrivate = db_column_int(&q1, 3);
629641
zDelta = db_column_text(&q1, 4);
630642
if( isPrivate ) blob_append(pXfer->pOut, "private\n", -1);
631
- if( pXfer->clientVersion<20000 && db_column_bytes(&q1,0)!=HNAME_LEN_SHA1 ){
643
+ if( pXfer->remoteVersion<20000 && db_column_bytes(&q1,0)!=HNAME_LEN_SHA1 ){
632644
xfer_cannot_send_sha3_error(pXfer);
633645
db_reset(&q1);
634646
return;
635647
}
636648
blob_appendf(pXfer->pOut, "cfile %s ", zUuid);
@@ -690,11 +702,11 @@
690702
);
691703
}
692704
if( db_step(&q1)==SQLITE_ROW ){
693705
sqlite3_int64 mtime = db_column_int64(&q1, 0);
694706
const char *zHash = db_column_text(&q1, 1);
695
- if( pXfer->clientVersion<20000 && db_column_bytes(&q1,1)>HNAME_LEN_SHA1 ){
707
+ if( pXfer->remoteVersion<20000 && db_column_bytes(&q1,1)>HNAME_LEN_SHA1 ){
696708
xfer_cannot_send_sha3_error(pXfer);
697709
db_reset(&q1);
698710
return;
699711
}
700712
if( blob_size(pXfer->pOut)>=pXfer->mxSend ){
@@ -1293,24 +1305,36 @@
12931305
}else
12941306
12951307
/* igot HASH ?ISPRIVATE?
12961308
**
12971309
** Client announces that it has a particular file. If the ISPRIVATE
1298
- ** argument exists and is non-zero, then the file is a private file.
1310
+ ** argument exists and is "1", then the file is a private file.
12991311
*/
13001312
if( xfer.nToken>=2
13011313
&& blob_eq(&xfer.aToken[0], "igot")
13021314
&& blob_is_hname(&xfer.aToken[1])
13031315
){
13041316
if( isPush ){
13051317
int rid = 0;
13061318
if( xfer.nToken==2 || blob_eq(&xfer.aToken[2],"1")==0 ){
1319
+ /* Client says the artifact is public */
13071320
rid = rid_from_uuid(&xfer.aToken[1], 1, 0);
13081321
}else if( g.perm.Private ){
1322
+ /* Client says the artifact is private and the client has
1323
+ ** permission to push private content. Create a new phantom
1324
+ ** artifact that is marked private. */
13091325
rid = rid_from_uuid(&xfer.aToken[1], 1, 1);
13101326
}else{
1311
- server_private_xfer_not_authorized();
1327
+ /* Client says the artifact is private and the client is unable
1328
+ ** or unwilling to send us the artifact. If we already hold the
1329
+ ** artifact here on the server as a phantom, make sure that
1330
+ ** phantom is marked as private so that we don't keep asking about
1331
+ ** it in subsequent sync requests. */
1332
+ rid = rid_from_uuid(&xfer.aToken[1], 0, 1);
1333
+ if( rid>0 ){
1334
+ db_multi_exec("INSERT OR IGNORE INTO private(rid) VALUES(%d)",rid);
1335
+ }
13121336
}
13131337
if( rid ) remote_has(rid);
13141338
}
13151339
}else
13161340
@@ -1542,17 +1566,24 @@
15421566
*/
15431567
if( blob_eq(&xfer.aToken[1], "send-catalog") ){
15441568
xfer.resync = 0x7fffffff;
15451569
}
15461570
1547
- /* pragma client-version VERSION
1571
+ /* pragma client-version VERSION ?DATE? ?TIME?
15481572
**
15491573
** The client announces to the server what version of Fossil it
1550
- ** is running.
1574
+ ** is running. The DATE and TIME are a pure numeric ISO8601 time
1575
+ ** for the specific check-in of the client.
15511576
*/
15521577
if( xfer.nToken>=3 && blob_eq(&xfer.aToken[1], "client-version") ){
1553
- xfer.clientVersion = atoi(blob_str(&xfer.aToken[2]));
1578
+ xfer.remoteVersion = atoi(blob_str(&xfer.aToken[2]));
1579
+ if( xfer.nToken>=5 ){
1580
+ xfer.remoteDate = atoi(blob_str(&xfer.aToken[3]));
1581
+ xfer.remoteTime = atoi(blob_str(&xfer.aToken[4]));
1582
+ @ pragma server-version %d(RELEASE_VERSION_NUMBER) \
1583
+ @ %d(MANIFEST_NUMERIC_DATE) %d(MANIFEST_NUMERIC_TIME)
1584
+ }
15541585
}
15551586
15561587
/* pragma uv-hash HASH
15571588
**
15581589
** The client wants to make sure that unversioned files are all synced.
@@ -1834,11 +1865,11 @@
18341865
memset(&xfer, 0, sizeof(xfer));
18351866
xfer.pIn = &recv;
18361867
xfer.pOut = &send;
18371868
xfer.mxSend = db_get_int("max-upload", 250000);
18381869
xfer.maxTime = -1;
1839
- xfer.clientVersion = RELEASE_VERSION_NUMBER;
1870
+ xfer.remoteVersion = RELEASE_VERSION_NUMBER;
18401871
if( syncFlags & SYNC_PRIVATE ){
18411872
g.perm.Private = 1;
18421873
xfer.syncPrivate = 1;
18431874
}
18441875
@@ -1885,11 +1916,13 @@
18851916
18861917
/*
18871918
** The request from the client always begin with a clone, pull,
18881919
** or push message.
18891920
*/
1890
- blob_appendf(&send, "pragma client-version %d\n", RELEASE_VERSION_NUMBER);
1921
+ blob_appendf(&send, "pragma client-version %d %d %d\n",
1922
+ RELEASE_VERSION_NUMBER, MANIFEST_NUMERIC_DATE,
1923
+ MANIFEST_NUMERIC_TIME);
18911924
if( syncFlags & SYNC_CLONE ){
18921925
blob_appendf(&send, "clone 3 %d\n", cloneSeqno);
18931926
syncFlags &= ~(SYNC_PUSH|SYNC_PULL);
18941927
nCardSent++;
18951928
/* TBD: Request all transferable configuration values */
@@ -2082,11 +2115,13 @@
20822115
xfer.nGimmeSent = 0;
20832116
xfer.nIGotSent = 0;
20842117
20852118
lastPctDone = -1;
20862119
blob_reset(&send);
2087
- blob_appendf(&send, "pragma client-version %d\n", RELEASE_VERSION_NUMBER);
2120
+ blob_appendf(&send, "pragma client-version %d %d %d\n",
2121
+ RELEASE_VERSION_NUMBER, MANIFEST_NUMERIC_DATE,
2122
+ MANIFEST_NUMERIC_TIME);
20882123
rArrivalTime = db_double(0.0, "SELECT julianday('now')");
20892124
20902125
/* Send the send-private pragma if we are trying to sync private data */
20912126
if( syncFlags & SYNC_PRIVATE ){
20922127
blob_append(&send, "pragma send-private\n", -1);
@@ -2210,10 +2245,11 @@
22102245
rid = rid_from_uuid(&xfer.aToken[1], 0, 0);
22112246
if( rid>0 ){
22122247
if( !isPriv ) content_make_public(rid);
22132248
}else if( isPriv && !g.perm.Private ){
22142249
/* ignore private files */
2250
+ db_multi_exec("INSERT OR IGNORE INTO private VALUES(%d)", rid);
22152251
}else if( (syncFlags & (SYNC_PULL|SYNC_CLONE))!=0 ){
22162252
rid = content_new(blob_str(&xfer.aToken[1]), isPriv);
22172253
if( rid ) newPhantom = 1;
22182254
}
22192255
remote_has(rid);
@@ -2393,11 +2429,27 @@
23932429
** The server can send pragmas to try to convey meta-information to
23942430
** the client. These are informational only. Unknown pragmas are
23952431
** silently ignored.
23962432
*/
23972433
if( blob_eq(&xfer.aToken[0], "pragma") && xfer.nToken>=2 ){
2398
- /* If the server is unwill to accept new unversioned content (because
2434
+ /* pragma server-version VERSION ?DATE? ?TIME?
2435
+ **
2436
+ ** The servger announces to the server what version of Fossil it
2437
+ ** is running. The DATE and TIME are a pure numeric ISO8601 time
2438
+ ** for the specific check-in of the client.
2439
+ */
2440
+ if( xfer.nToken>=3 && blob_eq(&xfer.aToken[1], "client-version") ){
2441
+ xfer.remoteVersion = atoi(blob_str(&xfer.aToken[2]));
2442
+ if( xfer.nToken>=5 ){
2443
+ xfer.remoteDate = atoi(blob_str(&xfer.aToken[3]));
2444
+ xfer.remoteTime = atoi(blob_str(&xfer.aToken[4]));
2445
+ }
2446
+ }
2447
+
2448
+ /* pragma uv-pull-only
2449
+ **
2450
+ ** If the server is unwill to accept new unversioned content (because
23992451
** this client lacks the necessary permissions) then it sends a
24002452
** "uv-pull-only" pragma so that the client will know not to waste
24012453
** bandwidth trying to upload unversioned content. If the server
24022454
** does accept new unversioned content, it sends "uv-push-ok".
24032455
*/
24042456
--- src/xfer.c
+++ src/xfer.c
@@ -49,11 +49,13 @@
49 int nDanglingFile; /* Number of dangling deltas received */
50 int mxSend; /* Stop sending "file" when pOut reaches this size */
51 int resync; /* Send igot cards for all holdings */
52 u8 syncPrivate; /* True to enable syncing private content */
53 u8 nextIsPrivate; /* If true, next "file" received is a private */
54 u32 clientVersion; /* Version of the client software */
 
 
55 time_t maxTime; /* Time when this transfer should be finished */
56 };
57
58
59 /*
@@ -524,20 +526,19 @@
524 static void send_file(Xfer *pXfer, int rid, Blob *pUuid, int nativeDelta){
525 Blob content, uuid;
526 int size = 0;
527 int isPriv = content_is_private(rid);
528
529 if( pXfer->syncPrivate==0 && isPriv ) return;
530 if( db_exists("SELECT 1 FROM onremote WHERE rid=%d", rid) ){
531 return;
532 }
533 blob_zero(&uuid);
534 db_blob(&uuid, "SELECT uuid FROM blob WHERE rid=%d AND size>=0", rid);
535 if( blob_size(&uuid)==0 ){
536 return;
537 }
538 if( blob_size(&uuid)>HNAME_LEN_SHA1 && pXfer->clientVersion<20000 ){
539 xfer_cannot_send_sha3_error(pXfer);
540 return;
541 }
542 if( pUuid ){
543 if( blob_compare(pUuid, &uuid)!=0 ){
@@ -548,10 +549,21 @@
548 pUuid = &uuid;
549 }
550 if( uuid_is_shunned(blob_str(pUuid)) ){
551 blob_reset(&uuid);
552 return;
 
 
 
 
 
 
 
 
 
 
 
553 }
554 if( (pXfer->maxTime != -1 && time(NULL) >= pXfer->maxTime) ||
555 pXfer->mxSend<=blob_size(pXfer->pOut) ){
556 const char *zFormat = isPriv ? "igot %b 1\n" : "igot %b\n";
557 blob_appendf(pXfer->pOut, zFormat /*works-like:"%b"*/, pUuid);
@@ -626,11 +638,11 @@
626 szC = db_column_bytes(&q1, 2);
627 zContent = db_column_raw(&q1, 2);
628 srcIsPrivate = db_column_int(&q1, 3);
629 zDelta = db_column_text(&q1, 4);
630 if( isPrivate ) blob_append(pXfer->pOut, "private\n", -1);
631 if( pXfer->clientVersion<20000 && db_column_bytes(&q1,0)!=HNAME_LEN_SHA1 ){
632 xfer_cannot_send_sha3_error(pXfer);
633 db_reset(&q1);
634 return;
635 }
636 blob_appendf(pXfer->pOut, "cfile %s ", zUuid);
@@ -690,11 +702,11 @@
690 );
691 }
692 if( db_step(&q1)==SQLITE_ROW ){
693 sqlite3_int64 mtime = db_column_int64(&q1, 0);
694 const char *zHash = db_column_text(&q1, 1);
695 if( pXfer->clientVersion<20000 && db_column_bytes(&q1,1)>HNAME_LEN_SHA1 ){
696 xfer_cannot_send_sha3_error(pXfer);
697 db_reset(&q1);
698 return;
699 }
700 if( blob_size(pXfer->pOut)>=pXfer->mxSend ){
@@ -1293,24 +1305,36 @@
1293 }else
1294
1295 /* igot HASH ?ISPRIVATE?
1296 **
1297 ** Client announces that it has a particular file. If the ISPRIVATE
1298 ** argument exists and is non-zero, then the file is a private file.
1299 */
1300 if( xfer.nToken>=2
1301 && blob_eq(&xfer.aToken[0], "igot")
1302 && blob_is_hname(&xfer.aToken[1])
1303 ){
1304 if( isPush ){
1305 int rid = 0;
1306 if( xfer.nToken==2 || blob_eq(&xfer.aToken[2],"1")==0 ){
 
1307 rid = rid_from_uuid(&xfer.aToken[1], 1, 0);
1308 }else if( g.perm.Private ){
 
 
 
1309 rid = rid_from_uuid(&xfer.aToken[1], 1, 1);
1310 }else{
1311 server_private_xfer_not_authorized();
 
 
 
 
 
 
 
 
1312 }
1313 if( rid ) remote_has(rid);
1314 }
1315 }else
1316
@@ -1542,17 +1566,24 @@
1542 */
1543 if( blob_eq(&xfer.aToken[1], "send-catalog") ){
1544 xfer.resync = 0x7fffffff;
1545 }
1546
1547 /* pragma client-version VERSION
1548 **
1549 ** The client announces to the server what version of Fossil it
1550 ** is running.
 
1551 */
1552 if( xfer.nToken>=3 && blob_eq(&xfer.aToken[1], "client-version") ){
1553 xfer.clientVersion = atoi(blob_str(&xfer.aToken[2]));
 
 
 
 
 
 
1554 }
1555
1556 /* pragma uv-hash HASH
1557 **
1558 ** The client wants to make sure that unversioned files are all synced.
@@ -1834,11 +1865,11 @@
1834 memset(&xfer, 0, sizeof(xfer));
1835 xfer.pIn = &recv;
1836 xfer.pOut = &send;
1837 xfer.mxSend = db_get_int("max-upload", 250000);
1838 xfer.maxTime = -1;
1839 xfer.clientVersion = RELEASE_VERSION_NUMBER;
1840 if( syncFlags & SYNC_PRIVATE ){
1841 g.perm.Private = 1;
1842 xfer.syncPrivate = 1;
1843 }
1844
@@ -1885,11 +1916,13 @@
1885
1886 /*
1887 ** The request from the client always begin with a clone, pull,
1888 ** or push message.
1889 */
1890 blob_appendf(&send, "pragma client-version %d\n", RELEASE_VERSION_NUMBER);
 
 
1891 if( syncFlags & SYNC_CLONE ){
1892 blob_appendf(&send, "clone 3 %d\n", cloneSeqno);
1893 syncFlags &= ~(SYNC_PUSH|SYNC_PULL);
1894 nCardSent++;
1895 /* TBD: Request all transferable configuration values */
@@ -2082,11 +2115,13 @@
2082 xfer.nGimmeSent = 0;
2083 xfer.nIGotSent = 0;
2084
2085 lastPctDone = -1;
2086 blob_reset(&send);
2087 blob_appendf(&send, "pragma client-version %d\n", RELEASE_VERSION_NUMBER);
 
 
2088 rArrivalTime = db_double(0.0, "SELECT julianday('now')");
2089
2090 /* Send the send-private pragma if we are trying to sync private data */
2091 if( syncFlags & SYNC_PRIVATE ){
2092 blob_append(&send, "pragma send-private\n", -1);
@@ -2210,10 +2245,11 @@
2210 rid = rid_from_uuid(&xfer.aToken[1], 0, 0);
2211 if( rid>0 ){
2212 if( !isPriv ) content_make_public(rid);
2213 }else if( isPriv && !g.perm.Private ){
2214 /* ignore private files */
 
2215 }else if( (syncFlags & (SYNC_PULL|SYNC_CLONE))!=0 ){
2216 rid = content_new(blob_str(&xfer.aToken[1]), isPriv);
2217 if( rid ) newPhantom = 1;
2218 }
2219 remote_has(rid);
@@ -2393,11 +2429,27 @@
2393 ** The server can send pragmas to try to convey meta-information to
2394 ** the client. These are informational only. Unknown pragmas are
2395 ** silently ignored.
2396 */
2397 if( blob_eq(&xfer.aToken[0], "pragma") && xfer.nToken>=2 ){
2398 /* If the server is unwill to accept new unversioned content (because
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2399 ** this client lacks the necessary permissions) then it sends a
2400 ** "uv-pull-only" pragma so that the client will know not to waste
2401 ** bandwidth trying to upload unversioned content. If the server
2402 ** does accept new unversioned content, it sends "uv-push-ok".
2403 */
2404
--- src/xfer.c
+++ src/xfer.c
@@ -49,11 +49,13 @@
49 int nDanglingFile; /* Number of dangling deltas received */
50 int mxSend; /* Stop sending "file" when pOut reaches this size */
51 int resync; /* Send igot cards for all holdings */
52 u8 syncPrivate; /* True to enable syncing private content */
53 u8 nextIsPrivate; /* If true, next "file" received is a private */
54 u32 remoteVersion; /* Version of fossil running on the other side */
55 u32 remoteDate; /* Date for specific client software edition */
56 u32 remoteTime; /* Time of date correspoding on remoteDate */
57 time_t maxTime; /* Time when this transfer should be finished */
58 };
59
60
61 /*
@@ -524,20 +526,19 @@
526 static void send_file(Xfer *pXfer, int rid, Blob *pUuid, int nativeDelta){
527 Blob content, uuid;
528 int size = 0;
529 int isPriv = content_is_private(rid);
530
 
531 if( db_exists("SELECT 1 FROM onremote WHERE rid=%d", rid) ){
532 return;
533 }
534 blob_zero(&uuid);
535 db_blob(&uuid, "SELECT uuid FROM blob WHERE rid=%d AND size>=0", rid);
536 if( blob_size(&uuid)==0 ){
537 return;
538 }
539 if( blob_size(&uuid)>HNAME_LEN_SHA1 && pXfer->remoteVersion<20000 ){
540 xfer_cannot_send_sha3_error(pXfer);
541 return;
542 }
543 if( pUuid ){
544 if( blob_compare(pUuid, &uuid)!=0 ){
@@ -548,10 +549,21 @@
549 pUuid = &uuid;
550 }
551 if( uuid_is_shunned(blob_str(pUuid)) ){
552 blob_reset(&uuid);
553 return;
554 }
555 if( isPriv && pXfer->syncPrivate==0 ){
556 if( pXfer->remoteDate>=20200413 ){
557 /* If the artifact is private and we are not doing a private sync,
558 ** at least tell the other side that the artifact exists and is
559 ** known to be private. But only do this for newer clients since
560 ** older ones will throw an error if they get a private igot card
561 ** and private syncing is disallowed */
562 blob_appendf(pXfer->pOut, "igot %b 1\n", pUuid);
563 }
564 return;
565 }
566 if( (pXfer->maxTime != -1 && time(NULL) >= pXfer->maxTime) ||
567 pXfer->mxSend<=blob_size(pXfer->pOut) ){
568 const char *zFormat = isPriv ? "igot %b 1\n" : "igot %b\n";
569 blob_appendf(pXfer->pOut, zFormat /*works-like:"%b"*/, pUuid);
@@ -626,11 +638,11 @@
638 szC = db_column_bytes(&q1, 2);
639 zContent = db_column_raw(&q1, 2);
640 srcIsPrivate = db_column_int(&q1, 3);
641 zDelta = db_column_text(&q1, 4);
642 if( isPrivate ) blob_append(pXfer->pOut, "private\n", -1);
643 if( pXfer->remoteVersion<20000 && db_column_bytes(&q1,0)!=HNAME_LEN_SHA1 ){
644 xfer_cannot_send_sha3_error(pXfer);
645 db_reset(&q1);
646 return;
647 }
648 blob_appendf(pXfer->pOut, "cfile %s ", zUuid);
@@ -690,11 +702,11 @@
702 );
703 }
704 if( db_step(&q1)==SQLITE_ROW ){
705 sqlite3_int64 mtime = db_column_int64(&q1, 0);
706 const char *zHash = db_column_text(&q1, 1);
707 if( pXfer->remoteVersion<20000 && db_column_bytes(&q1,1)>HNAME_LEN_SHA1 ){
708 xfer_cannot_send_sha3_error(pXfer);
709 db_reset(&q1);
710 return;
711 }
712 if( blob_size(pXfer->pOut)>=pXfer->mxSend ){
@@ -1293,24 +1305,36 @@
1305 }else
1306
1307 /* igot HASH ?ISPRIVATE?
1308 **
1309 ** Client announces that it has a particular file. If the ISPRIVATE
1310 ** argument exists and is "1", then the file is a private file.
1311 */
1312 if( xfer.nToken>=2
1313 && blob_eq(&xfer.aToken[0], "igot")
1314 && blob_is_hname(&xfer.aToken[1])
1315 ){
1316 if( isPush ){
1317 int rid = 0;
1318 if( xfer.nToken==2 || blob_eq(&xfer.aToken[2],"1")==0 ){
1319 /* Client says the artifact is public */
1320 rid = rid_from_uuid(&xfer.aToken[1], 1, 0);
1321 }else if( g.perm.Private ){
1322 /* Client says the artifact is private and the client has
1323 ** permission to push private content. Create a new phantom
1324 ** artifact that is marked private. */
1325 rid = rid_from_uuid(&xfer.aToken[1], 1, 1);
1326 }else{
1327 /* Client says the artifact is private and the client is unable
1328 ** or unwilling to send us the artifact. If we already hold the
1329 ** artifact here on the server as a phantom, make sure that
1330 ** phantom is marked as private so that we don't keep asking about
1331 ** it in subsequent sync requests. */
1332 rid = rid_from_uuid(&xfer.aToken[1], 0, 1);
1333 if( rid>0 ){
1334 db_multi_exec("INSERT OR IGNORE INTO private(rid) VALUES(%d)",rid);
1335 }
1336 }
1337 if( rid ) remote_has(rid);
1338 }
1339 }else
1340
@@ -1542,17 +1566,24 @@
1566 */
1567 if( blob_eq(&xfer.aToken[1], "send-catalog") ){
1568 xfer.resync = 0x7fffffff;
1569 }
1570
1571 /* pragma client-version VERSION ?DATE? ?TIME?
1572 **
1573 ** The client announces to the server what version of Fossil it
1574 ** is running. The DATE and TIME are a pure numeric ISO8601 time
1575 ** for the specific check-in of the client.
1576 */
1577 if( xfer.nToken>=3 && blob_eq(&xfer.aToken[1], "client-version") ){
1578 xfer.remoteVersion = atoi(blob_str(&xfer.aToken[2]));
1579 if( xfer.nToken>=5 ){
1580 xfer.remoteDate = atoi(blob_str(&xfer.aToken[3]));
1581 xfer.remoteTime = atoi(blob_str(&xfer.aToken[4]));
1582 @ pragma server-version %d(RELEASE_VERSION_NUMBER) \
1583 @ %d(MANIFEST_NUMERIC_DATE) %d(MANIFEST_NUMERIC_TIME)
1584 }
1585 }
1586
1587 /* pragma uv-hash HASH
1588 **
1589 ** The client wants to make sure that unversioned files are all synced.
@@ -1834,11 +1865,11 @@
1865 memset(&xfer, 0, sizeof(xfer));
1866 xfer.pIn = &recv;
1867 xfer.pOut = &send;
1868 xfer.mxSend = db_get_int("max-upload", 250000);
1869 xfer.maxTime = -1;
1870 xfer.remoteVersion = RELEASE_VERSION_NUMBER;
1871 if( syncFlags & SYNC_PRIVATE ){
1872 g.perm.Private = 1;
1873 xfer.syncPrivate = 1;
1874 }
1875
@@ -1885,11 +1916,13 @@
1916
1917 /*
1918 ** The request from the client always begin with a clone, pull,
1919 ** or push message.
1920 */
1921 blob_appendf(&send, "pragma client-version %d %d %d\n",
1922 RELEASE_VERSION_NUMBER, MANIFEST_NUMERIC_DATE,
1923 MANIFEST_NUMERIC_TIME);
1924 if( syncFlags & SYNC_CLONE ){
1925 blob_appendf(&send, "clone 3 %d\n", cloneSeqno);
1926 syncFlags &= ~(SYNC_PUSH|SYNC_PULL);
1927 nCardSent++;
1928 /* TBD: Request all transferable configuration values */
@@ -2082,11 +2115,13 @@
2115 xfer.nGimmeSent = 0;
2116 xfer.nIGotSent = 0;
2117
2118 lastPctDone = -1;
2119 blob_reset(&send);
2120 blob_appendf(&send, "pragma client-version %d %d %d\n",
2121 RELEASE_VERSION_NUMBER, MANIFEST_NUMERIC_DATE,
2122 MANIFEST_NUMERIC_TIME);
2123 rArrivalTime = db_double(0.0, "SELECT julianday('now')");
2124
2125 /* Send the send-private pragma if we are trying to sync private data */
2126 if( syncFlags & SYNC_PRIVATE ){
2127 blob_append(&send, "pragma send-private\n", -1);
@@ -2210,10 +2245,11 @@
2245 rid = rid_from_uuid(&xfer.aToken[1], 0, 0);
2246 if( rid>0 ){
2247 if( !isPriv ) content_make_public(rid);
2248 }else if( isPriv && !g.perm.Private ){
2249 /* ignore private files */
2250 db_multi_exec("INSERT OR IGNORE INTO private VALUES(%d)", rid);
2251 }else if( (syncFlags & (SYNC_PULL|SYNC_CLONE))!=0 ){
2252 rid = content_new(blob_str(&xfer.aToken[1]), isPriv);
2253 if( rid ) newPhantom = 1;
2254 }
2255 remote_has(rid);
@@ -2393,11 +2429,27 @@
2429 ** The server can send pragmas to try to convey meta-information to
2430 ** the client. These are informational only. Unknown pragmas are
2431 ** silently ignored.
2432 */
2433 if( blob_eq(&xfer.aToken[0], "pragma") && xfer.nToken>=2 ){
2434 /* pragma server-version VERSION ?DATE? ?TIME?
2435 **
2436 ** The servger announces to the server what version of Fossil it
2437 ** is running. The DATE and TIME are a pure numeric ISO8601 time
2438 ** for the specific check-in of the client.
2439 */
2440 if( xfer.nToken>=3 && blob_eq(&xfer.aToken[1], "client-version") ){
2441 xfer.remoteVersion = atoi(blob_str(&xfer.aToken[2]));
2442 if( xfer.nToken>=5 ){
2443 xfer.remoteDate = atoi(blob_str(&xfer.aToken[3]));
2444 xfer.remoteTime = atoi(blob_str(&xfer.aToken[4]));
2445 }
2446 }
2447
2448 /* pragma uv-pull-only
2449 **
2450 ** If the server is unwill to accept new unversioned content (because
2451 ** this client lacks the necessary permissions) then it sends a
2452 ** "uv-pull-only" pragma so that the client will know not to waste
2453 ** bandwidth trying to upload unversioned content. If the server
2454 ** does accept new unversioned content, it sends "uv-push-ok".
2455 */
2456
--- www/sync.wiki
+++ www/sync.wiki
@@ -421,10 +421,13 @@
421421
using a gimme card in either the reply or in the next message.</p>
422422
423423
<p>If the second argument exists and is "1", then the artifact
424424
identified by the first argument is private on the sender and should
425425
be ignored unless a "--private" [/help?cmd=sync|sync] is occurring.
426
+
427
+<p>The name "igot" comes from the English slang expression "I got" meaning
428
+"I have".
426429
427430
<h4>3.6.1 Unversioned Igot Cards</h4>
428431
429432
<p>Zero or more "uvigot" cards are sent from server to client when
430433
synchronizing unversioned content. The format of a uvigot card is
@@ -469,10 +472,14 @@
469472
<p>The argument to the gimme card is the ID of the artifact that
470473
the sender wants. The receiver will typically respond to a
471474
gimme card by sending a file card in its reply or in the next
472475
message.</p>
473476
477
+<p>The "gimme" name means "give me". The imperative "give me" is
478
+pronounced as if it were a single word "gimme" in some dialects of
479
+English (including the dialect spoken by the original author of Fossil).
480
+
474481
<h4>3.7.1 Unversioned Gimme Cards</h4>
475482
476483
<p>Sync synchronizing unversioned content, the client may send "uvgimme"
477484
cards to the server. A uvgimme card requests that the server send
478485
unversioned content to the client. The format of a uvgimme card is
479486
--- www/sync.wiki
+++ www/sync.wiki
@@ -421,10 +421,13 @@
421 using a gimme card in either the reply or in the next message.</p>
422
423 <p>If the second argument exists and is "1", then the artifact
424 identified by the first argument is private on the sender and should
425 be ignored unless a "--private" [/help?cmd=sync|sync] is occurring.
 
 
 
426
427 <h4>3.6.1 Unversioned Igot Cards</h4>
428
429 <p>Zero or more "uvigot" cards are sent from server to client when
430 synchronizing unversioned content. The format of a uvigot card is
@@ -469,10 +472,14 @@
469 <p>The argument to the gimme card is the ID of the artifact that
470 the sender wants. The receiver will typically respond to a
471 gimme card by sending a file card in its reply or in the next
472 message.</p>
473
 
 
 
 
474 <h4>3.7.1 Unversioned Gimme Cards</h4>
475
476 <p>Sync synchronizing unversioned content, the client may send "uvgimme"
477 cards to the server. A uvgimme card requests that the server send
478 unversioned content to the client. The format of a uvgimme card is
479
--- www/sync.wiki
+++ www/sync.wiki
@@ -421,10 +421,13 @@
421 using a gimme card in either the reply or in the next message.</p>
422
423 <p>If the second argument exists and is "1", then the artifact
424 identified by the first argument is private on the sender and should
425 be ignored unless a "--private" [/help?cmd=sync|sync] is occurring.
426
427 <p>The name "igot" comes from the English slang expression "I got" meaning
428 "I have".
429
430 <h4>3.6.1 Unversioned Igot Cards</h4>
431
432 <p>Zero or more "uvigot" cards are sent from server to client when
433 synchronizing unversioned content. The format of a uvigot card is
@@ -469,10 +472,14 @@
472 <p>The argument to the gimme card is the ID of the artifact that
473 the sender wants. The receiver will typically respond to a
474 gimme card by sending a file card in its reply or in the next
475 message.</p>
476
477 <p>The "gimme" name means "give me". The imperative "give me" is
478 pronounced as if it were a single word "gimme" in some dialects of
479 English (including the dialect spoken by the original author of Fossil).
480
481 <h4>3.7.1 Unversioned Gimme Cards</h4>
482
483 <p>Sync synchronizing unversioned content, the client may send "uvgimme"
484 cards to the server. A uvgimme card requests that the server send
485 unversioned content to the client. The format of a uvgimme card is
486

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button