Fossil SCM

Complete rework of the xfer mechanism. Compiles but not yet working.

drh 2007-08-10 00:08 trunk
Commit 573a464cb7be4d45ee10e206ec6be775f47b2323
+2 -2
--- src/checkin.c
+++ src/checkin.c
@@ -350,11 +350,11 @@
350350
zFullname = db_column_text(&q, 1);
351351
rid = db_column_int(&q, 2);
352352
353353
blob_zero(&content);
354354
blob_read_from_file(&content, zFullname);
355
- nrid = content_put(&content, 0);
355
+ nrid = content_put(&content, 0, 0);
356356
if( rid>0 ){
357357
content_deltify(rid, nrid, 0);
358358
}
359359
db_multi_exec("UPDATE vfile SET mrid=%d, rid=%d WHERE id=%d", nrid,nrid,id);
360360
}
@@ -408,11 +408,11 @@
408408
}
409409
blob_write_to_file(&manifest, zManifestFile);
410410
blob_reset(&manifest);
411411
blob_read_from_file(&manifest, zManifestFile);
412412
free(zManifestFile);
413
- nvid = content_put(&manifest, 0);
413
+ nvid = content_put(&manifest, 0, 0);
414414
if( nvid==0 ){
415415
fossil_panic("trouble committing manifest: %s", g.zErrMsg);
416416
}
417417
manifest_crosslink(nvid, &manifest);
418418
content_deltify(vid, nvid, 0);
419419
--- src/checkin.c
+++ src/checkin.c
@@ -350,11 +350,11 @@
350 zFullname = db_column_text(&q, 1);
351 rid = db_column_int(&q, 2);
352
353 blob_zero(&content);
354 blob_read_from_file(&content, zFullname);
355 nrid = content_put(&content, 0);
356 if( rid>0 ){
357 content_deltify(rid, nrid, 0);
358 }
359 db_multi_exec("UPDATE vfile SET mrid=%d, rid=%d WHERE id=%d", nrid,nrid,id);
360 }
@@ -408,11 +408,11 @@
408 }
409 blob_write_to_file(&manifest, zManifestFile);
410 blob_reset(&manifest);
411 blob_read_from_file(&manifest, zManifestFile);
412 free(zManifestFile);
413 nvid = content_put(&manifest, 0);
414 if( nvid==0 ){
415 fossil_panic("trouble committing manifest: %s", g.zErrMsg);
416 }
417 manifest_crosslink(nvid, &manifest);
418 content_deltify(vid, nvid, 0);
419
--- src/checkin.c
+++ src/checkin.c
@@ -350,11 +350,11 @@
350 zFullname = db_column_text(&q, 1);
351 rid = db_column_int(&q, 2);
352
353 blob_zero(&content);
354 blob_read_from_file(&content, zFullname);
355 nrid = content_put(&content, 0, 0);
356 if( rid>0 ){
357 content_deltify(rid, nrid, 0);
358 }
359 db_multi_exec("UPDATE vfile SET mrid=%d, rid=%d WHERE id=%d", nrid,nrid,id);
360 }
@@ -408,11 +408,11 @@
408 }
409 blob_write_to_file(&manifest, zManifestFile);
410 blob_reset(&manifest);
411 blob_read_from_file(&manifest, zManifestFile);
412 free(zManifestFile);
413 nvid = content_put(&manifest, 0, 0);
414 if( nvid==0 ){
415 fossil_panic("trouble committing manifest: %s", g.zErrMsg);
416 }
417 manifest_crosslink(nvid, &manifest);
418 content_deltify(vid, nvid, 0);
419
+91 -70
--- src/content.c
+++ src/content.c
@@ -29,73 +29,54 @@
2929
3030
/*
3131
** Return the srcid associated with rid. Or return 0 if rid is
3232
** original content and not a delta.
3333
*/
34
-static int findSrcid(int rid, const char *zDb){
35
- Stmt qsrc;
36
- int srcid;
37
- if( zDb ){
38
- db_prepare(&qsrc, "SELECT srcid FROM %s.delta WHERE rid=%d", zDb, rid);
39
- }else{
40
- db_prepare(&qsrc, "SELECT srcid FROM delta WHERE rid=%d", rid);
41
- }
42
- if( db_step(&qsrc)==SQLITE_ROW ){
43
- srcid = db_column_int(&qsrc, 0);
44
- }else{
45
- srcid = 0;
46
- }
47
- db_finalize(&qsrc);
34
+static int findSrcid(int rid){
35
+ int srcid = db_int(0, "SELECT srcid FROM delta WHERE rid=%d", rid);
4836
return srcid;
4937
}
5038
5139
/*
5240
** Extract the content for ID rid and put it into the
53
-** uninitialized blob.
41
+** uninitialized blob. Return 1 on success. If the record
42
+** is a phantom, zero pBlob and return 0.
5443
*/
55
-void content_get_from_db(int rid, Blob *pBlob, const char *zDb){
44
+int content_get(int rid, Blob *pBlob){
5645
Stmt q;
46
+ Blob src;
5747
int srcid;
48
+ int rc = 0;
49
+
5850
assert( g.repositoryOpen );
59
- srcid = findSrcid(rid, zDb);
60
- if( zDb ){
61
- db_prepare(&q,
62
- "SELECT content FROM %s.blob WHERE rid=%d AND size>=0",
63
- zDb, rid
64
- );
65
- }else{
66
- db_prepare(&q,
67
- "SELECT content FROM blob WHERE rid=%d AND size>=0",
68
- rid
69
- );
70
- }
51
+ srcid = findSrcid(rid);
52
+ blob_zero(pBlob);
7153
if( srcid ){
72
- Blob src;
73
- content_get_from_db(srcid, &src, zDb);
74
- if( db_step(&q)==SQLITE_ROW ){
75
- Blob delta;
76
- db_ephemeral_blob(&q, 0, &delta);
77
- blob_uncompress(&delta, &delta);
78
- blob_init(pBlob,0,0);
79
- blob_delta_apply(&src, &delta, pBlob);
80
- blob_reset(&delta);
81
- }else{
82
- blob_init(pBlob, 0, 0);
83
- }
84
- blob_reset(&src);
85
- }else{
54
+ if( content_get(srcid, &src) ){
55
+ db_prepare(&q, "SELECT content FROM blob WHERE rid=%d AND size>=0", rid);
56
+ if( db_step(&q)==SQLITE_ROW ){
57
+ Blob delta;
58
+ db_ephemeral_blob(&q, 0, &delta);
59
+ blob_uncompress(&delta, &delta);
60
+ blob_init(pBlob,0,0);
61
+ blob_delta_apply(&src, &delta, pBlob);
62
+ blob_reset(&delta);
63
+ rc = 1;
64
+ }
65
+ db_finalize(&q);
66
+ blob_reset(&src);
67
+ }
68
+ }else{
69
+ db_prepare(&q, "SELECT content FROM blob WHERE rid=%d AND size>=0", rid);
8670
if( db_step(&q)==SQLITE_ROW ){
8771
db_ephemeral_blob(&q, 0, pBlob);
8872
blob_uncompress(pBlob, pBlob);
89
- }else{
90
- blob_init(pBlob,0, 0);
91
- }
92
- }
93
- db_finalize(&q);
94
-}
95
-void content_get(int rid, Blob *pBlob){
96
- content_get_from_db(rid, pBlob, 0);
73
+ rc = 1;
74
+ }
75
+ db_finalize(&q);
76
+ }
77
+ return rc;
9778
}
9879
9980
/*
10081
** COMMAND: test-content-get
10182
**
@@ -130,33 +111,62 @@
130111
blob_zero(&content);
131112
db_blob(&content, "SELECT content FROM blob WHERE rid=%d", rid);
132113
blob_uncompress(&content, &content);
133114
blob_write_to_file(&content, zFile);
134115
}
116
+
117
+/*
118
+** When a record is converted from a phantom to a real record,
119
+** if that record has other records that are derived by delta,
120
+** then call manifest_crosslink() on those other records.
121
+*/
122
+void after_dephantomize(int rid, int linkFlag){
123
+ Stmt q;
124
+ db_prepare(&q, "SELECT rid FROM delta WHERE srcid=%d", rid);
125
+ while( db_step(&q)==SQLITE_ROW ){
126
+ int tid = db_column_int(&q, 0);
127
+ after_dephantomize(tid, 1);
128
+ }
129
+ db_finalize(&q);
130
+ if( linkFlag ){
131
+ Blob content;
132
+ content_get(rid, &content);
133
+ manifest_crosslink(rid, &content);
134
+ blob_reset(&content);
135
+ }
136
+}
135137
136138
/*
137139
** Write content into the database. Return the record ID. If the
138140
** content is already in the database, just return the record ID.
139141
**
140
-** A phantom is written if pBlob==0. If pBlob==0 then the UUID is set
141
-** to zUuid. Otherwise zUuid is ignored.
142
+** If srcId is specified, then pBlob is delta content from
143
+** the srcId record. srcId might be a phantom.
144
+**
145
+** A phantom is written if pBlob==0. If pBlob==0 or if srcId is
146
+** specified then the UUID is set to zUuid. Otherwise zUuid is
147
+** ignored. In the future this might change such that the content
148
+** hash is checked against zUuid to make sure it is correct.
142149
**
143150
** If the record already exists but is a phantom, the pBlob content
144151
** is inserted and the phatom becomes a real record.
145152
*/
146
-int content_put(Blob *pBlob, const char *zUuid){
153
+int content_put(Blob *pBlob, const char *zUuid, int srcId){
147154
int size;
148155
int rid;
149156
Stmt s1;
150157
Blob cmpr;
151158
Blob hash;
152159
assert( g.repositoryOpen );
153
- if( pBlob==0 ){
160
+ if( pBlob && srcId==0 ){
161
+ sha1sum_blob(pBlob, &hash);
162
+ }else{
154163
blob_init(&hash, zUuid, -1);
164
+ }
165
+ if( pBlob==0 ){
155166
size = -1;
156167
}else{
157
- sha1sum_blob(pBlob, &hash);
158168
size = blob_size(pBlob);
159169
}
160170
db_begin_transaction();
161171
162172
/* Check to see if the entry already exists and if it does whether
@@ -197,16 +207,19 @@
197207
);
198208
blob_compress(pBlob, &cmpr);
199209
db_bind_blob(&s1, ":data", &cmpr);
200210
db_exec(&s1);
201211
db_multi_exec("DELETE FROM phantom WHERE rid=%d", rid);
212
+ if( srcId==0 || db_int(0, "SELECT size FROM blob WHERE rid=%d", srcId)>0 ){
213
+ after_dephantomize(rid, 0);
214
+ }
202215
}else{
203216
/* We are creating a new entry */
204217
db_prepare(&s1,
205218
"INSERT INTO blob(rcvid,size,uuid,content)"
206
- "VALUES(%d,%d,'%s',:data)",
207
- g.rcvid, size, blob_str(&hash)
219
+ "VALUES(%d,%d,'%b',:data)",
220
+ g.rcvid, size, &hash
208221
);
209222
if( pBlob ){
210223
blob_compress(pBlob, &cmpr);
211224
db_bind_blob(&s1, ":data", &cmpr);
212225
}
@@ -215,10 +228,16 @@
215228
if( !pBlob ){
216229
db_multi_exec("INSERT OR IGNORE INTO phantom VALUES(%d)", rid);
217230
}
218231
}
219232
233
+ /* If the srcId is specified, then the data we just added is
234
+ ** really a delta. Record this fact in the delta table.
235
+ */
236
+ if( srcId ){
237
+ db_multi_exec("REPLACE INTO delta(rid,srcid) VALUES(%d,%d)", rid, srcId);
238
+ }
220239
221240
/* Finish the transaction and cleanup */
222241
db_finalize(&s1);
223242
db_end_transaction(0);
224243
blob_reset(&hash);
@@ -242,30 +261,32 @@
242261
Blob content;
243262
if( g.argc!=3 ) usage("FILENAME");
244263
db_must_be_within_tree();
245264
user_select();
246265
blob_read_from_file(&content, g.argv[2]);
247
- rid = content_put(&content, 0);
266
+ rid = content_put(&content, 0, 0);
248267
printf("inserted as record %d\n", rid);
249268
}
250269
251270
/*
252271
** Make sure the content at rid is the original content and is not a
253272
** delta.
254273
*/
255274
void content_undelta(int rid){
256
- if( findSrcid(rid, 0)>0 ){
275
+ if( findSrcid(rid)>0 ){
257276
Blob x;
258
- Stmt s;
259
- content_get(rid, &x);
260
- db_prepare(&s, "UPDATE blob SET content=:c WHERE rid=%d", rid);
261
- blob_compress(&x, &x);
262
- db_bind_blob(&s, ":c", &x);
263
- db_exec(&s);
264
- db_finalize(&s);
265
- blob_reset(&x);
266
- db_multi_exec("DELETE FROM delta WHERE rid=%d", rid);
277
+ if( content_get(rid, &x) ){
278
+ Stmt s;
279
+ db_prepare(&s, "UPDATE blob SET content=:c, size=%d WHERE rid=%d",
280
+ blob_size(&x), rid);
281
+ blob_compress(&x, &x);
282
+ db_bind_blob(&s, ":c", &x);
283
+ db_exec(&s);
284
+ db_finalize(&s);
285
+ blob_reset(&x);
286
+ db_multi_exec("DELETE FROM delta WHERE rid=%d", rid);
287
+ }
267288
}
268289
}
269290
270291
/*
271292
** COMMAND: test-content-undelta
@@ -292,13 +313,13 @@
292313
void content_deltify(int rid, int srcid, int force){
293314
int s;
294315
Blob data, src, delta;
295316
Stmt s1, s2;
296317
if( srcid==rid ) return;
297
- if( !force && findSrcid(rid, 0)>0 ) return;
318
+ if( !force && findSrcid(rid)>0 ) return;
298319
s = srcid;
299
- while( (s = findSrcid(s, 0))>0 ){
320
+ while( (s = findSrcid(s))>0 ){
300321
if( s==rid ){
301322
content_undelta(srcid);
302323
break;
303324
}
304325
}
305326
--- src/content.c
+++ src/content.c
@@ -29,73 +29,54 @@
29
30 /*
31 ** Return the srcid associated with rid. Or return 0 if rid is
32 ** original content and not a delta.
33 */
34 static int findSrcid(int rid, const char *zDb){
35 Stmt qsrc;
36 int srcid;
37 if( zDb ){
38 db_prepare(&qsrc, "SELECT srcid FROM %s.delta WHERE rid=%d", zDb, rid);
39 }else{
40 db_prepare(&qsrc, "SELECT srcid FROM delta WHERE rid=%d", rid);
41 }
42 if( db_step(&qsrc)==SQLITE_ROW ){
43 srcid = db_column_int(&qsrc, 0);
44 }else{
45 srcid = 0;
46 }
47 db_finalize(&qsrc);
48 return srcid;
49 }
50
51 /*
52 ** Extract the content for ID rid and put it into the
53 ** uninitialized blob.
 
54 */
55 void content_get_from_db(int rid, Blob *pBlob, const char *zDb){
56 Stmt q;
 
57 int srcid;
 
 
58 assert( g.repositoryOpen );
59 srcid = findSrcid(rid, zDb);
60 if( zDb ){
61 db_prepare(&q,
62 "SELECT content FROM %s.blob WHERE rid=%d AND size>=0",
63 zDb, rid
64 );
65 }else{
66 db_prepare(&q,
67 "SELECT content FROM blob WHERE rid=%d AND size>=0",
68 rid
69 );
70 }
71 if( srcid ){
72 Blob src;
73 content_get_from_db(srcid, &src, zDb);
74 if( db_step(&q)==SQLITE_ROW ){
75 Blob delta;
76 db_ephemeral_blob(&q, 0, &delta);
77 blob_uncompress(&delta, &delta);
78 blob_init(pBlob,0,0);
79 blob_delta_apply(&src, &delta, pBlob);
80 blob_reset(&delta);
81 }else{
82 blob_init(pBlob, 0, 0);
83 }
84 blob_reset(&src);
85 }else{
 
 
86 if( db_step(&q)==SQLITE_ROW ){
87 db_ephemeral_blob(&q, 0, pBlob);
88 blob_uncompress(pBlob, pBlob);
89 }else{
90 blob_init(pBlob,0, 0);
91 }
92 }
93 db_finalize(&q);
94 }
95 void content_get(int rid, Blob *pBlob){
96 content_get_from_db(rid, pBlob, 0);
97 }
98
99 /*
100 ** COMMAND: test-content-get
101 **
@@ -130,33 +111,62 @@
130 blob_zero(&content);
131 db_blob(&content, "SELECT content FROM blob WHERE rid=%d", rid);
132 blob_uncompress(&content, &content);
133 blob_write_to_file(&content, zFile);
134 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
135
136 /*
137 ** Write content into the database. Return the record ID. If the
138 ** content is already in the database, just return the record ID.
139 **
140 ** A phantom is written if pBlob==0. If pBlob==0 then the UUID is set
141 ** to zUuid. Otherwise zUuid is ignored.
 
 
 
 
 
142 **
143 ** If the record already exists but is a phantom, the pBlob content
144 ** is inserted and the phatom becomes a real record.
145 */
146 int content_put(Blob *pBlob, const char *zUuid){
147 int size;
148 int rid;
149 Stmt s1;
150 Blob cmpr;
151 Blob hash;
152 assert( g.repositoryOpen );
153 if( pBlob==0 ){
 
 
154 blob_init(&hash, zUuid, -1);
 
 
155 size = -1;
156 }else{
157 sha1sum_blob(pBlob, &hash);
158 size = blob_size(pBlob);
159 }
160 db_begin_transaction();
161
162 /* Check to see if the entry already exists and if it does whether
@@ -197,16 +207,19 @@
197 );
198 blob_compress(pBlob, &cmpr);
199 db_bind_blob(&s1, ":data", &cmpr);
200 db_exec(&s1);
201 db_multi_exec("DELETE FROM phantom WHERE rid=%d", rid);
 
 
 
202 }else{
203 /* We are creating a new entry */
204 db_prepare(&s1,
205 "INSERT INTO blob(rcvid,size,uuid,content)"
206 "VALUES(%d,%d,'%s',:data)",
207 g.rcvid, size, blob_str(&hash)
208 );
209 if( pBlob ){
210 blob_compress(pBlob, &cmpr);
211 db_bind_blob(&s1, ":data", &cmpr);
212 }
@@ -215,10 +228,16 @@
215 if( !pBlob ){
216 db_multi_exec("INSERT OR IGNORE INTO phantom VALUES(%d)", rid);
217 }
218 }
219
 
 
 
 
 
 
220
221 /* Finish the transaction and cleanup */
222 db_finalize(&s1);
223 db_end_transaction(0);
224 blob_reset(&hash);
@@ -242,30 +261,32 @@
242 Blob content;
243 if( g.argc!=3 ) usage("FILENAME");
244 db_must_be_within_tree();
245 user_select();
246 blob_read_from_file(&content, g.argv[2]);
247 rid = content_put(&content, 0);
248 printf("inserted as record %d\n", rid);
249 }
250
251 /*
252 ** Make sure the content at rid is the original content and is not a
253 ** delta.
254 */
255 void content_undelta(int rid){
256 if( findSrcid(rid, 0)>0 ){
257 Blob x;
258 Stmt s;
259 content_get(rid, &x);
260 db_prepare(&s, "UPDATE blob SET content=:c WHERE rid=%d", rid);
261 blob_compress(&x, &x);
262 db_bind_blob(&s, ":c", &x);
263 db_exec(&s);
264 db_finalize(&s);
265 blob_reset(&x);
266 db_multi_exec("DELETE FROM delta WHERE rid=%d", rid);
 
 
267 }
268 }
269
270 /*
271 ** COMMAND: test-content-undelta
@@ -292,13 +313,13 @@
292 void content_deltify(int rid, int srcid, int force){
293 int s;
294 Blob data, src, delta;
295 Stmt s1, s2;
296 if( srcid==rid ) return;
297 if( !force && findSrcid(rid, 0)>0 ) return;
298 s = srcid;
299 while( (s = findSrcid(s, 0))>0 ){
300 if( s==rid ){
301 content_undelta(srcid);
302 break;
303 }
304 }
305
--- src/content.c
+++ src/content.c
@@ -29,73 +29,54 @@
29
30 /*
31 ** Return the srcid associated with rid. Or return 0 if rid is
32 ** original content and not a delta.
33 */
34 static int findSrcid(int rid){
35 int srcid = db_int(0, "SELECT srcid FROM delta WHERE rid=%d", rid);
 
 
 
 
 
 
 
 
 
 
 
 
36 return srcid;
37 }
38
39 /*
40 ** Extract the content for ID rid and put it into the
41 ** uninitialized blob. Return 1 on success. If the record
42 ** is a phantom, zero pBlob and return 0.
43 */
44 int content_get(int rid, Blob *pBlob){
45 Stmt q;
46 Blob src;
47 int srcid;
48 int rc = 0;
49
50 assert( g.repositoryOpen );
51 srcid = findSrcid(rid);
52 blob_zero(pBlob);
 
 
 
 
 
 
 
 
 
 
53 if( srcid ){
54 if( content_get(srcid, &src) ){
55 db_prepare(&q, "SELECT content FROM blob WHERE rid=%d AND size>=0", rid);
56 if( db_step(&q)==SQLITE_ROW ){
57 Blob delta;
58 db_ephemeral_blob(&q, 0, &delta);
59 blob_uncompress(&delta, &delta);
60 blob_init(pBlob,0,0);
61 blob_delta_apply(&src, &delta, pBlob);
62 blob_reset(&delta);
63 rc = 1;
64 }
65 db_finalize(&q);
66 blob_reset(&src);
67 }
68 }else{
69 db_prepare(&q, "SELECT content FROM blob WHERE rid=%d AND size>=0", rid);
70 if( db_step(&q)==SQLITE_ROW ){
71 db_ephemeral_blob(&q, 0, pBlob);
72 blob_uncompress(pBlob, pBlob);
73 rc = 1;
74 }
75 db_finalize(&q);
76 }
77 return rc;
 
 
 
78 }
79
80 /*
81 ** COMMAND: test-content-get
82 **
@@ -130,33 +111,62 @@
111 blob_zero(&content);
112 db_blob(&content, "SELECT content FROM blob WHERE rid=%d", rid);
113 blob_uncompress(&content, &content);
114 blob_write_to_file(&content, zFile);
115 }
116
117 /*
118 ** When a record is converted from a phantom to a real record,
119 ** if that record has other records that are derived by delta,
120 ** then call manifest_crosslink() on those other records.
121 */
122 void after_dephantomize(int rid, int linkFlag){
123 Stmt q;
124 db_prepare(&q, "SELECT rid FROM delta WHERE srcid=%d", rid);
125 while( db_step(&q)==SQLITE_ROW ){
126 int tid = db_column_int(&q, 0);
127 after_dephantomize(tid, 1);
128 }
129 db_finalize(&q);
130 if( linkFlag ){
131 Blob content;
132 content_get(rid, &content);
133 manifest_crosslink(rid, &content);
134 blob_reset(&content);
135 }
136 }
137
138 /*
139 ** Write content into the database. Return the record ID. If the
140 ** content is already in the database, just return the record ID.
141 **
142 ** If srcId is specified, then pBlob is delta content from
143 ** the srcId record. srcId might be a phantom.
144 **
145 ** A phantom is written if pBlob==0. If pBlob==0 or if srcId is
146 ** specified then the UUID is set to zUuid. Otherwise zUuid is
147 ** ignored. In the future this might change such that the content
148 ** hash is checked against zUuid to make sure it is correct.
149 **
150 ** If the record already exists but is a phantom, the pBlob content
151 ** is inserted and the phatom becomes a real record.
152 */
153 int content_put(Blob *pBlob, const char *zUuid, int srcId){
154 int size;
155 int rid;
156 Stmt s1;
157 Blob cmpr;
158 Blob hash;
159 assert( g.repositoryOpen );
160 if( pBlob && srcId==0 ){
161 sha1sum_blob(pBlob, &hash);
162 }else{
163 blob_init(&hash, zUuid, -1);
164 }
165 if( pBlob==0 ){
166 size = -1;
167 }else{
 
168 size = blob_size(pBlob);
169 }
170 db_begin_transaction();
171
172 /* Check to see if the entry already exists and if it does whether
@@ -197,16 +207,19 @@
207 );
208 blob_compress(pBlob, &cmpr);
209 db_bind_blob(&s1, ":data", &cmpr);
210 db_exec(&s1);
211 db_multi_exec("DELETE FROM phantom WHERE rid=%d", rid);
212 if( srcId==0 || db_int(0, "SELECT size FROM blob WHERE rid=%d", srcId)>0 ){
213 after_dephantomize(rid, 0);
214 }
215 }else{
216 /* We are creating a new entry */
217 db_prepare(&s1,
218 "INSERT INTO blob(rcvid,size,uuid,content)"
219 "VALUES(%d,%d,'%b',:data)",
220 g.rcvid, size, &hash
221 );
222 if( pBlob ){
223 blob_compress(pBlob, &cmpr);
224 db_bind_blob(&s1, ":data", &cmpr);
225 }
@@ -215,10 +228,16 @@
228 if( !pBlob ){
229 db_multi_exec("INSERT OR IGNORE INTO phantom VALUES(%d)", rid);
230 }
231 }
232
233 /* If the srcId is specified, then the data we just added is
234 ** really a delta. Record this fact in the delta table.
235 */
236 if( srcId ){
237 db_multi_exec("REPLACE INTO delta(rid,srcid) VALUES(%d,%d)", rid, srcId);
238 }
239
240 /* Finish the transaction and cleanup */
241 db_finalize(&s1);
242 db_end_transaction(0);
243 blob_reset(&hash);
@@ -242,30 +261,32 @@
261 Blob content;
262 if( g.argc!=3 ) usage("FILENAME");
263 db_must_be_within_tree();
264 user_select();
265 blob_read_from_file(&content, g.argv[2]);
266 rid = content_put(&content, 0, 0);
267 printf("inserted as record %d\n", rid);
268 }
269
270 /*
271 ** Make sure the content at rid is the original content and is not a
272 ** delta.
273 */
274 void content_undelta(int rid){
275 if( findSrcid(rid)>0 ){
276 Blob x;
277 if( content_get(rid, &x) ){
278 Stmt s;
279 db_prepare(&s, "UPDATE blob SET content=:c, size=%d WHERE rid=%d",
280 blob_size(&x), rid);
281 blob_compress(&x, &x);
282 db_bind_blob(&s, ":c", &x);
283 db_exec(&s);
284 db_finalize(&s);
285 blob_reset(&x);
286 db_multi_exec("DELETE FROM delta WHERE rid=%d", rid);
287 }
288 }
289 }
290
291 /*
292 ** COMMAND: test-content-undelta
@@ -292,13 +313,13 @@
313 void content_deltify(int rid, int srcid, int force){
314 int s;
315 Blob data, src, delta;
316 Stmt s1, s2;
317 if( srcid==rid ) return;
318 if( !force && findSrcid(rid)>0 ) return;
319 s = srcid;
320 while( (s = findSrcid(s))>0 ){
321 if( s==rid ){
322 content_undelta(srcid);
323 break;
324 }
325 }
326
+1 -32
--- src/db.c
+++ src/db.c
@@ -699,11 +699,11 @@
699699
blob_appendf(&manifest, "R %s\n", md5sum_finish(0));
700700
blob_appendf(&manifest, "U %F\n", g.zLogin);
701701
md5sum_blob(&manifest, &hash);
702702
blob_appendf(&manifest, "Z %b\n", &hash);
703703
blob_reset(&hash);
704
- content_put(&manifest, 0);
704
+ content_put(&manifest, 0, 0);
705705
db_end_transaction(0);
706706
printf("project-id: %s\n", db_get("project-code", 0));
707707
printf("server-id: %s\n", db_get("server-code", 0));
708708
printf("admin-user: %s (no password set yet!)\n", g.zLogin);
709709
printf("baseline: %s\n", db_text(0, "SELECT uuid FROM blob"));
@@ -817,41 +817,10 @@
817817
return db_int(dflt, "SELECT value FROM vvar WHERE name=%Q", zName);
818818
}
819819
void db_lset_int(const char *zName, int value){
820820
db_multi_exec("REPLACE INTO vvar(name,value) VALUES(%Q,%d)", zName, value);
821821
}
822
-
823
-int db_row_to_table(const char *zFormat, ...){
824
- Stmt q;
825
- va_list ap;
826
- int rc;
827
-
828
- va_start(ap, zFormat);
829
- rc = db_vprepare(&q, zFormat, ap);
830
- va_end(ap);
831
- if( rc!=SQLITE_OK ){
832
- return rc;
833
- }
834
-
835
- @ <table border="0" cellpadding="0" cellspacing="0">
836
- if( db_step(&q)==SQLITE_ROW ){
837
- int ii;
838
- for(ii=0; ii<sqlite3_column_count(q.pStmt); ii++){
839
- char *zCol = htmlize(sqlite3_column_name(q.pStmt, ii), -1);
840
- char *zVal = htmlize(sqlite3_column_text(q.pStmt, ii), -1);
841
-
842
- @ <tr><td align=right>%s(zCol):<td width=10><td>%s(zVal)
843
-
844
- free(zVal);
845
- free(zCol);
846
- }
847
- }
848
- @ </table>
849
-
850
- return db_finalize(&q);
851
-}
852
-
853822
854823
/*
855824
** COMMAND: open
856825
**
857826
** Create a new local repository.
858827
--- src/db.c
+++ src/db.c
@@ -699,11 +699,11 @@
699 blob_appendf(&manifest, "R %s\n", md5sum_finish(0));
700 blob_appendf(&manifest, "U %F\n", g.zLogin);
701 md5sum_blob(&manifest, &hash);
702 blob_appendf(&manifest, "Z %b\n", &hash);
703 blob_reset(&hash);
704 content_put(&manifest, 0);
705 db_end_transaction(0);
706 printf("project-id: %s\n", db_get("project-code", 0));
707 printf("server-id: %s\n", db_get("server-code", 0));
708 printf("admin-user: %s (no password set yet!)\n", g.zLogin);
709 printf("baseline: %s\n", db_text(0, "SELECT uuid FROM blob"));
@@ -817,41 +817,10 @@
817 return db_int(dflt, "SELECT value FROM vvar WHERE name=%Q", zName);
818 }
819 void db_lset_int(const char *zName, int value){
820 db_multi_exec("REPLACE INTO vvar(name,value) VALUES(%Q,%d)", zName, value);
821 }
822
823 int db_row_to_table(const char *zFormat, ...){
824 Stmt q;
825 va_list ap;
826 int rc;
827
828 va_start(ap, zFormat);
829 rc = db_vprepare(&q, zFormat, ap);
830 va_end(ap);
831 if( rc!=SQLITE_OK ){
832 return rc;
833 }
834
835 @ <table border="0" cellpadding="0" cellspacing="0">
836 if( db_step(&q)==SQLITE_ROW ){
837 int ii;
838 for(ii=0; ii<sqlite3_column_count(q.pStmt); ii++){
839 char *zCol = htmlize(sqlite3_column_name(q.pStmt, ii), -1);
840 char *zVal = htmlize(sqlite3_column_text(q.pStmt, ii), -1);
841
842 @ <tr><td align=right>%s(zCol):<td width=10><td>%s(zVal)
843
844 free(zVal);
845 free(zCol);
846 }
847 }
848 @ </table>
849
850 return db_finalize(&q);
851 }
852
853
854 /*
855 ** COMMAND: open
856 **
857 ** Create a new local repository.
858
--- src/db.c
+++ src/db.c
@@ -699,11 +699,11 @@
699 blob_appendf(&manifest, "R %s\n", md5sum_finish(0));
700 blob_appendf(&manifest, "U %F\n", g.zLogin);
701 md5sum_blob(&manifest, &hash);
702 blob_appendf(&manifest, "Z %b\n", &hash);
703 blob_reset(&hash);
704 content_put(&manifest, 0, 0);
705 db_end_transaction(0);
706 printf("project-id: %s\n", db_get("project-code", 0));
707 printf("server-id: %s\n", db_get("server-code", 0));
708 printf("admin-user: %s (no password set yet!)\n", g.zLogin);
709 printf("baseline: %s\n", db_text(0, "SELECT uuid FROM blob"));
@@ -817,41 +817,10 @@
817 return db_int(dflt, "SELECT value FROM vvar WHERE name=%Q", zName);
818 }
819 void db_lset_int(const char *zName, int value){
820 db_multi_exec("REPLACE INTO vvar(name,value) VALUES(%Q,%d)", zName, value);
821 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
822
823 /*
824 ** COMMAND: open
825 **
826 ** Create a new local repository.
827
+1 -1
--- src/manifest.c
+++ src/manifest.c
@@ -289,11 +289,11 @@
289289
manifest_clear(&other);
290290
}
291291
292292
/*
293293
** Scan record rid/pContent to see if it is a manifest. If
294
-** it is a manifest, then populate tables the mlink, plink,
294
+** it is a manifest, then populate the mlink, plink,
295295
** filename, and event tables with cross-reference information.
296296
*/
297297
int manifest_crosslink(int rid, Blob *pContent){
298298
int i;
299299
Manifest m;
300300
--- src/manifest.c
+++ src/manifest.c
@@ -289,11 +289,11 @@
289 manifest_clear(&other);
290 }
291
292 /*
293 ** Scan record rid/pContent to see if it is a manifest. If
294 ** it is a manifest, then populate tables the mlink, plink,
295 ** filename, and event tables with cross-reference information.
296 */
297 int manifest_crosslink(int rid, Blob *pContent){
298 int i;
299 Manifest m;
300
--- src/manifest.c
+++ src/manifest.c
@@ -289,11 +289,11 @@
289 manifest_clear(&other);
290 }
291
292 /*
293 ** Scan record rid/pContent to see if it is a manifest. If
294 ** it is a manifest, then populate the mlink, plink,
295 ** filename, and event tables with cross-reference information.
296 */
297 int manifest_crosslink(int rid, Blob *pContent){
298 int i;
299 Manifest m;
300
+1 -1
--- src/vfile.c
+++ src/vfile.c
@@ -49,11 +49,11 @@
4949
}
5050
strcpy(z, zUuid);
5151
canonical16(z, sz);
5252
rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", z);
5353
if( rid==0 && phantomize ){
54
- rid = content_put(0, zUuid);
54
+ rid = content_put(0, zUuid, 0);
5555
}
5656
return rid;
5757
}
5858
5959
/*
6060
--- src/vfile.c
+++ src/vfile.c
@@ -49,11 +49,11 @@
49 }
50 strcpy(z, zUuid);
51 canonical16(z, sz);
52 rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", z);
53 if( rid==0 && phantomize ){
54 rid = content_put(0, zUuid);
55 }
56 return rid;
57 }
58
59 /*
60
--- src/vfile.c
+++ src/vfile.c
@@ -49,11 +49,11 @@
49 }
50 strcpy(z, zUuid);
51 canonical16(z, sz);
52 rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", z);
53 if( rid==0 && phantomize ){
54 rid = content_put(0, zUuid, 0);
55 }
56 return rid;
57 }
58
59 /*
60
+350 -419
--- src/xfer.c
+++ src/xfer.c
@@ -25,109 +25,43 @@
2525
*/
2626
#include "config.h"
2727
#include "xfer.h"
2828
2929
/*
30
-** Try to locate a record that is similar to rid and is a likely
31
-** candidate for delta against rid. The similar record must be
32
-** referenced in the onremote table.
33
-**
34
-** Return the integer record ID of the similar record. Or return
35
-** 0 if none is found.
30
+** This structure holds information about the current state of either
31
+** a client or a server that is participating in xfer.
3632
*/
37
-static int similar_record(int rid, int traceFlag){
38
- int inCnt, outCnt;
39
- int i;
40
- Stmt q;
41
- int queue[100];
42
- static const char *azQuery[] = {
43
- /* Scan the delta table first */
44
- "SELECT srcid, EXISTS(SELECT 1 FROM onremote WHERE rid=srcid)"
45
- " FROM delta"
46
- " WHERE rid=:x"
47
- " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=srcid)"
48
- " UNION ALL "
49
- "SELECT rid, EXISTS(SELECT 1 FROM onremote WHERE rid=delta.rid)"
50
- " FROM delta"
51
- " WHERE srcid=:x"
52
- " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=delta.rid)",
53
-
54
- /* Then the plink table */
55
- "SELECT pid, EXISTS(SELECT 1 FROM onremote WHERE rid=pid)"
56
- " FROM plink"
57
- " WHERE cid=:x"
58
- " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=pid)"
59
- " UNION ALL "
60
- "SELECT cid, EXISTS(SELECT 1 FROM onremote WHERE rid=cid)"
61
- " FROM plink"
62
- " WHERE pid=:x"
63
- " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=cid)",
64
-
65
- /* Finally the mlink table */
66
- "SELECT pid, EXISTS(SELECT 1 FROM onremote WHERE rid=pid)"
67
- " FROM mlink"
68
- " WHERE fid=:x AND pid>0"
69
- " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=pid)"
70
- " UNION ALL "
71
- "SELECT fid, EXISTS(SELECT 1 FROM onremote WHERE rid=fid)"
72
- " FROM mlink"
73
- " WHERE pid=:x AND fid>0"
74
- " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=fid)",
75
- };
76
-
77
- for(i=0; i<sizeof(azQuery)/sizeof(azQuery[0]); i++){
78
- db_prepare(&q, azQuery[i]);
79
- queue[0] = rid;
80
- inCnt = 1;
81
- outCnt = 0;
82
- if( traceFlag ) printf("PASS %d\n", i+1);
83
- while( outCnt<inCnt ){
84
- int xid = queue[outCnt%64];
85
- outCnt++;
86
- db_bind_int(&q, ":x", xid);
87
- if( traceFlag ) printf("xid=%d\n", xid);
88
- while( db_step(&q)==SQLITE_ROW ){
89
- int nid = db_column_int(&q, 0);
90
- int hit = db_column_int(&q, 1);
91
- if( traceFlag ) printf("nid=%d hit=%d\n", nid, hit);
92
- if( hit ){
93
- db_finalize(&q);
94
- return nid;
95
- }
96
- if( inCnt<sizeof(queue)/sizeof(queue[0]) ){
97
- int i;
98
- for(i=0; i<inCnt && queue[i]!=nid; i++){}
99
- if( i>=inCnt ){
100
- queue[inCnt++] = nid;
101
- }
102
- }
103
- }
104
- db_reset(&q);
105
- }
106
- db_finalize(&q);
107
- }
108
- return 0;
109
-}
33
+typedef struct Xfer Xfer;
34
+struct Xfer {
35
+ Blob *pIn; /* Input text from the other side */
36
+ Blob *pOut; /* Compose our reply here */
37
+ Blob line; /* The current line of input */
38
+ Blob aToken[5]; /* Tokenized version of line */
39
+ Blob err; /* Error message text */
40
+ int nToken; /* Number of tokens in line */
41
+ int nIGot; /* Number of "igot" messages sent */
42
+ int nFile; /* Number of files sent or received */
43
+ int nDelta; /* Number of deltas sent or received */
44
+ int nDanglingFile; /* Number of dangling deltas received */
45
+ int mxSend; /* Stop sending "file" with pOut reaches this size */
46
+};
47
+
11048
11149
/*
112
-** COMMAND: test-similar-record
50
+** The input blob contains a UUID. Convert it into a record ID.
51
+** Create a phantom record if no prior record exists and
52
+** phantomize is true.
53
+**
54
+** Compare to uuid_to_rid(). This routine takes a blob argument
55
+** and does less error checking.
11356
*/
114
-void test_similar_record(void){
115
- int i;
116
- if( g.argc<4 ){
117
- usage("SRC ONREMOTE...");
118
- }
119
- db_must_be_within_tree();
120
- db_multi_exec(
121
- "CREATE TEMP TABLE onremote(rid INTEGER PRIMARY KEY);"
122
- );
123
- for(i=3; i<g.argc; i++){
124
- int rid = name_to_rid(g.argv[i]);
125
- printf("%s -> %d\n", g.argv[i], rid);
126
- db_multi_exec("INSERT INTO onremote VALUES(%d)", rid);
127
- }
128
- printf("similar: %d\n", similar_record(name_to_rid(g.argv[2]), 1));
57
+static int rid_from_uuid(Blob *pUuid, int phantomize){
58
+ int rid = db_int(0, "SELECT rid FROM blob WHERE uuid='%b'", pUuid);
59
+ if( rid==0 && phantomize ){
60
+ rid = content_put(0, blob_str(pUuid), 0);
61
+ }
62
+ return rid;
12963
}
13064
13165
13266
/*
13367
** The aToken[0..nToken-1] blob array is a parse of a "file" line
@@ -144,162 +78,227 @@
14478
** content of DELTASRC.
14579
**
14680
** If any error occurs, write a message into pErr which has already
14781
** be initialized to an empty string.
14882
*/
149
-static void xfer_accept_file(Blob *pIn, Blob *aToken, int nToken, Blob *pErr){
83
+static void xfer_accept_file(Xfer *pXfer){
15084
int n;
15185
int rid;
15286
Blob content, hash;
15387
154
- if( nToken<3 || nToken>4 || !blob_is_uuid(&aToken[1])
155
- || !blob_is_int(&aToken[nToken-1], &n) || n<=0
156
- || (nToken==4 && !blob_is_uuid(&aToken[2])) ){
157
- blob_appendf(pErr, "malformed file line");
88
+ if( pXfer->nToken<3
89
+ || pXfer->nToken>4
90
+ || !blob_is_uuid(&pXfer->aToken[1])
91
+ || !blob_is_int(&pXfer->aToken[pXfer->nToken-1], &n)
92
+ || n<=0
93
+ || (pXfer->nToken==4 && !blob_is_uuid(&pXfer->aToken[2]))
94
+ ){
95
+ blob_appendf(&pXfer->err, "malformed file line");
15896
return;
15997
}
16098
blob_zero(&content);
16199
blob_zero(&hash);
162
- blob_extract(pIn, n, &content);
163
- if( nToken==4 ){
100
+ blob_extract(pXfer->pIn, n, &content);
101
+ if( pXfer->nToken==4 ){
164102
Blob src;
165
- int srcid = db_int(0, "SELECT rid FROM blob WHERE uuid=%B", &aToken[2]);
166
- if( srcid==0 ){
167
- blob_appendf(pErr, "unknown delta source: %b", &aToken[2]);
103
+ int srcid = rid_from_uuid(&pXfer->aToken[2], 1);
104
+ if( content_get(srcid, &src)==0 ){
105
+ content_put(&content, blob_str(&hash), srcid);
106
+ blob_appendf(pXfer->pOut, "gimme %b\n", &pXfer->aToken[2]);
107
+ pXfer->nDanglingFile++;
168108
return;
169109
}
170
- content_get(srcid, &src);
110
+ pXfer->nDelta++;
171111
blob_delta_apply(&src, &content, &content);
172112
blob_reset(&src);
113
+ }else{
114
+ pXfer->nFile++;
173115
}
174116
sha1sum_blob(&content, &hash);
175
- if( !blob_eq_str(&aToken[1], blob_str(&hash), -1) ){
176
- blob_appendf(pErr, "content does not match sha1 hash");
117
+ if( !blob_eq_str(&pXfer->aToken[1], blob_str(&hash), -1) ){
118
+ blob_appendf(&pXfer->err, "content does not match sha1 hash");
177119
}
178120
blob_reset(&hash);
179
- rid = content_put(&content, 0);
180
- manifest_crosslink(rid, &content);
121
+ rid = content_put(&content, 0, 0);
181122
if( rid==0 ){
182
- blob_appendf(pErr, "%s", g.zErrMsg);
123
+ blob_appendf(&pXfer->err, "%s", g.zErrMsg);
124
+ }else{
125
+ manifest_crosslink(rid, &content);
126
+ }
127
+}
128
+
129
+/*
130
+** Try to send a file as a delta. If successful, return the number
131
+** of bytes in the delta. If not, return zero.
132
+**
133
+** If srcId is specified, use it. If not, try to figure out a
134
+** reasonable srcId.
135
+*/
136
+static int send_as_delta(
137
+ Xfer *pXfer, /* The transfer context */
138
+ int rid, /* record id of the file to send */
139
+ Blob *pContent, /* The content of the file to send */
140
+ Blob *pUuid, /* The UUID of the file to send */
141
+ int srcId /* Send as a delta against this record */
142
+){
143
+ static const char *azQuery[] = {
144
+ "SELECT srcid FROM delta JOIN pending ON pending.rid=delta.srcid"
145
+ " WHERE delta.rid=%d"
146
+ " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=srcid)",
147
+
148
+ "SELECT delta.rid FROM delta JOIN pending ON pending.rid=delta.rid"
149
+ " WHERE srcid=%d"
150
+ " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=delta.rid)",
151
+
152
+ "SELECT pid FROM plink JOIN pending ON rid=pid"
153
+ " WHERE cid=%d"
154
+ " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=pid)",
155
+
156
+ "SELECT cid FROM plink JOIN pending ON rid=cid"
157
+ " WHERE pid=%d"
158
+ " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=cid)",
159
+
160
+ "SELECT pid FROM mlink JOIN pending ON rid=pid"
161
+ " WHERE fid=%d"
162
+ " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=pid)",
163
+
164
+ "SELECT fid FROM mlink JOIN pending ON rid=fid"
165
+ " WHERE pid=%d"
166
+ " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=fid)",
167
+ };
168
+ int i;
169
+ Blob src, delta;
170
+ int size = 0;
171
+
172
+ for(i=0; srcId==0 && i<count(azQuery); i++){
173
+ srcId = db_int(0, azQuery[i], rid);
174
+ }
175
+ if( srcId && content_get(srcId, &src) ){
176
+ char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", srcId);
177
+ blob_delta_create(&src, pContent, &delta);
178
+ size = blob_size(&delta);
179
+ if( size>=blob_size(pContent)-50 ){
180
+ size = 0;
181
+ }else{
182
+ blob_appendf(pXfer->pOut, "file %b %s %d\n", pUuid, zUuid, size);
183
+ blob_append(pXfer->pOut, blob_buffer(&delta), size);
184
+ blob_appendf(pXfer->pOut, "\n", 1);
185
+ }
186
+ blob_reset(&delta);
187
+ free(zUuid);
188
+ blob_reset(&src);
183189
}
190
+ return size;
184191
}
185192
186193
/*
187194
** Send the file identified by rid.
188
-**
189
-** If pOut is not NULL, then append the text of the send message
190
-** to pOut. Otherwise, append the text to the CGI output.
191195
*/
192
-static int send_file(int rid, Blob *pOut){
196
+static void send_file(Xfer *pXfer, int rid, Blob *pUuid, int srcId){
193197
Blob content, uuid;
194
- int size;
195
- int srcid;
198
+ int size = 0;
196199
197
-
200
+ if( db_exists("SELECT 1 FROM sent WHERE rid=%d", rid) ){
201
+ return;
202
+ }
198203
blob_zero(&uuid);
199
- db_blob(&uuid, "SELECT uuid FROM blob WHERE rid=%d AND size>=0", rid);
200
- if( blob_size(&uuid)==0 ){
201
- return 0;
204
+ if( pUuid==0 ){
205
+ db_blob(&uuid, "SELECT uuid FROM blob WHERE rid=%d AND size>=0", rid);
206
+ if( blob_size(&uuid)==0 ){
207
+ return;
208
+ }
209
+ pUuid = &uuid;
210
+ }
211
+ if( pXfer->mxSend<=blob_size(pXfer->pOut) ){
212
+ blob_appendf(pXfer->pOut, "igot %b\n", pUuid);
213
+ pXfer->nIGot++;
214
+ blob_reset(&uuid);
215
+ return;
202216
}
203217
content_get(rid, &content);
204218
205219
if( blob_size(&content)>100 ){
206
- srcid = similar_record(rid, 0);
207
- if( srcid ){
208
- Blob src;
209
- content_get(srcid, &src);
210
- if( blob_size(&src)>100 ){
211
- Blob delta;
212
- blob_delta_create(&src, &content, &delta);
213
- blob_reset(&content);
214
- content = delta;
215
- blob_append(&uuid, " ", 1);
216
- blob_append(&content, "\n", 1);
217
- db_blob(&uuid, "SELECT uuid FROM blob WHERE rid=%d", srcid);
218
- }
219
- blob_reset(&src);
220
- }
221
- }
222
- size = blob_size(&content);
223
- if( pOut ){
224
- blob_appendf(pOut, "file %b %d\n", &uuid, size);
225
- blob_append(pOut, blob_buffer(&content), size);
226
- }else{
227
- cgi_printf("file %b %d\n", &uuid, size);
228
- cgi_append_content(blob_buffer(&content), size);
229
- }
230
- blob_reset(&content);
231
- blob_reset(&uuid);
232
- db_multi_exec("INSERT OR IGNORE INTO onremote VALUES(%d)", rid);
233
- return size;
234
-}
235
-
236
-
237
-/*
238
-** Send all pending files.
239
-*/
240
-static int send_all_pending(Blob *pOut){
241
- int rid, xid, i;
242
- int nIgot = 0;
243
- int sent = 0;
244
- int nSent = 0;
245
- int maxSize = db_get_int("http-msg-size", 500000);
246
- static const char *azQuery[] = {
247
- "SELECT srcid FROM delta JOIN pending ON pending.rid=delta.srcid"
248
- " WHERE delta.rid=%d"
249
- " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=srcid)",
250
-
251
- "SELECT delta.rid FROM delta JOIN pending ON pending.rid=delta.rid"
252
- " WHERE srcid=%d"
253
- " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=delta.rid)",
254
-
255
- "SELECT pid FROM plink JOIN pending ON rid=pid"
256
- " WHERE cid=%d"
257
- " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=pid)",
258
-
259
- "SELECT cid FROM plink JOIN pending ON rid=cid"
260
- " WHERE pid=%d"
261
- " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=cid)",
262
-
263
- "SELECT pid FROM mlink JOIN pending ON rid=pid"
264
- " WHERE fid=%d"
265
- " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=pid)",
266
-
267
- "SELECT fid FROM mlink JOIN pending ON rid=fid"
268
- " WHERE pid=%d"
269
- " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=fid)",
270
- };
271
-
272
- rid = db_int(0, "SELECT rid FROM pending");
273
- while( rid && nIgot<200 ){
274
- db_multi_exec("DELETE FROM pending WHERE rid=%d", rid);
275
- if( sent<maxSize ){
276
- sent += send_file(rid, pOut);
277
- nSent++;
278
- }else{
279
- char *zUuid = db_text(0,
280
- "SELECT uuid FROM blob WHERE rid=%d AND size>=0", rid);
281
- if( zUuid ){
282
- if( pOut ){
283
- blob_appendf(pOut, "igot %s\n", zUuid);
284
- }else{
285
- cgi_printf("igot %s\n", zUuid);
286
- }
287
- free(zUuid);
288
- nIgot++;
289
- }
290
- }
291
- xid = 0;
292
- for(i=0; xid==0 && i<sizeof(azQuery)/sizeof(azQuery[0]); i++){
293
- xid = db_int(0, azQuery[i], rid);
294
- }
295
- rid = xid;
296
- if( rid==0 ){
297
- rid = db_int(0, "SELECT rid FROM pending");
298
- }
299
- }
300
- return nSent;
220
+ size = send_as_delta(pXfer, rid, &content, pUuid, srcId);
221
+ }
222
+ if( size==0 ){
223
+ int size = blob_size(&content);
224
+ blob_appendf(pXfer->pOut, "file %b %d\n", pUuid, size);
225
+ blob_append(pXfer->pOut, blob_buffer(&content), size);
226
+ pXfer->nFile++;
227
+ }else{
228
+ pXfer->nDelta++;
229
+ }
230
+ db_multi_exec("INSERT INTO sent VALUES(%d)", rid);
231
+ blob_reset(&uuid);
232
+}
233
+
234
+/*
235
+** This routine runs when either client or server is notified that
236
+** the other side things rid is a leaf manifest. If we hold
237
+** children of rid, then send them over to the other side.
238
+*/
239
+static void leaf_response(Xfer *pXfer, int rid){
240
+ Stmt q1, q2;
241
+ db_prepare(&q1,
242
+ "SELECT cid, uuid FROM plink, blob"
243
+ " WHERE blob.rid=plink.cid"
244
+ " AND plink.pid=%d",
245
+ rid
246
+ );
247
+ while( db_step(&q1)==SQLITE_ROW ){
248
+ Blob uuid;
249
+ int cid;
250
+
251
+ cid = db_column_int(&q1, 0);
252
+ db_ephemeral_blob(&q1, 1, &uuid);
253
+ send_file(pXfer, cid, &uuid, rid);
254
+ db_prepare(&q2,
255
+ "SELECT pid, uuid, fid FROM mlink, blob"
256
+ " WHERE rid=fid AND mid=%d",
257
+ cid
258
+ );
259
+ while( db_step(&q2)==SQLITE_ROW ){
260
+ int pid, fid;
261
+ pid = db_column_int(&q2, 0);
262
+ db_ephemeral_blob(&q2, 1, &uuid);
263
+ fid = db_column_int(&q2, 2);
264
+ send_file(pXfer, fid, &uuid, pid);
265
+ }
266
+ db_finalize(&q2);
267
+ if( blob_size(pXfer->pOut)<pXfer->mxSend ){
268
+ leaf_response(pXfer, cid);
269
+ }
270
+ }
271
+}
272
+
273
+/*
274
+** Sent a leaf message for every leaf.
275
+*/
276
+static void send_leaves(Xfer *pXfer){
277
+ Stmt q;
278
+ db_prepare(&q,
279
+ "SELECT uuid FROM blob WHERE rid IN"
280
+ " (SELECT cid FROM plink EXCEPT SELECT pid FROM plink)"
281
+ );
282
+ while( db_step(&q)==SQLITE_ROW ){
283
+ const char *zUuid = db_column_text(&q, 0);
284
+ blob_appendf(pXfer->pOut, "leaf %s\n", zUuid);
285
+ }
286
+ db_finalize(&q);
287
+}
288
+
289
+/*
290
+** Sen a gimme message for every phantom.
291
+*/
292
+static void request_phantoms(Xfer *pXfer){
293
+ Stmt q;
294
+ db_prepare(&q, "SELECT uuid FROM phantom JOIN blob USING(rid)");
295
+ while( db_step(&q)==SQLITE_ROW ){
296
+ const char *zUuid = db_column_text(&q, 0);
297
+ blob_appendf(pXfer->pOut, "gimme %s\n", zUuid);
298
+ }
299
+ db_finalize(&q);
301300
}
302301
303302
304303
/*
305304
** Check the signature on an application/x-fossil payload received by
@@ -372,116 +371,126 @@
372371
void page_xfer(void){
373372
int nToken;
374373
int isPull = 0;
375374
int isPush = 0;
376375
int nErr = 0;
377
- Blob line, errmsg, aToken[5];
376
+ Xfer xfer;
377
+
378
+ memset(&xfer, 0, sizeof(xfer));
379
+ blobarray_zero(xfer.aToken, count(xfer.aToken));
380
+ cgi_set_content_type(g.zContentType);
381
+ blob_zero(&xfer.err);
382
+ xfer.pIn = &g.cgiIn;
383
+ xfer.pOut = cgi_output_blob();
378384
379385
db_begin_transaction();
380
- blobarray_zero(aToken, count(aToken));
381
- cgi_set_content_type(g.zContentType);
382
- blob_zero(&errmsg);
383386
db_multi_exec(
384
- "CREATE TEMP TABLE onremote(rid INTEGER PRIMARY KEY);" /* Client has */
385
- "CREATE TEMP TABLE pending(rid INTEGER PRIMARY KEY);" /* Client needs */
387
+ "CREATE TEMP TABLE sent(rid INTEGER PRIMARY KEY);"
386388
);
387
- while( blob_line(&g.cgiIn, &line) ){
388
- nToken = blob_tokenize(&line, aToken, count(aToken));
389
+ while( blob_line(xfer.pIn, &xfer.line) ){
390
+ xfer.nToken = blob_tokenize(&xfer.line, xfer.aToken, count(xfer.aToken));
389391
390392
/* file UUID SIZE \n CONTENT
391393
** file UUID DELTASRC SIZE \n CONTENT
392394
**
393395
** Accept a file from the client.
394396
*/
395
- if( blob_eq(&aToken[0], "file") && nToken>=3 && nToken<=4 ){
397
+ if( blob_eq(&xfer.aToken[0], "file") ){
396398
if( !isPush ){
397399
cgi_reset_content();
398400
@ error not\sauthorized\sto\swrite
399401
nErr++;
400402
break;
401403
}
402
- xfer_accept_file(&g.cgiIn, aToken, nToken, &errmsg);
403
- if( blob_size(&errmsg) ){
404
+ xfer_accept_file(&xfer);
405
+ if( blob_size(&xfer.err) ){
404406
cgi_reset_content();
405
- @ error %T(blob_str(&errmsg))
407
+ @ error %T(blob_str(&xfer.err))
406408
nErr++;
407409
break;
408410
}
409411
}else
410412
411413
/* gimme UUID
412414
**
413415
** Client is requesting a file
414416
*/
415
- if( blob_eq(&aToken[0], "gimme") && nToken==2 && blob_is_uuid(&aToken[1]) ){
417
+ if( blob_eq(&xfer.aToken[0], "gimme")
418
+ && xfer.nToken==2
419
+ && blob_is_uuid(&xfer.aToken[1])
420
+ ){
416421
if( isPull ){
417
- db_multi_exec(
418
- "INSERT OR IGNORE INTO pending(rid) "
419
- "SELECT rid FROM blob WHERE uuid=%B AND size>=0", &aToken[1]
420
- );
422
+ int rid = rid_from_uuid(&xfer.aToken[1], 0);
423
+ if( rid ){
424
+ send_file(&xfer, rid, &xfer.aToken[1], 0);
425
+ }
421426
}
422427
}else
423428
424429
/* igot UUID
425
- ** leaf UUID
426430
**
427431
** Client announces that it has a particular file
428432
*/
429
- if( nToken==2
430
- && (blob_eq(&aToken[0], "igot") || blob_eq(&aToken[0],"leaf"))
431
- && blob_is_uuid(&aToken[1]) ){
432
- if( isPull || isPush ){
433
- int rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%B", &aToken[1]);
434
- if( rid>0 ){
435
- db_multi_exec(
436
- "INSERT OR IGNORE INTO onremote(rid) VALUES(%d)", rid
437
- );
438
- if( isPull && blob_eq(&aToken[0], "leaf") ){
439
- db_multi_exec(
440
- "INSERT OR IGNORE INTO pending(rid) "
441
- "SELECT cid FROM plink WHERE pid=%d", rid
442
- );
443
- }
444
- }else if( isPush ){
445
- content_put(0, blob_str(&aToken[1]));
446
- }
433
+ if( xfer.nToken==2
434
+ && blob_eq(&xfer.aToken[0], "igot")
435
+ && blob_is_uuid(&xfer.aToken[1])
436
+ ){
437
+ if( isPush ){
438
+ rid_from_uuid(&xfer.aToken[1], 1);
439
+ }
440
+ }else
441
+
442
+
443
+ /* leaf UUID
444
+ **
445
+ ** Client announces that it has a particular manifest
446
+ */
447
+ if( xfer.nToken==2
448
+ && blob_eq(&xfer.aToken[0], "leaf")
449
+ && blob_is_uuid(&xfer.aToken[1])
450
+ ){
451
+ if( isPull ){
452
+ int rid = rid_from_uuid(&xfer.aToken[1], 0);
453
+ leaf_response(&xfer, rid);
447454
}
448455
}else
449456
450457
/* pull SERVERCODE PROJECTCODE
451458
** push SERVERCODE PROJECTCODE
452459
**
453460
** The client wants either send or receive
454461
*/
455462
if( nToken==3
456
- && (blob_eq(&aToken[0], "pull") || blob_eq(&aToken[0], "push"))
457
- && blob_is_uuid(&aToken[1]) && blob_is_uuid(&aToken[2]) ){
463
+ && (blob_eq(&xfer.aToken[0], "pull") || blob_eq(&xfer.aToken[0], "push"))
464
+ && blob_is_uuid(&xfer.aToken[1])
465
+ && blob_is_uuid(&xfer.aToken[2])
466
+ ){
458467
const char *zSCode;
459468
const char *zPCode;
460469
461470
zSCode = db_get("server-code", 0);
462471
if( zSCode==0 ){
463472
fossil_panic("missing server code");
464473
}
465
- if( blob_eq_str(&aToken[1], zSCode, -1) ){
474
+ if( blob_eq_str(&xfer.aToken[1], zSCode, -1) ){
466475
cgi_reset_content();
467476
@ error server\sloop
468477
nErr++;
469478
break;
470479
}
471480
zPCode = db_get("project-code", 0);
472481
if( zPCode==0 ){
473482
fossil_panic("missing project code");
474483
}
475
- if( !blob_eq_str(&aToken[2], zPCode, -1) ){
484
+ if( !blob_eq_str(&xfer.aToken[2], zPCode, -1) ){
476485
cgi_reset_content();
477486
@ error wrong\sproject
478487
nErr++;
479488
break;
480489
}
481490
login_check_credentials();
482
- if( blob_eq(&aToken[0], "pull") ){
491
+ if( blob_eq(&xfer.aToken[0], "pull") ){
483492
if( !g.okRead ){
484493
cgi_reset_content();
485494
@ error not\sauthorized\sto\sread
486495
nErr++;
487496
break;
@@ -492,91 +501,57 @@
492501
cgi_reset_content();
493502
@ error not\sauthorized\sto\swrite
494503
nErr++;
495504
break;
496505
}
506
+ send_leaves(&xfer);
497507
isPush = 1;
498
-
499508
}
500509
}else
501510
502511
/* clone
503512
**
504513
** The client knows nothing. Tell all.
505514
*/
506
- if( blob_eq(&aToken[0], "clone") ){
515
+ if( blob_eq(&xfer.aToken[0], "clone") ){
507516
login_check_credentials();
508517
if( !g.okRead || !g.okHistory ){
509518
cgi_reset_content();
510519
@ error not\sauthorized\sto\sclone
511520
nErr++;
512521
break;
513522
}
514523
isPull = 1;
515524
@ push %s(db_get("server-code", "x")) %s(db_get("project-code", "x"))
516
- db_multi_exec(
517
- "INSERT OR IGNORE INTO pending(rid) "
518
- "SELECT mid FROM mlink JOIN blob ON mid=rid"
519
- );
525
+ send_leaves(&xfer);
520526
}else
521527
522528
/* login USER NONCE SIGNATURE
523529
**
524530
** Check for a valid login. This has to happen before anything else.
525531
*/
526
- if( blob_eq(&aToken[0], "login") && nToken==4 ){
532
+ if( blob_eq(&xfer.aToken[0], "login")
533
+ && nToken==4
534
+ ){
527535
if( disableLogin ){
528536
g.okRead = g.okWrite = 1;
529537
}else{
530
- check_login(&aToken[1], &aToken[2], &aToken[3]);
538
+ check_login(&xfer.aToken[1], &xfer.aToken[2], &xfer.aToken[3]);
531539
}
532540
}else
533541
534542
/* Unknown message
535543
*/
536544
{
537545
cgi_reset_content();
538
- @ error bad\scommand:\s%F(blob_str(&line))
546
+ @ error bad\scommand:\s%F(blob_str(&xfer.line))
539547
}
540
- blobarray_reset(aToken, nToken);
548
+ blobarray_reset(xfer.aToken, xfer.nToken);
541549
}
542
-
543
- /* The input message has now been processed. Generate a reply. */
544550
if( isPush ){
545
- Stmt q;
546
- int nReq = 0;
547
- db_prepare(&q, "SELECT uuid, rid FROM phantom JOIN blob USING (rid)");
548
- while( db_step(&q)==SQLITE_ROW && nReq++ < 200 ){
549
- const char *zUuid = db_column_text(&q, 0);
550
- int rid = db_column_int(&q, 1);
551
- int xid = similar_record(rid, 0);
552
- @ gimme %s(zUuid)
553
- if( xid ){
554
- char *zXUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", xid);
555
- @ igot %s(zXUuid);
556
- free(zXUuid);
557
- }
558
- }
559
- db_finalize(&q);
560
- }
561
- if( isPull ){
562
- send_all_pending(0);
563
- }
564
- if( isPush || isPull ){
565
- /* Always send our leaves */
566
- Stmt q;
567
- db_prepare(&q,
568
- "SELECT uuid FROM blob WHERE rid IN"
569
- " (SELECT cid FROM plink EXCEPT SELECT pid FROM plink)"
570
- );
571
- while( db_step(&q)==SQLITE_ROW ){
572
- const char *zUuid = db_column_text(&q, 0);
573
- @ leaf %s(zUuid)
574
- }
575
- db_finalize(&q);
576
- }
577
-
551
+ request_phantoms(&xfer);
552
+ }
578553
db_end_transaction(0);
579554
}
580555
581556
/*
582557
** COMMAND: test-xfer
@@ -620,43 +595,40 @@
620595
** are pulled if pullFlag is true. A full sync occurs if both are
621596
** true.
622597
*/
623598
void client_sync(int pushFlag, int pullFlag, int cloneFlag){
624599
int go = 1; /* Loop until zero */
625
- int nToken;
626600
const char *zSCode = db_get("server-code", "x");
627601
const char *zPCode = db_get("project-code", 0);
628
- int nFile = 0;
629602
int nMsg = 0;
630603
int nReq = 0;
631
- int nFileSend;
604
+ int nFileSend = 0;
632605
int nNoFileCycle = 0;
633606
Blob send; /* Text we are sending to the server */
634607
Blob recv; /* Reply we got back from the server */
635
- Blob line; /* A single line of the reply */
636
- Blob aToken[5]; /* A tokenization of line */
637
- Blob errmsg; /* Error message */
608
+ Xfer xfer; /* Transfer data */
609
+
610
+ memset(&xfer, 0, sizeof(xfer));
611
+ xfer.pIn = &recv;
612
+ xfer.pOut = &send;
638613
639614
assert( pushFlag || pullFlag || cloneFlag );
640615
assert( !g.urlIsFile ); /* This only works for networking */
641616
642617
db_begin_transaction();
643618
db_multi_exec(
644
- /* Records which we know the other side also has */
645
- "CREATE TEMP TABLE onremote(rid INTEGER PRIMARY KEY);"
646
- /* Records we know the other side needs */
647
- "CREATE TEMP TABLE pending(rid INTEGER PRIMARY KEY);"
619
+ "CREATE TEMP TABLE sent(rid INTEGER PRIMARY KEY);"
648620
);
649
- blobarray_zero(aToken, count(aToken));
621
+ blobarray_zero(xfer.aToken, count(xfer.aToken));
650622
blob_zero(&send);
651623
blob_zero(&recv);
652
- blob_zero(&errmsg);
624
+ blob_zero(&xfer.err);
653625
654626
655627
while( go ){
656628
go = 0;
657
- nFile = nReq = nMsg = 0;
629
+ nReq = nMsg = 0;
658630
659631
/* Generate a request to be sent to the server.
660632
** Always begin with a clone, pull, or push message
661633
*/
662634
@@ -665,143 +637,102 @@
665637
pushFlag = 0;
666638
pullFlag = 0;
667639
nMsg++;
668640
}else if( pullFlag ){
669641
blob_appendf(&send, "pull %s %s\n", zSCode, zPCode);
642
+ send_leaves(&xfer);
643
+ request_phantoms(&xfer);
670644
nMsg++;
671645
}
672646
if( pushFlag ){
673647
blob_appendf(&send, "push %s %s\n", zSCode, zPCode);
674648
nMsg++;
675649
}
676650
677
- if( pullFlag ){
678
- /* Send gimme message for every phantom that we hold.
679
- */
680
- Stmt q;
681
- db_prepare(&q, "SELECT uuid, rid FROM phantom JOIN blob USING (rid)");
682
- while( db_step(&q)==SQLITE_ROW && nReq<200 ){
683
- const char *zUuid = db_column_text(&q, 0);
684
- int rid = db_column_int(&q, 1);
685
- int xid = similar_record(rid, 0);
686
- blob_appendf(&send,"gimme %s\n", zUuid);
687
- nReq++;
688
- if( xid ){
689
- blob_appendf(&send, "igot %z\n",
690
- db_text(0, "SELECT uuid FROM blob WHERE rid=%d", xid));
691
- }
692
- }
693
- db_finalize(&q);
694
- }
695
-
696
- if( pushFlag ){
697
- /* Send the server any files that the server has requested */
698
- nFile += send_all_pending(&send);
699
- }
700
-
701
- if( pullFlag || pushFlag ){
702
- /* Always send our leaves */
703
- Stmt q;
704
- db_prepare(&q,
705
- "SELECT uuid FROM blob WHERE rid IN"
706
- " (SELECT cid FROM plink EXCEPT SELECT pid FROM plink)"
707
- );
708
- while( db_step(&q)==SQLITE_ROW ){
709
- const char *zUuid = db_column_text(&q, 0);
710
- blob_appendf(&send, "leaf %s\n", zUuid);
711
- nMsg++;
712
- }
713
- db_finalize(&q);
714
- }
715
-
716651
/* Exchange messages with the server */
652
+ nFileSend = xfer.nFile + xfer.nDelta + xfer.nDanglingFile;
717653
printf("Send: %3d files, %3d requests, %3d other msgs, %8d bytes\n",
718
- nFile, nReq, nMsg, blob_size(&send));
719
- nFileSend = nFile;
720
- nFile = nReq = nMsg = 0;
654
+ nFileSend, nReq, nMsg, blob_size(&send));
655
+ xfer.nFile = 0;
656
+ xfer.nDelta = 0;
657
+ xfer.nDanglingFile = 0;
658
+ nReq = nMsg = 0;
721659
http_exchange(&send, &recv);
722660
blob_reset(&send);
723661
724662
/* Process the reply that came back from the server */
725
- while( blob_line(&recv, &line) ){
726
- nToken = blob_tokenize(&line, aToken, count(aToken));
663
+ while( blob_line(&recv, &xfer.line) ){
664
+ xfer.nToken = blob_tokenize(&xfer.line, xfer.aToken, count(xfer.aToken));
727665
728666
/* file UUID SIZE \n CONTENT
729667
** file UUID DELTASRC SIZE \n CONTENT
730668
**
731669
** Receive a file transmitted from the other side
732670
*/
733
- if( blob_eq(&aToken[0],"file") ){
734
- xfer_accept_file(&recv, aToken, nToken, &errmsg);
735
- nFile++;
736
- go = 1;
671
+ if( blob_eq(&xfer.aToken[0],"file") ){
672
+ xfer_accept_file(&xfer);
737673
}else
738674
739675
/* gimme UUID
740676
**
741677
** Server is requesting a file
742678
*/
743
- if( blob_eq(&aToken[0], "gimme") && nToken==2
744
- && blob_is_uuid(&aToken[1]) ){
745
- nReq++;
679
+ if( blob_eq(&xfer.aToken[0], "gimme")
680
+ && xfer.nToken==2
681
+ && blob_is_uuid(&xfer.aToken[1])
682
+ ){
746683
if( pushFlag ){
747
- db_multi_exec(
748
- "INSERT OR IGNORE INTO pending(rid) "
749
- "SELECT rid FROM blob WHERE uuid=%B AND size>=0", &aToken[1]
750
- );
751
- go = 1;
684
+ int rid = rid_from_uuid(&xfer.aToken[1], 0);
685
+ send_file(&xfer, rid, &xfer.aToken[1], 0);
752686
}
753687
}else
754688
755689
/* igot UUID
756
- ** leaf UUID
690
+ **
691
+ ** Server announces that it has a particular file
692
+ */
693
+ if( xfer.nToken==2
694
+ && blob_eq(&xfer.aToken[0], "igot")
695
+ && blob_is_uuid(&xfer.aToken[1])
696
+ ){
697
+ if( pullFlag ){
698
+ rid_from_uuid(&xfer.aToken[1], 1);
699
+ }
700
+ }else
701
+
702
+
703
+ /* leaf UUID
757704
**
758
- ** Server proclaims that it has a particular file. A leaf message
759
- ** means that the file is a leaf manifest on the server.
705
+ ** Server announces that it has a particular manifest
760706
*/
761
- if( nToken==2
762
- && (blob_eq(&aToken[0], "igot") || blob_eq(&aToken[0], "leaf"))
763
- && blob_is_uuid(&aToken[1]) ){
764
- int rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%B", &aToken[1]);
765
- nMsg++;
766
- if( rid>0 ){
767
- db_multi_exec(
768
- "INSERT OR IGNORE INTO onremote(rid) VALUES(%d)", rid
769
- );
770
- /* Add to the pending set all children of the server's leaves */
771
- if( pushFlag && blob_eq(&aToken[0], "leaf") ){
772
- db_multi_exec(
773
- "INSERT OR IGNORE INTO pending(rid) "
774
- "SELECT cid FROM plink WHERE pid=%d", rid
775
- );
776
- if( db_changes()>0 ){
777
- go = 1;
778
- }
779
- }
780
- if( pullFlag && !go &&
781
- db_exists("SELECT 1 FROM phantom WHERE rid=%d", rid) ){
782
- go = 1;
783
- }
784
- }else if( pullFlag ){
785
- go = 1;
786
- content_put(0, blob_str(&aToken[1]));
707
+ if( xfer.nToken==2
708
+ && blob_eq(&xfer.aToken[0], "leaf")
709
+ && blob_is_uuid(&xfer.aToken[1])
710
+ ){
711
+ if( pushFlag ){
712
+ int rid = rid_from_uuid(&xfer.aToken[1], 0);
713
+ leaf_response(&xfer, rid);
787714
}
788715
}else
716
+
789717
790718
/* push SERVERCODE PRODUCTCODE
791719
**
792720
** Should only happen in response to a clone.
793721
*/
794
- if( blob_eq(&aToken[0],"push") && nToken==3 && cloneFlag
795
- && blob_is_uuid(&aToken[1]) && blob_is_uuid(&aToken[2]) ){
796
-
797
- if( blob_eq_str(&aToken[1], zSCode, -1) ){
722
+ if( blob_eq(&xfer.aToken[0],"push")
723
+ && xfer.nToken==3
724
+ && cloneFlag
725
+ && blob_is_uuid(&xfer.aToken[1])
726
+ && blob_is_uuid(&xfer.aToken[2])
727
+ ){
728
+ if( blob_eq_str(&xfer.aToken[1], zSCode, -1) ){
798729
fossil_fatal("server loop");
799730
}
800731
nMsg++;
801732
if( zPCode==0 ){
802
- zPCode = mprintf("%b", &aToken[2]);
733
+ zPCode = mprintf("%b", &xfer.aToken[2]);
803734
db_set("project-code", zPCode);
804735
}
805736
cloneFlag = 0;
806737
pullFlag = 1;
807738
}else
@@ -808,41 +739,41 @@
808739
809740
/* error MESSAGE
810741
**
811742
** Report an error
812743
*/
813
- if( blob_eq(&aToken[0],"error") && nToken==2 ){
814
- char *zMsg = blob_terminate(&aToken[1]);
744
+ if( blob_eq(&xfer.aToken[0],"error") && xfer.nToken==2 ){
745
+ char *zMsg = blob_terminate(&xfer.aToken[1]);
815746
defossilize(zMsg);
816
- blob_appendf(&errmsg, "server says: %s", zMsg);
747
+ blob_appendf(&xfer.err, "server says: %s", zMsg);
817748
}else
818749
819750
/* Unknown message */
820751
{
821
- blob_appendf(&errmsg, "unknown command: %b", &aToken[0]);
752
+ blob_appendf(&xfer.err, "unknown command: %b", &xfer.aToken[0]);
822753
}
823754
824
- if( blob_size(&errmsg) ){
825
- fossil_fatal("%b", &errmsg);
755
+ if( blob_size(&xfer.err) ){
756
+ fossil_fatal("%b", &xfer.err);
826757
}
827
- blobarray_reset(aToken, nToken);
758
+ blobarray_reset(xfer.aToken, xfer.nToken);
828759
}
829760
printf("Received: %3d files, %3d requests, %3d other msgs, %8d bytes\n",
830
- nFile, nReq, nMsg, blob_size(&recv));
761
+ xfer.nFile + xfer.nDelta + xfer.nDanglingFile,
762
+ nReq, nMsg, blob_size(&recv));
831763
blob_reset(&recv);
832
- if( nFileSend + nFile==0 ){
764
+ if( nFileSend + xfer.nFile + xfer.nDelta + xfer.nDanglingFile==0 ){
833765
nNoFileCycle++;
834766
if( nNoFileCycle>1 ){
835767
go = 0;
836768
}
837769
}else{
838770
nNoFileCycle = 0;
839771
}
840
- nFile = nReq = nMsg = 0;
772
+ nReq = nMsg = 0;
773
+ xfer.nFile = 0;
774
+ xfer.nDelta = 0;
775
+ xfer.nDanglingFile = 0;
841776
};
842777
http_close();
843778
db_end_transaction(0);
844
- db_multi_exec(
845
- "DROP TABLE onremote;"
846
- "DROP TABLE pending;"
847
- );
848779
}
849780
--- src/xfer.c
+++ src/xfer.c
@@ -25,109 +25,43 @@
25 */
26 #include "config.h"
27 #include "xfer.h"
28
29 /*
30 ** Try to locate a record that is similar to rid and is a likely
31 ** candidate for delta against rid. The similar record must be
32 ** referenced in the onremote table.
33 **
34 ** Return the integer record ID of the similar record. Or return
35 ** 0 if none is found.
36 */
37 static int similar_record(int rid, int traceFlag){
38 int inCnt, outCnt;
39 int i;
40 Stmt q;
41 int queue[100];
42 static const char *azQuery[] = {
43 /* Scan the delta table first */
44 "SELECT srcid, EXISTS(SELECT 1 FROM onremote WHERE rid=srcid)"
45 " FROM delta"
46 " WHERE rid=:x"
47 " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=srcid)"
48 " UNION ALL "
49 "SELECT rid, EXISTS(SELECT 1 FROM onremote WHERE rid=delta.rid)"
50 " FROM delta"
51 " WHERE srcid=:x"
52 " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=delta.rid)",
53
54 /* Then the plink table */
55 "SELECT pid, EXISTS(SELECT 1 FROM onremote WHERE rid=pid)"
56 " FROM plink"
57 " WHERE cid=:x"
58 " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=pid)"
59 " UNION ALL "
60 "SELECT cid, EXISTS(SELECT 1 FROM onremote WHERE rid=cid)"
61 " FROM plink"
62 " WHERE pid=:x"
63 " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=cid)",
64
65 /* Finally the mlink table */
66 "SELECT pid, EXISTS(SELECT 1 FROM onremote WHERE rid=pid)"
67 " FROM mlink"
68 " WHERE fid=:x AND pid>0"
69 " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=pid)"
70 " UNION ALL "
71 "SELECT fid, EXISTS(SELECT 1 FROM onremote WHERE rid=fid)"
72 " FROM mlink"
73 " WHERE pid=:x AND fid>0"
74 " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=fid)",
75 };
76
77 for(i=0; i<sizeof(azQuery)/sizeof(azQuery[0]); i++){
78 db_prepare(&q, azQuery[i]);
79 queue[0] = rid;
80 inCnt = 1;
81 outCnt = 0;
82 if( traceFlag ) printf("PASS %d\n", i+1);
83 while( outCnt<inCnt ){
84 int xid = queue[outCnt%64];
85 outCnt++;
86 db_bind_int(&q, ":x", xid);
87 if( traceFlag ) printf("xid=%d\n", xid);
88 while( db_step(&q)==SQLITE_ROW ){
89 int nid = db_column_int(&q, 0);
90 int hit = db_column_int(&q, 1);
91 if( traceFlag ) printf("nid=%d hit=%d\n", nid, hit);
92 if( hit ){
93 db_finalize(&q);
94 return nid;
95 }
96 if( inCnt<sizeof(queue)/sizeof(queue[0]) ){
97 int i;
98 for(i=0; i<inCnt && queue[i]!=nid; i++){}
99 if( i>=inCnt ){
100 queue[inCnt++] = nid;
101 }
102 }
103 }
104 db_reset(&q);
105 }
106 db_finalize(&q);
107 }
108 return 0;
109 }
110
111 /*
112 ** COMMAND: test-similar-record
 
 
 
 
 
113 */
114 void test_similar_record(void){
115 int i;
116 if( g.argc<4 ){
117 usage("SRC ONREMOTE...");
118 }
119 db_must_be_within_tree();
120 db_multi_exec(
121 "CREATE TEMP TABLE onremote(rid INTEGER PRIMARY KEY);"
122 );
123 for(i=3; i<g.argc; i++){
124 int rid = name_to_rid(g.argv[i]);
125 printf("%s -> %d\n", g.argv[i], rid);
126 db_multi_exec("INSERT INTO onremote VALUES(%d)", rid);
127 }
128 printf("similar: %d\n", similar_record(name_to_rid(g.argv[2]), 1));
129 }
130
131
132 /*
133 ** The aToken[0..nToken-1] blob array is a parse of a "file" line
@@ -144,162 +78,227 @@
144 ** content of DELTASRC.
145 **
146 ** If any error occurs, write a message into pErr which has already
147 ** be initialized to an empty string.
148 */
149 static void xfer_accept_file(Blob *pIn, Blob *aToken, int nToken, Blob *pErr){
150 int n;
151 int rid;
152 Blob content, hash;
153
154 if( nToken<3 || nToken>4 || !blob_is_uuid(&aToken[1])
155 || !blob_is_int(&aToken[nToken-1], &n) || n<=0
156 || (nToken==4 && !blob_is_uuid(&aToken[2])) ){
157 blob_appendf(pErr, "malformed file line");
 
 
 
 
158 return;
159 }
160 blob_zero(&content);
161 blob_zero(&hash);
162 blob_extract(pIn, n, &content);
163 if( nToken==4 ){
164 Blob src;
165 int srcid = db_int(0, "SELECT rid FROM blob WHERE uuid=%B", &aToken[2]);
166 if( srcid==0 ){
167 blob_appendf(pErr, "unknown delta source: %b", &aToken[2]);
 
 
168 return;
169 }
170 content_get(srcid, &src);
171 blob_delta_apply(&src, &content, &content);
172 blob_reset(&src);
 
 
173 }
174 sha1sum_blob(&content, &hash);
175 if( !blob_eq_str(&aToken[1], blob_str(&hash), -1) ){
176 blob_appendf(pErr, "content does not match sha1 hash");
177 }
178 blob_reset(&hash);
179 rid = content_put(&content, 0);
180 manifest_crosslink(rid, &content);
181 if( rid==0 ){
182 blob_appendf(pErr, "%s", g.zErrMsg);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
183 }
 
184 }
185
186 /*
187 ** Send the file identified by rid.
188 **
189 ** If pOut is not NULL, then append the text of the send message
190 ** to pOut. Otherwise, append the text to the CGI output.
191 */
192 static int send_file(int rid, Blob *pOut){
193 Blob content, uuid;
194 int size;
195 int srcid;
196
197
 
 
198 blob_zero(&uuid);
199 db_blob(&uuid, "SELECT uuid FROM blob WHERE rid=%d AND size>=0", rid);
200 if( blob_size(&uuid)==0 ){
201 return 0;
 
 
 
 
 
 
 
 
 
202 }
203 content_get(rid, &content);
204
205 if( blob_size(&content)>100 ){
206 srcid = similar_record(rid, 0);
207 if( srcid ){
208 Blob src;
209 content_get(srcid, &src);
210 if( blob_size(&src)>100 ){
211 Blob delta;
212 blob_delta_create(&src, &content, &delta);
213 blob_reset(&content);
214 content = delta;
215 blob_append(&uuid, " ", 1);
216 blob_append(&content, "\n", 1);
217 db_blob(&uuid, "SELECT uuid FROM blob WHERE rid=%d", srcid);
218 }
219 blob_reset(&src);
220 }
221 }
222 size = blob_size(&content);
223 if( pOut ){
224 blob_appendf(pOut, "file %b %d\n", &uuid, size);
225 blob_append(pOut, blob_buffer(&content), size);
226 }else{
227 cgi_printf("file %b %d\n", &uuid, size);
228 cgi_append_content(blob_buffer(&content), size);
229 }
230 blob_reset(&content);
231 blob_reset(&uuid);
232 db_multi_exec("INSERT OR IGNORE INTO onremote VALUES(%d)", rid);
233 return size;
234 }
235
236
237 /*
238 ** Send all pending files.
239 */
240 static int send_all_pending(Blob *pOut){
241 int rid, xid, i;
242 int nIgot = 0;
243 int sent = 0;
244 int nSent = 0;
245 int maxSize = db_get_int("http-msg-size", 500000);
246 static const char *azQuery[] = {
247 "SELECT srcid FROM delta JOIN pending ON pending.rid=delta.srcid"
248 " WHERE delta.rid=%d"
249 " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=srcid)",
250
251 "SELECT delta.rid FROM delta JOIN pending ON pending.rid=delta.rid"
252 " WHERE srcid=%d"
253 " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=delta.rid)",
254
255 "SELECT pid FROM plink JOIN pending ON rid=pid"
256 " WHERE cid=%d"
257 " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=pid)",
258
259 "SELECT cid FROM plink JOIN pending ON rid=cid"
260 " WHERE pid=%d"
261 " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=cid)",
262
263 "SELECT pid FROM mlink JOIN pending ON rid=pid"
264 " WHERE fid=%d"
265 " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=pid)",
266
267 "SELECT fid FROM mlink JOIN pending ON rid=fid"
268 " WHERE pid=%d"
269 " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=fid)",
270 };
271
272 rid = db_int(0, "SELECT rid FROM pending");
273 while( rid && nIgot<200 ){
274 db_multi_exec("DELETE FROM pending WHERE rid=%d", rid);
275 if( sent<maxSize ){
276 sent += send_file(rid, pOut);
277 nSent++;
278 }else{
279 char *zUuid = db_text(0,
280 "SELECT uuid FROM blob WHERE rid=%d AND size>=0", rid);
281 if( zUuid ){
282 if( pOut ){
283 blob_appendf(pOut, "igot %s\n", zUuid);
284 }else{
285 cgi_printf("igot %s\n", zUuid);
286 }
287 free(zUuid);
288 nIgot++;
289 }
290 }
291 xid = 0;
292 for(i=0; xid==0 && i<sizeof(azQuery)/sizeof(azQuery[0]); i++){
293 xid = db_int(0, azQuery[i], rid);
294 }
295 rid = xid;
296 if( rid==0 ){
297 rid = db_int(0, "SELECT rid FROM pending");
298 }
299 }
300 return nSent;
301 }
302
303
304 /*
305 ** Check the signature on an application/x-fossil payload received by
@@ -372,116 +371,126 @@
372 void page_xfer(void){
373 int nToken;
374 int isPull = 0;
375 int isPush = 0;
376 int nErr = 0;
377 Blob line, errmsg, aToken[5];
 
 
 
 
 
 
 
378
379 db_begin_transaction();
380 blobarray_zero(aToken, count(aToken));
381 cgi_set_content_type(g.zContentType);
382 blob_zero(&errmsg);
383 db_multi_exec(
384 "CREATE TEMP TABLE onremote(rid INTEGER PRIMARY KEY);" /* Client has */
385 "CREATE TEMP TABLE pending(rid INTEGER PRIMARY KEY);" /* Client needs */
386 );
387 while( blob_line(&g.cgiIn, &line) ){
388 nToken = blob_tokenize(&line, aToken, count(aToken));
389
390 /* file UUID SIZE \n CONTENT
391 ** file UUID DELTASRC SIZE \n CONTENT
392 **
393 ** Accept a file from the client.
394 */
395 if( blob_eq(&aToken[0], "file") && nToken>=3 && nToken<=4 ){
396 if( !isPush ){
397 cgi_reset_content();
398 @ error not\sauthorized\sto\swrite
399 nErr++;
400 break;
401 }
402 xfer_accept_file(&g.cgiIn, aToken, nToken, &errmsg);
403 if( blob_size(&errmsg) ){
404 cgi_reset_content();
405 @ error %T(blob_str(&errmsg))
406 nErr++;
407 break;
408 }
409 }else
410
411 /* gimme UUID
412 **
413 ** Client is requesting a file
414 */
415 if( blob_eq(&aToken[0], "gimme") && nToken==2 && blob_is_uuid(&aToken[1]) ){
 
 
 
416 if( isPull ){
417 db_multi_exec(
418 "INSERT OR IGNORE INTO pending(rid) "
419 "SELECT rid FROM blob WHERE uuid=%B AND size>=0", &aToken[1]
420 );
421 }
422 }else
423
424 /* igot UUID
425 ** leaf UUID
426 **
427 ** Client announces that it has a particular file
428 */
429 if( nToken==2
430 && (blob_eq(&aToken[0], "igot") || blob_eq(&aToken[0],"leaf"))
431 && blob_is_uuid(&aToken[1]) ){
432 if( isPull || isPush ){
433 int rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%B", &aToken[1]);
434 if( rid>0 ){
435 db_multi_exec(
436 "INSERT OR IGNORE INTO onremote(rid) VALUES(%d)", rid
437 );
438 if( isPull && blob_eq(&aToken[0], "leaf") ){
439 db_multi_exec(
440 "INSERT OR IGNORE INTO pending(rid) "
441 "SELECT cid FROM plink WHERE pid=%d", rid
442 );
443 }
444 }else if( isPush ){
445 content_put(0, blob_str(&aToken[1]));
446 }
 
 
 
447 }
448 }else
449
450 /* pull SERVERCODE PROJECTCODE
451 ** push SERVERCODE PROJECTCODE
452 **
453 ** The client wants either send or receive
454 */
455 if( nToken==3
456 && (blob_eq(&aToken[0], "pull") || blob_eq(&aToken[0], "push"))
457 && blob_is_uuid(&aToken[1]) && blob_is_uuid(&aToken[2]) ){
 
 
458 const char *zSCode;
459 const char *zPCode;
460
461 zSCode = db_get("server-code", 0);
462 if( zSCode==0 ){
463 fossil_panic("missing server code");
464 }
465 if( blob_eq_str(&aToken[1], zSCode, -1) ){
466 cgi_reset_content();
467 @ error server\sloop
468 nErr++;
469 break;
470 }
471 zPCode = db_get("project-code", 0);
472 if( zPCode==0 ){
473 fossil_panic("missing project code");
474 }
475 if( !blob_eq_str(&aToken[2], zPCode, -1) ){
476 cgi_reset_content();
477 @ error wrong\sproject
478 nErr++;
479 break;
480 }
481 login_check_credentials();
482 if( blob_eq(&aToken[0], "pull") ){
483 if( !g.okRead ){
484 cgi_reset_content();
485 @ error not\sauthorized\sto\sread
486 nErr++;
487 break;
@@ -492,91 +501,57 @@
492 cgi_reset_content();
493 @ error not\sauthorized\sto\swrite
494 nErr++;
495 break;
496 }
 
497 isPush = 1;
498
499 }
500 }else
501
502 /* clone
503 **
504 ** The client knows nothing. Tell all.
505 */
506 if( blob_eq(&aToken[0], "clone") ){
507 login_check_credentials();
508 if( !g.okRead || !g.okHistory ){
509 cgi_reset_content();
510 @ error not\sauthorized\sto\sclone
511 nErr++;
512 break;
513 }
514 isPull = 1;
515 @ push %s(db_get("server-code", "x")) %s(db_get("project-code", "x"))
516 db_multi_exec(
517 "INSERT OR IGNORE INTO pending(rid) "
518 "SELECT mid FROM mlink JOIN blob ON mid=rid"
519 );
520 }else
521
522 /* login USER NONCE SIGNATURE
523 **
524 ** Check for a valid login. This has to happen before anything else.
525 */
526 if( blob_eq(&aToken[0], "login") && nToken==4 ){
 
 
527 if( disableLogin ){
528 g.okRead = g.okWrite = 1;
529 }else{
530 check_login(&aToken[1], &aToken[2], &aToken[3]);
531 }
532 }else
533
534 /* Unknown message
535 */
536 {
537 cgi_reset_content();
538 @ error bad\scommand:\s%F(blob_str(&line))
539 }
540 blobarray_reset(aToken, nToken);
541 }
542
543 /* The input message has now been processed. Generate a reply. */
544 if( isPush ){
545 Stmt q;
546 int nReq = 0;
547 db_prepare(&q, "SELECT uuid, rid FROM phantom JOIN blob USING (rid)");
548 while( db_step(&q)==SQLITE_ROW && nReq++ < 200 ){
549 const char *zUuid = db_column_text(&q, 0);
550 int rid = db_column_int(&q, 1);
551 int xid = similar_record(rid, 0);
552 @ gimme %s(zUuid)
553 if( xid ){
554 char *zXUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", xid);
555 @ igot %s(zXUuid);
556 free(zXUuid);
557 }
558 }
559 db_finalize(&q);
560 }
561 if( isPull ){
562 send_all_pending(0);
563 }
564 if( isPush || isPull ){
565 /* Always send our leaves */
566 Stmt q;
567 db_prepare(&q,
568 "SELECT uuid FROM blob WHERE rid IN"
569 " (SELECT cid FROM plink EXCEPT SELECT pid FROM plink)"
570 );
571 while( db_step(&q)==SQLITE_ROW ){
572 const char *zUuid = db_column_text(&q, 0);
573 @ leaf %s(zUuid)
574 }
575 db_finalize(&q);
576 }
577
578 db_end_transaction(0);
579 }
580
581 /*
582 ** COMMAND: test-xfer
@@ -620,43 +595,40 @@
620 ** are pulled if pullFlag is true. A full sync occurs if both are
621 ** true.
622 */
623 void client_sync(int pushFlag, int pullFlag, int cloneFlag){
624 int go = 1; /* Loop until zero */
625 int nToken;
626 const char *zSCode = db_get("server-code", "x");
627 const char *zPCode = db_get("project-code", 0);
628 int nFile = 0;
629 int nMsg = 0;
630 int nReq = 0;
631 int nFileSend;
632 int nNoFileCycle = 0;
633 Blob send; /* Text we are sending to the server */
634 Blob recv; /* Reply we got back from the server */
635 Blob line; /* A single line of the reply */
636 Blob aToken[5]; /* A tokenization of line */
637 Blob errmsg; /* Error message */
 
 
638
639 assert( pushFlag || pullFlag || cloneFlag );
640 assert( !g.urlIsFile ); /* This only works for networking */
641
642 db_begin_transaction();
643 db_multi_exec(
644 /* Records which we know the other side also has */
645 "CREATE TEMP TABLE onremote(rid INTEGER PRIMARY KEY);"
646 /* Records we know the other side needs */
647 "CREATE TEMP TABLE pending(rid INTEGER PRIMARY KEY);"
648 );
649 blobarray_zero(aToken, count(aToken));
650 blob_zero(&send);
651 blob_zero(&recv);
652 blob_zero(&errmsg);
653
654
655 while( go ){
656 go = 0;
657 nFile = nReq = nMsg = 0;
658
659 /* Generate a request to be sent to the server.
660 ** Always begin with a clone, pull, or push message
661 */
662
@@ -665,143 +637,102 @@
665 pushFlag = 0;
666 pullFlag = 0;
667 nMsg++;
668 }else if( pullFlag ){
669 blob_appendf(&send, "pull %s %s\n", zSCode, zPCode);
 
 
670 nMsg++;
671 }
672 if( pushFlag ){
673 blob_appendf(&send, "push %s %s\n", zSCode, zPCode);
674 nMsg++;
675 }
676
677 if( pullFlag ){
678 /* Send gimme message for every phantom that we hold.
679 */
680 Stmt q;
681 db_prepare(&q, "SELECT uuid, rid FROM phantom JOIN blob USING (rid)");
682 while( db_step(&q)==SQLITE_ROW && nReq<200 ){
683 const char *zUuid = db_column_text(&q, 0);
684 int rid = db_column_int(&q, 1);
685 int xid = similar_record(rid, 0);
686 blob_appendf(&send,"gimme %s\n", zUuid);
687 nReq++;
688 if( xid ){
689 blob_appendf(&send, "igot %z\n",
690 db_text(0, "SELECT uuid FROM blob WHERE rid=%d", xid));
691 }
692 }
693 db_finalize(&q);
694 }
695
696 if( pushFlag ){
697 /* Send the server any files that the server has requested */
698 nFile += send_all_pending(&send);
699 }
700
701 if( pullFlag || pushFlag ){
702 /* Always send our leaves */
703 Stmt q;
704 db_prepare(&q,
705 "SELECT uuid FROM blob WHERE rid IN"
706 " (SELECT cid FROM plink EXCEPT SELECT pid FROM plink)"
707 );
708 while( db_step(&q)==SQLITE_ROW ){
709 const char *zUuid = db_column_text(&q, 0);
710 blob_appendf(&send, "leaf %s\n", zUuid);
711 nMsg++;
712 }
713 db_finalize(&q);
714 }
715
716 /* Exchange messages with the server */
 
717 printf("Send: %3d files, %3d requests, %3d other msgs, %8d bytes\n",
718 nFile, nReq, nMsg, blob_size(&send));
719 nFileSend = nFile;
720 nFile = nReq = nMsg = 0;
 
 
721 http_exchange(&send, &recv);
722 blob_reset(&send);
723
724 /* Process the reply that came back from the server */
725 while( blob_line(&recv, &line) ){
726 nToken = blob_tokenize(&line, aToken, count(aToken));
727
728 /* file UUID SIZE \n CONTENT
729 ** file UUID DELTASRC SIZE \n CONTENT
730 **
731 ** Receive a file transmitted from the other side
732 */
733 if( blob_eq(&aToken[0],"file") ){
734 xfer_accept_file(&recv, aToken, nToken, &errmsg);
735 nFile++;
736 go = 1;
737 }else
738
739 /* gimme UUID
740 **
741 ** Server is requesting a file
742 */
743 if( blob_eq(&aToken[0], "gimme") && nToken==2
744 && blob_is_uuid(&aToken[1]) ){
745 nReq++;
 
746 if( pushFlag ){
747 db_multi_exec(
748 "INSERT OR IGNORE INTO pending(rid) "
749 "SELECT rid FROM blob WHERE uuid=%B AND size>=0", &aToken[1]
750 );
751 go = 1;
752 }
753 }else
754
755 /* igot UUID
756 ** leaf UUID
 
 
 
 
 
 
 
 
 
 
 
 
 
757 **
758 ** Server proclaims that it has a particular file. A leaf message
759 ** means that the file is a leaf manifest on the server.
760 */
761 if( nToken==2
762 && (blob_eq(&aToken[0], "igot") || blob_eq(&aToken[0], "leaf"))
763 && blob_is_uuid(&aToken[1]) ){
764 int rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%B", &aToken[1]);
765 nMsg++;
766 if( rid>0 ){
767 db_multi_exec(
768 "INSERT OR IGNORE INTO onremote(rid) VALUES(%d)", rid
769 );
770 /* Add to the pending set all children of the server's leaves */
771 if( pushFlag && blob_eq(&aToken[0], "leaf") ){
772 db_multi_exec(
773 "INSERT OR IGNORE INTO pending(rid) "
774 "SELECT cid FROM plink WHERE pid=%d", rid
775 );
776 if( db_changes()>0 ){
777 go = 1;
778 }
779 }
780 if( pullFlag && !go &&
781 db_exists("SELECT 1 FROM phantom WHERE rid=%d", rid) ){
782 go = 1;
783 }
784 }else if( pullFlag ){
785 go = 1;
786 content_put(0, blob_str(&aToken[1]));
787 }
788 }else
 
789
790 /* push SERVERCODE PRODUCTCODE
791 **
792 ** Should only happen in response to a clone.
793 */
794 if( blob_eq(&aToken[0],"push") && nToken==3 && cloneFlag
795 && blob_is_uuid(&aToken[1]) && blob_is_uuid(&aToken[2]) ){
796
797 if( blob_eq_str(&aToken[1], zSCode, -1) ){
 
 
 
798 fossil_fatal("server loop");
799 }
800 nMsg++;
801 if( zPCode==0 ){
802 zPCode = mprintf("%b", &aToken[2]);
803 db_set("project-code", zPCode);
804 }
805 cloneFlag = 0;
806 pullFlag = 1;
807 }else
@@ -808,41 +739,41 @@
808
809 /* error MESSAGE
810 **
811 ** Report an error
812 */
813 if( blob_eq(&aToken[0],"error") && nToken==2 ){
814 char *zMsg = blob_terminate(&aToken[1]);
815 defossilize(zMsg);
816 blob_appendf(&errmsg, "server says: %s", zMsg);
817 }else
818
819 /* Unknown message */
820 {
821 blob_appendf(&errmsg, "unknown command: %b", &aToken[0]);
822 }
823
824 if( blob_size(&errmsg) ){
825 fossil_fatal("%b", &errmsg);
826 }
827 blobarray_reset(aToken, nToken);
828 }
829 printf("Received: %3d files, %3d requests, %3d other msgs, %8d bytes\n",
830 nFile, nReq, nMsg, blob_size(&recv));
 
831 blob_reset(&recv);
832 if( nFileSend + nFile==0 ){
833 nNoFileCycle++;
834 if( nNoFileCycle>1 ){
835 go = 0;
836 }
837 }else{
838 nNoFileCycle = 0;
839 }
840 nFile = nReq = nMsg = 0;
 
 
 
841 };
842 http_close();
843 db_end_transaction(0);
844 db_multi_exec(
845 "DROP TABLE onremote;"
846 "DROP TABLE pending;"
847 );
848 }
849
--- src/xfer.c
+++ src/xfer.c
@@ -25,109 +25,43 @@
25 */
26 #include "config.h"
27 #include "xfer.h"
28
29 /*
30 ** This structure holds information about the current state of either
31 ** a client or a server that is participating in xfer.
 
 
 
 
32 */
33 typedef struct Xfer Xfer;
34 struct Xfer {
35 Blob *pIn; /* Input text from the other side */
36 Blob *pOut; /* Compose our reply here */
37 Blob line; /* The current line of input */
38 Blob aToken[5]; /* Tokenized version of line */
39 Blob err; /* Error message text */
40 int nToken; /* Number of tokens in line */
41 int nIGot; /* Number of "igot" messages sent */
42 int nFile; /* Number of files sent or received */
43 int nDelta; /* Number of deltas sent or received */
44 int nDanglingFile; /* Number of dangling deltas received */
45 int mxSend; /* Stop sending "file" with pOut reaches this size */
46 };
47
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48
49 /*
50 ** The input blob contains a UUID. Convert it into a record ID.
51 ** Create a phantom record if no prior record exists and
52 ** phantomize is true.
53 **
54 ** Compare to uuid_to_rid(). This routine takes a blob argument
55 ** and does less error checking.
56 */
57 static int rid_from_uuid(Blob *pUuid, int phantomize){
58 int rid = db_int(0, "SELECT rid FROM blob WHERE uuid='%b'", pUuid);
59 if( rid==0 && phantomize ){
60 rid = content_put(0, blob_str(pUuid), 0);
61 }
62 return rid;
 
 
 
 
 
 
 
 
 
63 }
64
65
66 /*
67 ** The aToken[0..nToken-1] blob array is a parse of a "file" line
@@ -144,162 +78,227 @@
78 ** content of DELTASRC.
79 **
80 ** If any error occurs, write a message into pErr which has already
81 ** be initialized to an empty string.
82 */
83 static void xfer_accept_file(Xfer *pXfer){
84 int n;
85 int rid;
86 Blob content, hash;
87
88 if( pXfer->nToken<3
89 || pXfer->nToken>4
90 || !blob_is_uuid(&pXfer->aToken[1])
91 || !blob_is_int(&pXfer->aToken[pXfer->nToken-1], &n)
92 || n<=0
93 || (pXfer->nToken==4 && !blob_is_uuid(&pXfer->aToken[2]))
94 ){
95 blob_appendf(&pXfer->err, "malformed file line");
96 return;
97 }
98 blob_zero(&content);
99 blob_zero(&hash);
100 blob_extract(pXfer->pIn, n, &content);
101 if( pXfer->nToken==4 ){
102 Blob src;
103 int srcid = rid_from_uuid(&pXfer->aToken[2], 1);
104 if( content_get(srcid, &src)==0 ){
105 content_put(&content, blob_str(&hash), srcid);
106 blob_appendf(pXfer->pOut, "gimme %b\n", &pXfer->aToken[2]);
107 pXfer->nDanglingFile++;
108 return;
109 }
110 pXfer->nDelta++;
111 blob_delta_apply(&src, &content, &content);
112 blob_reset(&src);
113 }else{
114 pXfer->nFile++;
115 }
116 sha1sum_blob(&content, &hash);
117 if( !blob_eq_str(&pXfer->aToken[1], blob_str(&hash), -1) ){
118 blob_appendf(&pXfer->err, "content does not match sha1 hash");
119 }
120 blob_reset(&hash);
121 rid = content_put(&content, 0, 0);
 
122 if( rid==0 ){
123 blob_appendf(&pXfer->err, "%s", g.zErrMsg);
124 }else{
125 manifest_crosslink(rid, &content);
126 }
127 }
128
129 /*
130 ** Try to send a file as a delta. If successful, return the number
131 ** of bytes in the delta. If not, return zero.
132 **
133 ** If srcId is specified, use it. If not, try to figure out a
134 ** reasonable srcId.
135 */
136 static int send_as_delta(
137 Xfer *pXfer, /* The transfer context */
138 int rid, /* record id of the file to send */
139 Blob *pContent, /* The content of the file to send */
140 Blob *pUuid, /* The UUID of the file to send */
141 int srcId /* Send as a delta against this record */
142 ){
143 static const char *azQuery[] = {
144 "SELECT srcid FROM delta JOIN pending ON pending.rid=delta.srcid"
145 " WHERE delta.rid=%d"
146 " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=srcid)",
147
148 "SELECT delta.rid FROM delta JOIN pending ON pending.rid=delta.rid"
149 " WHERE srcid=%d"
150 " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=delta.rid)",
151
152 "SELECT pid FROM plink JOIN pending ON rid=pid"
153 " WHERE cid=%d"
154 " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=pid)",
155
156 "SELECT cid FROM plink JOIN pending ON rid=cid"
157 " WHERE pid=%d"
158 " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=cid)",
159
160 "SELECT pid FROM mlink JOIN pending ON rid=pid"
161 " WHERE fid=%d"
162 " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=pid)",
163
164 "SELECT fid FROM mlink JOIN pending ON rid=fid"
165 " WHERE pid=%d"
166 " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=fid)",
167 };
168 int i;
169 Blob src, delta;
170 int size = 0;
171
172 for(i=0; srcId==0 && i<count(azQuery); i++){
173 srcId = db_int(0, azQuery[i], rid);
174 }
175 if( srcId && content_get(srcId, &src) ){
176 char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", srcId);
177 blob_delta_create(&src, pContent, &delta);
178 size = blob_size(&delta);
179 if( size>=blob_size(pContent)-50 ){
180 size = 0;
181 }else{
182 blob_appendf(pXfer->pOut, "file %b %s %d\n", pUuid, zUuid, size);
183 blob_append(pXfer->pOut, blob_buffer(&delta), size);
184 blob_appendf(pXfer->pOut, "\n", 1);
185 }
186 blob_reset(&delta);
187 free(zUuid);
188 blob_reset(&src);
189 }
190 return size;
191 }
192
193 /*
194 ** Send the file identified by rid.
 
 
 
195 */
196 static void send_file(Xfer *pXfer, int rid, Blob *pUuid, int srcId){
197 Blob content, uuid;
198 int size = 0;
 
199
200 if( db_exists("SELECT 1 FROM sent WHERE rid=%d", rid) ){
201 return;
202 }
203 blob_zero(&uuid);
204 if( pUuid==0 ){
205 db_blob(&uuid, "SELECT uuid FROM blob WHERE rid=%d AND size>=0", rid);
206 if( blob_size(&uuid)==0 ){
207 return;
208 }
209 pUuid = &uuid;
210 }
211 if( pXfer->mxSend<=blob_size(pXfer->pOut) ){
212 blob_appendf(pXfer->pOut, "igot %b\n", pUuid);
213 pXfer->nIGot++;
214 blob_reset(&uuid);
215 return;
216 }
217 content_get(rid, &content);
218
219 if( blob_size(&content)>100 ){
220 size = send_as_delta(pXfer, rid, &content, pUuid, srcId);
221 }
222 if( size==0 ){
223 int size = blob_size(&content);
224 blob_appendf(pXfer->pOut, "file %b %d\n", pUuid, size);
225 blob_append(pXfer->pOut, blob_buffer(&content), size);
226 pXfer->nFile++;
227 }else{
228 pXfer->nDelta++;
229 }
230 db_multi_exec("INSERT INTO sent VALUES(%d)", rid);
231 blob_reset(&uuid);
232 }
233
234 /*
235 ** This routine runs when either client or server is notified that
236 ** the other side things rid is a leaf manifest. If we hold
237 ** children of rid, then send them over to the other side.
238 */
239 static void leaf_response(Xfer *pXfer, int rid){
240 Stmt q1, q2;
241 db_prepare(&q1,
242 "SELECT cid, uuid FROM plink, blob"
243 " WHERE blob.rid=plink.cid"
244 " AND plink.pid=%d",
245 rid
246 );
247 while( db_step(&q1)==SQLITE_ROW ){
248 Blob uuid;
249 int cid;
250
251 cid = db_column_int(&q1, 0);
252 db_ephemeral_blob(&q1, 1, &uuid);
253 send_file(pXfer, cid, &uuid, rid);
254 db_prepare(&q2,
255 "SELECT pid, uuid, fid FROM mlink, blob"
256 " WHERE rid=fid AND mid=%d",
257 cid
258 );
259 while( db_step(&q2)==SQLITE_ROW ){
260 int pid, fid;
261 pid = db_column_int(&q2, 0);
262 db_ephemeral_blob(&q2, 1, &uuid);
263 fid = db_column_int(&q2, 2);
264 send_file(pXfer, fid, &uuid, pid);
265 }
266 db_finalize(&q2);
267 if( blob_size(pXfer->pOut)<pXfer->mxSend ){
268 leaf_response(pXfer, cid);
269 }
270 }
271 }
272
273 /*
274 ** Sent a leaf message for every leaf.
275 */
276 static void send_leaves(Xfer *pXfer){
277 Stmt q;
278 db_prepare(&q,
279 "SELECT uuid FROM blob WHERE rid IN"
280 " (SELECT cid FROM plink EXCEPT SELECT pid FROM plink)"
281 );
282 while( db_step(&q)==SQLITE_ROW ){
283 const char *zUuid = db_column_text(&q, 0);
284 blob_appendf(pXfer->pOut, "leaf %s\n", zUuid);
285 }
286 db_finalize(&q);
287 }
288
289 /*
290 ** Sen a gimme message for every phantom.
291 */
292 static void request_phantoms(Xfer *pXfer){
293 Stmt q;
294 db_prepare(&q, "SELECT uuid FROM phantom JOIN blob USING(rid)");
295 while( db_step(&q)==SQLITE_ROW ){
296 const char *zUuid = db_column_text(&q, 0);
297 blob_appendf(pXfer->pOut, "gimme %s\n", zUuid);
298 }
299 db_finalize(&q);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
300 }
301
302
303 /*
304 ** Check the signature on an application/x-fossil payload received by
@@ -372,116 +371,126 @@
371 void page_xfer(void){
372 int nToken;
373 int isPull = 0;
374 int isPush = 0;
375 int nErr = 0;
376 Xfer xfer;
377
378 memset(&xfer, 0, sizeof(xfer));
379 blobarray_zero(xfer.aToken, count(xfer.aToken));
380 cgi_set_content_type(g.zContentType);
381 blob_zero(&xfer.err);
382 xfer.pIn = &g.cgiIn;
383 xfer.pOut = cgi_output_blob();
384
385 db_begin_transaction();
 
 
 
386 db_multi_exec(
387 "CREATE TEMP TABLE sent(rid INTEGER PRIMARY KEY);"
 
388 );
389 while( blob_line(xfer.pIn, &xfer.line) ){
390 xfer.nToken = blob_tokenize(&xfer.line, xfer.aToken, count(xfer.aToken));
391
392 /* file UUID SIZE \n CONTENT
393 ** file UUID DELTASRC SIZE \n CONTENT
394 **
395 ** Accept a file from the client.
396 */
397 if( blob_eq(&xfer.aToken[0], "file") ){
398 if( !isPush ){
399 cgi_reset_content();
400 @ error not\sauthorized\sto\swrite
401 nErr++;
402 break;
403 }
404 xfer_accept_file(&xfer);
405 if( blob_size(&xfer.err) ){
406 cgi_reset_content();
407 @ error %T(blob_str(&xfer.err))
408 nErr++;
409 break;
410 }
411 }else
412
413 /* gimme UUID
414 **
415 ** Client is requesting a file
416 */
417 if( blob_eq(&xfer.aToken[0], "gimme")
418 && xfer.nToken==2
419 && blob_is_uuid(&xfer.aToken[1])
420 ){
421 if( isPull ){
422 int rid = rid_from_uuid(&xfer.aToken[1], 0);
423 if( rid ){
424 send_file(&xfer, rid, &xfer.aToken[1], 0);
425 }
426 }
427 }else
428
429 /* igot UUID
 
430 **
431 ** Client announces that it has a particular file
432 */
433 if( xfer.nToken==2
434 && blob_eq(&xfer.aToken[0], "igot")
435 && blob_is_uuid(&xfer.aToken[1])
436 ){
437 if( isPush ){
438 rid_from_uuid(&xfer.aToken[1], 1);
439 }
440 }else
441
442
443 /* leaf UUID
444 **
445 ** Client announces that it has a particular manifest
446 */
447 if( xfer.nToken==2
448 && blob_eq(&xfer.aToken[0], "leaf")
449 && blob_is_uuid(&xfer.aToken[1])
450 ){
451 if( isPull ){
452 int rid = rid_from_uuid(&xfer.aToken[1], 0);
453 leaf_response(&xfer, rid);
454 }
455 }else
456
457 /* pull SERVERCODE PROJECTCODE
458 ** push SERVERCODE PROJECTCODE
459 **
460 ** The client wants either send or receive
461 */
462 if( nToken==3
463 && (blob_eq(&xfer.aToken[0], "pull") || blob_eq(&xfer.aToken[0], "push"))
464 && blob_is_uuid(&xfer.aToken[1])
465 && blob_is_uuid(&xfer.aToken[2])
466 ){
467 const char *zSCode;
468 const char *zPCode;
469
470 zSCode = db_get("server-code", 0);
471 if( zSCode==0 ){
472 fossil_panic("missing server code");
473 }
474 if( blob_eq_str(&xfer.aToken[1], zSCode, -1) ){
475 cgi_reset_content();
476 @ error server\sloop
477 nErr++;
478 break;
479 }
480 zPCode = db_get("project-code", 0);
481 if( zPCode==0 ){
482 fossil_panic("missing project code");
483 }
484 if( !blob_eq_str(&xfer.aToken[2], zPCode, -1) ){
485 cgi_reset_content();
486 @ error wrong\sproject
487 nErr++;
488 break;
489 }
490 login_check_credentials();
491 if( blob_eq(&xfer.aToken[0], "pull") ){
492 if( !g.okRead ){
493 cgi_reset_content();
494 @ error not\sauthorized\sto\sread
495 nErr++;
496 break;
@@ -492,91 +501,57 @@
501 cgi_reset_content();
502 @ error not\sauthorized\sto\swrite
503 nErr++;
504 break;
505 }
506 send_leaves(&xfer);
507 isPush = 1;
 
508 }
509 }else
510
511 /* clone
512 **
513 ** The client knows nothing. Tell all.
514 */
515 if( blob_eq(&xfer.aToken[0], "clone") ){
516 login_check_credentials();
517 if( !g.okRead || !g.okHistory ){
518 cgi_reset_content();
519 @ error not\sauthorized\sto\sclone
520 nErr++;
521 break;
522 }
523 isPull = 1;
524 @ push %s(db_get("server-code", "x")) %s(db_get("project-code", "x"))
525 send_leaves(&xfer);
 
 
 
526 }else
527
528 /* login USER NONCE SIGNATURE
529 **
530 ** Check for a valid login. This has to happen before anything else.
531 */
532 if( blob_eq(&xfer.aToken[0], "login")
533 && nToken==4
534 ){
535 if( disableLogin ){
536 g.okRead = g.okWrite = 1;
537 }else{
538 check_login(&xfer.aToken[1], &xfer.aToken[2], &xfer.aToken[3]);
539 }
540 }else
541
542 /* Unknown message
543 */
544 {
545 cgi_reset_content();
546 @ error bad\scommand:\s%F(blob_str(&xfer.line))
547 }
548 blobarray_reset(xfer.aToken, xfer.nToken);
549 }
 
 
550 if( isPush ){
551 request_phantoms(&xfer);
552 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
553 db_end_transaction(0);
554 }
555
556 /*
557 ** COMMAND: test-xfer
@@ -620,43 +595,40 @@
595 ** are pulled if pullFlag is true. A full sync occurs if both are
596 ** true.
597 */
598 void client_sync(int pushFlag, int pullFlag, int cloneFlag){
599 int go = 1; /* Loop until zero */
 
600 const char *zSCode = db_get("server-code", "x");
601 const char *zPCode = db_get("project-code", 0);
 
602 int nMsg = 0;
603 int nReq = 0;
604 int nFileSend = 0;
605 int nNoFileCycle = 0;
606 Blob send; /* Text we are sending to the server */
607 Blob recv; /* Reply we got back from the server */
608 Xfer xfer; /* Transfer data */
609
610 memset(&xfer, 0, sizeof(xfer));
611 xfer.pIn = &recv;
612 xfer.pOut = &send;
613
614 assert( pushFlag || pullFlag || cloneFlag );
615 assert( !g.urlIsFile ); /* This only works for networking */
616
617 db_begin_transaction();
618 db_multi_exec(
619 "CREATE TEMP TABLE sent(rid INTEGER PRIMARY KEY);"
 
 
 
620 );
621 blobarray_zero(xfer.aToken, count(xfer.aToken));
622 blob_zero(&send);
623 blob_zero(&recv);
624 blob_zero(&xfer.err);
625
626
627 while( go ){
628 go = 0;
629 nReq = nMsg = 0;
630
631 /* Generate a request to be sent to the server.
632 ** Always begin with a clone, pull, or push message
633 */
634
@@ -665,143 +637,102 @@
637 pushFlag = 0;
638 pullFlag = 0;
639 nMsg++;
640 }else if( pullFlag ){
641 blob_appendf(&send, "pull %s %s\n", zSCode, zPCode);
642 send_leaves(&xfer);
643 request_phantoms(&xfer);
644 nMsg++;
645 }
646 if( pushFlag ){
647 blob_appendf(&send, "push %s %s\n", zSCode, zPCode);
648 nMsg++;
649 }
650
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
651 /* Exchange messages with the server */
652 nFileSend = xfer.nFile + xfer.nDelta + xfer.nDanglingFile;
653 printf("Send: %3d files, %3d requests, %3d other msgs, %8d bytes\n",
654 nFileSend, nReq, nMsg, blob_size(&send));
655 xfer.nFile = 0;
656 xfer.nDelta = 0;
657 xfer.nDanglingFile = 0;
658 nReq = nMsg = 0;
659 http_exchange(&send, &recv);
660 blob_reset(&send);
661
662 /* Process the reply that came back from the server */
663 while( blob_line(&recv, &xfer.line) ){
664 xfer.nToken = blob_tokenize(&xfer.line, xfer.aToken, count(xfer.aToken));
665
666 /* file UUID SIZE \n CONTENT
667 ** file UUID DELTASRC SIZE \n CONTENT
668 **
669 ** Receive a file transmitted from the other side
670 */
671 if( blob_eq(&xfer.aToken[0],"file") ){
672 xfer_accept_file(&xfer);
 
 
673 }else
674
675 /* gimme UUID
676 **
677 ** Server is requesting a file
678 */
679 if( blob_eq(&xfer.aToken[0], "gimme")
680 && xfer.nToken==2
681 && blob_is_uuid(&xfer.aToken[1])
682 ){
683 if( pushFlag ){
684 int rid = rid_from_uuid(&xfer.aToken[1], 0);
685 send_file(&xfer, rid, &xfer.aToken[1], 0);
 
 
 
686 }
687 }else
688
689 /* igot UUID
690 **
691 ** Server announces that it has a particular file
692 */
693 if( xfer.nToken==2
694 && blob_eq(&xfer.aToken[0], "igot")
695 && blob_is_uuid(&xfer.aToken[1])
696 ){
697 if( pullFlag ){
698 rid_from_uuid(&xfer.aToken[1], 1);
699 }
700 }else
701
702
703 /* leaf UUID
704 **
705 ** Server announces that it has a particular manifest
 
706 */
707 if( xfer.nToken==2
708 && blob_eq(&xfer.aToken[0], "leaf")
709 && blob_is_uuid(&xfer.aToken[1])
710 ){
711 if( pushFlag ){
712 int rid = rid_from_uuid(&xfer.aToken[1], 0);
713 leaf_response(&xfer, rid);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
714 }
715 }else
716
717
718 /* push SERVERCODE PRODUCTCODE
719 **
720 ** Should only happen in response to a clone.
721 */
722 if( blob_eq(&xfer.aToken[0],"push")
723 && xfer.nToken==3
724 && cloneFlag
725 && blob_is_uuid(&xfer.aToken[1])
726 && blob_is_uuid(&xfer.aToken[2])
727 ){
728 if( blob_eq_str(&xfer.aToken[1], zSCode, -1) ){
729 fossil_fatal("server loop");
730 }
731 nMsg++;
732 if( zPCode==0 ){
733 zPCode = mprintf("%b", &xfer.aToken[2]);
734 db_set("project-code", zPCode);
735 }
736 cloneFlag = 0;
737 pullFlag = 1;
738 }else
@@ -808,41 +739,41 @@
739
740 /* error MESSAGE
741 **
742 ** Report an error
743 */
744 if( blob_eq(&xfer.aToken[0],"error") && xfer.nToken==2 ){
745 char *zMsg = blob_terminate(&xfer.aToken[1]);
746 defossilize(zMsg);
747 blob_appendf(&xfer.err, "server says: %s", zMsg);
748 }else
749
750 /* Unknown message */
751 {
752 blob_appendf(&xfer.err, "unknown command: %b", &xfer.aToken[0]);
753 }
754
755 if( blob_size(&xfer.err) ){
756 fossil_fatal("%b", &xfer.err);
757 }
758 blobarray_reset(xfer.aToken, xfer.nToken);
759 }
760 printf("Received: %3d files, %3d requests, %3d other msgs, %8d bytes\n",
761 xfer.nFile + xfer.nDelta + xfer.nDanglingFile,
762 nReq, nMsg, blob_size(&recv));
763 blob_reset(&recv);
764 if( nFileSend + xfer.nFile + xfer.nDelta + xfer.nDanglingFile==0 ){
765 nNoFileCycle++;
766 if( nNoFileCycle>1 ){
767 go = 0;
768 }
769 }else{
770 nNoFileCycle = 0;
771 }
772 nReq = nMsg = 0;
773 xfer.nFile = 0;
774 xfer.nDelta = 0;
775 xfer.nDanglingFile = 0;
776 };
777 http_close();
778 db_end_transaction(0);
 
 
 
 
779 }
780

Keyboard Shortcuts

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