Fossil SCM

Merge the --private sync enhancement into the trunk.

drh 2011-02-27 21:08 trunk merge
Commit 8b8cc4f1b7626f1eade85ac9d4c2b287cee1dec6
+5 -2
--- src/clone.c
+++ src/clone.c
@@ -35,18 +35,21 @@
3535
** admin user. This can be overridden using the -A|--admin-user
3636
** parameter.
3737
**
3838
** Options:
3939
**
40
-** --admin-user|-A USERNAME
40
+** --admin-user|-A USERNAME Make USERNAME the administrator
41
+** --private Also clone private branches
4142
**
4243
*/
4344
void clone_cmd(void){
4445
char *zPassword;
4546
const char *zDefaultUser; /* Optional name of the default user */
4647
int nErr = 0;
48
+ int bPrivate; /* Also clone private branches */
4749
50
+ bPrivate = find_option("private",0,0)!=0;
4851
url_proxy_options();
4952
if( g.argc < 4 ){
5053
usage("?OPTIONS? FILE-OR-URL NEW-REPOSITORY");
5154
}
5255
db_open_config(0);
@@ -94,11 +97,11 @@
9497
"REPLACE INTO config(name,value)"
9598
" VALUES('server-code', lower(hex(randomblob(20))));"
9699
);
97100
url_enable_proxy(0);
98101
g.xlinkClusterOnly = 1;
99
- nErr = client_sync(0,0,1,CONFIGSET_ALL,0);
102
+ nErr = client_sync(0,0,1,bPrivate,CONFIGSET_ALL,0);
100103
g.xlinkClusterOnly = 0;
101104
verify_cancel();
102105
db_end_transaction(0);
103106
db_close(1);
104107
if( nErr ){
105108
--- src/clone.c
+++ src/clone.c
@@ -35,18 +35,21 @@
35 ** admin user. This can be overridden using the -A|--admin-user
36 ** parameter.
37 **
38 ** Options:
39 **
40 ** --admin-user|-A USERNAME
 
41 **
42 */
43 void clone_cmd(void){
44 char *zPassword;
45 const char *zDefaultUser; /* Optional name of the default user */
46 int nErr = 0;
 
47
 
48 url_proxy_options();
49 if( g.argc < 4 ){
50 usage("?OPTIONS? FILE-OR-URL NEW-REPOSITORY");
51 }
52 db_open_config(0);
@@ -94,11 +97,11 @@
94 "REPLACE INTO config(name,value)"
95 " VALUES('server-code', lower(hex(randomblob(20))));"
96 );
97 url_enable_proxy(0);
98 g.xlinkClusterOnly = 1;
99 nErr = client_sync(0,0,1,CONFIGSET_ALL,0);
100 g.xlinkClusterOnly = 0;
101 verify_cancel();
102 db_end_transaction(0);
103 db_close(1);
104 if( nErr ){
105
--- src/clone.c
+++ src/clone.c
@@ -35,18 +35,21 @@
35 ** admin user. This can be overridden using the -A|--admin-user
36 ** parameter.
37 **
38 ** Options:
39 **
40 ** --admin-user|-A USERNAME Make USERNAME the administrator
41 ** --private Also clone private branches
42 **
43 */
44 void clone_cmd(void){
45 char *zPassword;
46 const char *zDefaultUser; /* Optional name of the default user */
47 int nErr = 0;
48 int bPrivate; /* Also clone private branches */
49
50 bPrivate = find_option("private",0,0)!=0;
51 url_proxy_options();
52 if( g.argc < 4 ){
53 usage("?OPTIONS? FILE-OR-URL NEW-REPOSITORY");
54 }
55 db_open_config(0);
@@ -94,11 +97,11 @@
97 "REPLACE INTO config(name,value)"
98 " VALUES('server-code', lower(hex(randomblob(20))));"
99 );
100 url_enable_proxy(0);
101 g.xlinkClusterOnly = 1;
102 nErr = client_sync(0,0,1,bPrivate,CONFIGSET_ALL,0);
103 g.xlinkClusterOnly = 0;
104 verify_cancel();
105 db_end_transaction(0);
106 db_close(1);
107 if( nErr ){
108
+2 -2
--- src/configure.c
+++ src/configure.c
@@ -464,13 +464,13 @@
464464
url_parse(zServer);
465465
if( g.urlPasswd==0 && zPw ) g.urlPasswd = mprintf("%s", zPw);
466466
user_select();
467467
url_enable_proxy("via proxy: ");
468468
if( strncmp(zMethod, "push", n)==0 ){
469
- client_sync(0,0,0,0,mask);
469
+ client_sync(0,0,0,0,0,mask);
470470
}else{
471
- client_sync(0,0,0,mask,0);
471
+ client_sync(0,0,0,0,mask,0);
472472
}
473473
}else
474474
if( strncmp(zMethod, "reset", n)==0 ){
475475
int mask, i;
476476
char *zBackup;
477477
--- src/configure.c
+++ src/configure.c
@@ -464,13 +464,13 @@
464 url_parse(zServer);
465 if( g.urlPasswd==0 && zPw ) g.urlPasswd = mprintf("%s", zPw);
466 user_select();
467 url_enable_proxy("via proxy: ");
468 if( strncmp(zMethod, "push", n)==0 ){
469 client_sync(0,0,0,0,mask);
470 }else{
471 client_sync(0,0,0,mask,0);
472 }
473 }else
474 if( strncmp(zMethod, "reset", n)==0 ){
475 int mask, i;
476 char *zBackup;
477
--- src/configure.c
+++ src/configure.c
@@ -464,13 +464,13 @@
464 url_parse(zServer);
465 if( g.urlPasswd==0 && zPw ) g.urlPasswd = mprintf("%s", zPw);
466 user_select();
467 url_enable_proxy("via proxy: ");
468 if( strncmp(zMethod, "push", n)==0 ){
469 client_sync(0,0,0,0,0,mask);
470 }else{
471 client_sync(0,0,0,0,mask,0);
472 }
473 }else
474 if( strncmp(zMethod, "reset", n)==0 ){
475 int mask, i;
476 char *zBackup;
477
--- src/http_transport.c
+++ src/http_transport.c
@@ -302,11 +302,11 @@
302302
if( g.urlIsSsh ){
303303
fprintf(sshOut, "\n\n");
304304
}else if( g.urlIsFile ){
305305
char *zCmd;
306306
fclose(transport.pFile);
307
- zCmd = mprintf("\"%s\" http \"%s\" \"%s\" \"%s\" 127.0.0.1",
307
+ zCmd = mprintf("\"%s\" http \"%s\" \"%s\" \"%s\" 127.0.0.1 --localauth",
308308
fossil_nameofexe(), g.urlName, transport.zOutFile, transport.zInFile
309309
);
310310
fossil_system(zCmd);
311311
free(zCmd);
312312
transport.pFile = fopen(transport.zInFile, "rb");
313313
--- src/http_transport.c
+++ src/http_transport.c
@@ -302,11 +302,11 @@
302 if( g.urlIsSsh ){
303 fprintf(sshOut, "\n\n");
304 }else if( g.urlIsFile ){
305 char *zCmd;
306 fclose(transport.pFile);
307 zCmd = mprintf("\"%s\" http \"%s\" \"%s\" \"%s\" 127.0.0.1",
308 fossil_nameofexe(), g.urlName, transport.zOutFile, transport.zInFile
309 );
310 fossil_system(zCmd);
311 free(zCmd);
312 transport.pFile = fopen(transport.zInFile, "rb");
313
--- src/http_transport.c
+++ src/http_transport.c
@@ -302,11 +302,11 @@
302 if( g.urlIsSsh ){
303 fprintf(sshOut, "\n\n");
304 }else if( g.urlIsFile ){
305 char *zCmd;
306 fclose(transport.pFile);
307 zCmd = mprintf("\"%s\" http \"%s\" \"%s\" \"%s\" 127.0.0.1 --localauth",
308 fossil_nameofexe(), g.urlName, transport.zOutFile, transport.zInFile
309 );
310 fossil_system(zCmd);
311 free(zCmd);
312 transport.pFile = fopen(transport.zInFile, "rb");
313
+2 -1
--- src/login.c
+++ src/login.c
@@ -374,11 +374,11 @@
374374
&& db_get_int("localauth",0)==0
375375
&& P("HTTPS")==0
376376
){
377377
uid = db_int(0, "SELECT uid FROM user WHERE cap LIKE '%%s%%'");
378378
g.zLogin = db_text("?", "SELECT login FROM user WHERE uid=%d", uid);
379
- zCap = "s";
379
+ zCap = "sx";
380380
g.noPswd = 1;
381381
sqlite3_snprintf(sizeof(g.zCsrfToken), g.zCsrfToken, "localhost");
382382
}
383383
384384
/* Check the login cookie to see if it matches a known valid user.
@@ -535,10 +535,11 @@
535535
case 'w': g.okWrTkt = g.okRdTkt = g.okNewTkt =
536536
g.okApndTkt = 1; break;
537537
case 'c': g.okApndTkt = 1; break;
538538
case 't': g.okTktFmt = 1; break;
539539
case 'b': g.okAttach = 1; break;
540
+ case 'x': g.okPrivate = 1; break;
540541
541542
/* The "u" privileges is a little different. It recursively
542543
** inherits all privileges of the user named "reader" */
543544
case 'u': {
544545
if( zUser==0 ){
545546
--- src/login.c
+++ src/login.c
@@ -374,11 +374,11 @@
374 && db_get_int("localauth",0)==0
375 && P("HTTPS")==0
376 ){
377 uid = db_int(0, "SELECT uid FROM user WHERE cap LIKE '%%s%%'");
378 g.zLogin = db_text("?", "SELECT login FROM user WHERE uid=%d", uid);
379 zCap = "s";
380 g.noPswd = 1;
381 sqlite3_snprintf(sizeof(g.zCsrfToken), g.zCsrfToken, "localhost");
382 }
383
384 /* Check the login cookie to see if it matches a known valid user.
@@ -535,10 +535,11 @@
535 case 'w': g.okWrTkt = g.okRdTkt = g.okNewTkt =
536 g.okApndTkt = 1; break;
537 case 'c': g.okApndTkt = 1; break;
538 case 't': g.okTktFmt = 1; break;
539 case 'b': g.okAttach = 1; break;
 
540
541 /* The "u" privileges is a little different. It recursively
542 ** inherits all privileges of the user named "reader" */
543 case 'u': {
544 if( zUser==0 ){
545
--- src/login.c
+++ src/login.c
@@ -374,11 +374,11 @@
374 && db_get_int("localauth",0)==0
375 && P("HTTPS")==0
376 ){
377 uid = db_int(0, "SELECT uid FROM user WHERE cap LIKE '%%s%%'");
378 g.zLogin = db_text("?", "SELECT login FROM user WHERE uid=%d", uid);
379 zCap = "sx";
380 g.noPswd = 1;
381 sqlite3_snprintf(sizeof(g.zCsrfToken), g.zCsrfToken, "localhost");
382 }
383
384 /* Check the login cookie to see if it matches a known valid user.
@@ -535,10 +535,11 @@
535 case 'w': g.okWrTkt = g.okRdTkt = g.okNewTkt =
536 g.okApndTkt = 1; break;
537 case 'c': g.okApndTkt = 1; break;
538 case 't': g.okTktFmt = 1; break;
539 case 'b': g.okAttach = 1; break;
540 case 'x': g.okPrivate = 1; break;
541
542 /* The "u" privileges is a little different. It recursively
543 ** inherits all privileges of the user named "reader" */
544 case 'u': {
545 if( zUser==0 ){
546
+1
--- src/main.c
+++ src/main.c
@@ -134,10 +134,11 @@
134134
int okWrTkt; /* w: make changes to tickets via web */
135135
int okAttach; /* b: add attachments */
136136
int okTktFmt; /* t: create new ticket report formats */
137137
int okRdAddr; /* e: read email addresses or other private data */
138138
int okZip; /* z: download zipped artifact via /zip URL */
139
+ int okPrivate; /* x: can send and receive private content */
139140
140141
/* For defense against Cross-site Request Forgery attacks */
141142
char zCsrfToken[12]; /* Value of the anti-CSRF token */
142143
int okCsrf; /* Anti-CSRF token is present and valid */
143144
144145
--- src/main.c
+++ src/main.c
@@ -134,10 +134,11 @@
134 int okWrTkt; /* w: make changes to tickets via web */
135 int okAttach; /* b: add attachments */
136 int okTktFmt; /* t: create new ticket report formats */
137 int okRdAddr; /* e: read email addresses or other private data */
138 int okZip; /* z: download zipped artifact via /zip URL */
 
139
140 /* For defense against Cross-site Request Forgery attacks */
141 char zCsrfToken[12]; /* Value of the anti-CSRF token */
142 int okCsrf; /* Anti-CSRF token is present and valid */
143
144
--- src/main.c
+++ src/main.c
@@ -134,10 +134,11 @@
134 int okWrTkt; /* w: make changes to tickets via web */
135 int okAttach; /* b: add attachments */
136 int okTktFmt; /* t: create new ticket report formats */
137 int okRdAddr; /* e: read email addresses or other private data */
138 int okZip; /* z: download zipped artifact via /zip URL */
139 int okPrivate; /* x: can send and receive private content */
140
141 /* For defense against Cross-site Request Forgery attacks */
142 char zCsrfToken[12]; /* Value of the anti-CSRF token */
143 int okCsrf; /* Anti-CSRF token is present and valid */
144
145
+23 -14
--- src/rebuild.c
+++ src/rebuild.c
@@ -540,19 +540,20 @@
540540
}
541541
}
542542
543543
/*
544544
** COMMAND: scrub
545
-** %fossil scrub [--verily] [--force] [REPOSITORY]
545
+** %fossil scrub [--verily] [--force] [--private] [REPOSITORY]
546546
**
547547
** The command removes sensitive information (such as passwords) from a
548548
** repository so that the respository can be sent to an untrusted reader.
549549
**
550550
** By default, only passwords are removed. However, if the --verily option
551551
** is added, then private branches, concealed email addresses, IP
552552
** addresses of correspondents, and similar privacy-sensitive fields
553
-** are also purged.
553
+** are also purged. If the --private option is used, then only private
554
+** branches are removed and all other information is left intact.
554555
**
555556
** This command permanently deletes the scrubbed information. The effects
556557
** of this command are irreversible. Use with caution.
557558
**
558559
** The user is prompted to confirm the scrub unless the --force option
@@ -559,10 +560,11 @@
559560
** is used.
560561
*/
561562
void scrub_cmd(void){
562563
int bVerily = find_option("verily",0,0)!=0;
563564
int bForce = find_option("force", "f", 0)!=0;
565
+ int privateOnly = find_option("private",0,0)!=0;
564566
int bNeedRebuild = 0;
565567
if( g.argc!=2 && g.argc!=3 ) usage("?REPOSITORY?");
566568
if( g.argc==2 ){
567569
db_must_be_within_tree();
568570
}else{
@@ -569,30 +571,37 @@
569571
db_open_repository(g.argv[2]);
570572
}
571573
if( !bForce ){
572574
Blob ans;
573575
blob_zero(&ans);
574
- prompt_user("Scrubbing the repository will permanently remove user\n"
575
- "passwords and other information. Changes cannot be undone.\n"
576
- "Continue (y/N)? ", &ans);
576
+ prompt_user("Scrubbing the repository will permanently information.\n"
577
+ "Changes cannot be undone. Continue (y/N)? ", &ans);
577578
if( blob_str(&ans)[0]!='y' ){
578579
fossil_exit(1);
579580
}
580581
}
581582
db_begin_transaction();
582
- db_multi_exec(
583
- "UPDATE user SET pw='';"
584
- "DELETE FROM config WHERE name GLOB 'last-sync-*';"
585
- );
586
- if( bVerily ){
583
+ if( privateOnly || bVerily ){
587584
bNeedRebuild = db_exists("SELECT 1 FROM private");
588585
db_multi_exec(
589
- "DELETE FROM concealed;"
590
- "UPDATE rcvfrom SET ipaddr='unknown';"
591
- "UPDATE user SET photo=NULL, info='';"
592
- "INSERT INTO shun SELECT uuid FROM blob WHERE rid IN private;"
586
+ "DELETE FROM blob WHERE rid IN private;"
587
+ "DELETE FROM delta WHERE rid IN private;"
588
+ "DELETE FROM private;"
589
+ );
590
+ }
591
+ if( !privateOnly ){
592
+ db_multi_exec(
593
+ "UPDATE user SET pw='';"
594
+ "DELETE FROM config WHERE name GLOB 'last-sync-*';"
593595
);
596
+ if( bVerily ){
597
+ db_multi_exec(
598
+ "DELETE FROM concealed;"
599
+ "UPDATE rcvfrom SET ipaddr='unknown';"
600
+ "UPDATE user SET photo=NULL, info='';"
601
+ );
602
+ }
594603
}
595604
if( !bNeedRebuild ){
596605
db_end_transaction(0);
597606
db_multi_exec("VACUUM;");
598607
}else{
599608
--- src/rebuild.c
+++ src/rebuild.c
@@ -540,19 +540,20 @@
540 }
541 }
542
543 /*
544 ** COMMAND: scrub
545 ** %fossil scrub [--verily] [--force] [REPOSITORY]
546 **
547 ** The command removes sensitive information (such as passwords) from a
548 ** repository so that the respository can be sent to an untrusted reader.
549 **
550 ** By default, only passwords are removed. However, if the --verily option
551 ** is added, then private branches, concealed email addresses, IP
552 ** addresses of correspondents, and similar privacy-sensitive fields
553 ** are also purged.
 
554 **
555 ** This command permanently deletes the scrubbed information. The effects
556 ** of this command are irreversible. Use with caution.
557 **
558 ** The user is prompted to confirm the scrub unless the --force option
@@ -559,10 +560,11 @@
559 ** is used.
560 */
561 void scrub_cmd(void){
562 int bVerily = find_option("verily",0,0)!=0;
563 int bForce = find_option("force", "f", 0)!=0;
 
564 int bNeedRebuild = 0;
565 if( g.argc!=2 && g.argc!=3 ) usage("?REPOSITORY?");
566 if( g.argc==2 ){
567 db_must_be_within_tree();
568 }else{
@@ -569,30 +571,37 @@
569 db_open_repository(g.argv[2]);
570 }
571 if( !bForce ){
572 Blob ans;
573 blob_zero(&ans);
574 prompt_user("Scrubbing the repository will permanently remove user\n"
575 "passwords and other information. Changes cannot be undone.\n"
576 "Continue (y/N)? ", &ans);
577 if( blob_str(&ans)[0]!='y' ){
578 fossil_exit(1);
579 }
580 }
581 db_begin_transaction();
582 db_multi_exec(
583 "UPDATE user SET pw='';"
584 "DELETE FROM config WHERE name GLOB 'last-sync-*';"
585 );
586 if( bVerily ){
587 bNeedRebuild = db_exists("SELECT 1 FROM private");
588 db_multi_exec(
589 "DELETE FROM concealed;"
590 "UPDATE rcvfrom SET ipaddr='unknown';"
591 "UPDATE user SET photo=NULL, info='';"
592 "INSERT INTO shun SELECT uuid FROM blob WHERE rid IN private;"
 
 
 
 
 
593 );
 
 
 
 
 
 
 
594 }
595 if( !bNeedRebuild ){
596 db_end_transaction(0);
597 db_multi_exec("VACUUM;");
598 }else{
599
--- src/rebuild.c
+++ src/rebuild.c
@@ -540,19 +540,20 @@
540 }
541 }
542
543 /*
544 ** COMMAND: scrub
545 ** %fossil scrub [--verily] [--force] [--private] [REPOSITORY]
546 **
547 ** The command removes sensitive information (such as passwords) from a
548 ** repository so that the respository can be sent to an untrusted reader.
549 **
550 ** By default, only passwords are removed. However, if the --verily option
551 ** is added, then private branches, concealed email addresses, IP
552 ** addresses of correspondents, and similar privacy-sensitive fields
553 ** are also purged. If the --private option is used, then only private
554 ** branches are removed and all other information is left intact.
555 **
556 ** This command permanently deletes the scrubbed information. The effects
557 ** of this command are irreversible. Use with caution.
558 **
559 ** The user is prompted to confirm the scrub unless the --force option
@@ -559,10 +560,11 @@
560 ** is used.
561 */
562 void scrub_cmd(void){
563 int bVerily = find_option("verily",0,0)!=0;
564 int bForce = find_option("force", "f", 0)!=0;
565 int privateOnly = find_option("private",0,0)!=0;
566 int bNeedRebuild = 0;
567 if( g.argc!=2 && g.argc!=3 ) usage("?REPOSITORY?");
568 if( g.argc==2 ){
569 db_must_be_within_tree();
570 }else{
@@ -569,30 +571,37 @@
571 db_open_repository(g.argv[2]);
572 }
573 if( !bForce ){
574 Blob ans;
575 blob_zero(&ans);
576 prompt_user("Scrubbing the repository will permanently information.\n"
577 "Changes cannot be undone. Continue (y/N)? ", &ans);
 
578 if( blob_str(&ans)[0]!='y' ){
579 fossil_exit(1);
580 }
581 }
582 db_begin_transaction();
583 if( privateOnly || bVerily ){
 
 
 
 
584 bNeedRebuild = db_exists("SELECT 1 FROM private");
585 db_multi_exec(
586 "DELETE FROM blob WHERE rid IN private;"
587 "DELETE FROM delta WHERE rid IN private;"
588 "DELETE FROM private;"
589 );
590 }
591 if( !privateOnly ){
592 db_multi_exec(
593 "UPDATE user SET pw='';"
594 "DELETE FROM config WHERE name GLOB 'last-sync-*';"
595 );
596 if( bVerily ){
597 db_multi_exec(
598 "DELETE FROM concealed;"
599 "UPDATE rcvfrom SET ipaddr='unknown';"
600 "UPDATE user SET photo=NULL, info='';"
601 );
602 }
603 }
604 if( !bNeedRebuild ){
605 db_end_transaction(0);
606 db_multi_exec("VACUUM;");
607 }else{
608
+8 -3
--- src/setup.c
+++ src/setup.c
@@ -122,11 +122,10 @@
122122
@ <th class="usetupListCon" style="text-align: left;">Contact&nbsp;Info</th>
123123
@ </tr>
124124
db_prepare(&s, "SELECT uid, login, cap, info FROM user ORDER BY login");
125125
while( db_step(&s)==SQLITE_ROW ){
126126
const char *zCap = db_column_text(&s, 2);
127
- if( strstr(zCap, "s") ) zCap = "s";
128127
@ <tr>
129128
@ <td class="usetupListUser" style="text-align: right;padding-right: 20px;white-space:nowrap;">
130129
if( g.okAdmin && (zCap[0]!='s' || g.okSetup) ){
131130
@ <a href="setup_uedit?id=%d(db_column_int(&s,0))">
132131
}
@@ -188,10 +187,12 @@
188187
@ <tr><td valign="top"><b>v</b></td>
189188
@ <td><i>Developer:</i> Inherit privileges of
190189
@ user <tt>developer</tt></td></tr>
191190
@ <tr><td valign="top"><b>w</b></td>
192191
@ <td><i>Write-Tkt:</i> Edit tickets</td></tr>
192
+ @ <tr><td valign="top"><b>x</b></td>
193
+ @ <td><i>Private:</i> Push and/or pull private branches</td></tr>
193194
@ <tr><td valign="top"><b>z</b></td>
194195
@ <td><i>Zip download:</i> Download a baseline via the
195196
@ <tt>/zip</tt> URL even without
196197
@ check<span class="capability">o</span>ut
197198
@ and <span class="capability">h</span>istory permissions</td></tr>
@@ -243,11 +244,11 @@
243244
*/
244245
void user_edit(void){
245246
const char *zId, *zLogin, *zInfo, *zCap, *zPw;
246247
char *oaa, *oas, *oar, *oaw, *oan, *oai, *oaj, *oao, *oap;
247248
char *oak, *oad, *oac, *oaf, *oam, *oah, *oag, *oae;
248
- char *oat, *oau, *oav, *oab, *oaz;
249
+ char *oat, *oau, *oav, *oab, *oax, *oaz;
249250
const char *inherit[128];
250251
int doWrite;
251252
int uid;
252253
int higherUser = 0; /* True if user being edited is SETUP and the */
253254
/* user doing the editing is ADMIN. Disallow editing */
@@ -300,10 +301,11 @@
300301
int ah = P("ah")!=0;
301302
int ag = P("ag")!=0;
302303
int at = P("at")!=0;
303304
int au = P("au")!=0;
304305
int av = P("av")!=0;
306
+ int ax = P("ax")!=0;
305307
int az = P("az")!=0;
306308
if( aa ){ zCap[i++] = 'a'; }
307309
if( ab ){ zCap[i++] = 'b'; }
308310
if( ac ){ zCap[i++] = 'c'; }
309311
if( ad ){ zCap[i++] = 'd'; }
@@ -322,10 +324,11 @@
322324
if( as ){ zCap[i++] = 's'; }
323325
if( at ){ zCap[i++] = 't'; }
324326
if( au ){ zCap[i++] = 'u'; }
325327
if( av ){ zCap[i++] = 'v'; }
326328
if( aw ){ zCap[i++] = 'w'; }
329
+ if( ax ){ zCap[i++] = 'x'; }
327330
if( az ){ zCap[i++] = 'z'; }
328331
329332
zCap[i] = 0;
330333
zPw = P("pw");
331334
zLogin = P("login");
@@ -360,11 +363,11 @@
360363
zLogin = "";
361364
zInfo = "";
362365
zCap = "";
363366
zPw = "";
364367
oaa = oab = oac = oad = oae = oaf = oag = oah = oai = oaj = oak = oam =
365
- oan = oao = oap = oar = oas = oat = oau = oav = oaw = oaz = "";
368
+ oan = oao = oap = oar = oas = oat = oau = oav = oaw = oax = oaz = "";
366369
if( uid ){
367370
zLogin = db_text("", "SELECT login FROM user WHERE uid=%d", uid);
368371
zInfo = db_text("", "SELECT info FROM user WHERE uid=%d", uid);
369372
zCap = db_text("", "SELECT cap FROM user WHERE uid=%d", uid);
370373
zPw = db_text("", "SELECT pw FROM user WHERE uid=%d", uid);
@@ -387,10 +390,11 @@
387390
if( strchr(zCap, 's') ) oas = " checked=\"checked\"";
388391
if( strchr(zCap, 't') ) oat = " checked=\"checked\"";
389392
if( strchr(zCap, 'u') ) oau = " checked=\"checked\"";
390393
if( strchr(zCap, 'v') ) oav = " checked=\"checked\"";
391394
if( strchr(zCap, 'w') ) oaw = " checked=\"checked\"";
395
+ if( strchr(zCap, 'x') ) oax = " checked=\"checked\"";
392396
if( strchr(zCap, 'z') ) oaz = " checked=\"checked\"";
393397
}
394398
395399
/* figure out inherited permissions */
396400
memset(inherit, 0, sizeof(inherit));
@@ -484,10 +488,11 @@
484488
@ <input type="checkbox" name="ar"%s(oar) />%s(B('r'))Read Ticket<br />
485489
@ <input type="checkbox" name="an"%s(oan) />%s(B('n'))New Ticket<br />
486490
@ <input type="checkbox" name="ac"%s(oac) />%s(B('c'))Append Ticket<br />
487491
@ <input type="checkbox" name="aw"%s(oaw) />%s(B('w'))Write Ticket<br />
488492
@ <input type="checkbox" name="at"%s(oat) />%s(B('t'))Ticket Report<br />
493
+ @ <input type="checkbox" name="ax"%s(oax) />%s(B('x'))Private<br />
489494
@ <input type="checkbox" name="az"%s(oaz) />%s(B('z'))Download Zip
490495
@ </td>
491496
@ </tr>
492497
@ <tr>
493498
@ <td align="right">Password:</td>
494499
--- src/setup.c
+++ src/setup.c
@@ -122,11 +122,10 @@
122 @ <th class="usetupListCon" style="text-align: left;">Contact&nbsp;Info</th>
123 @ </tr>
124 db_prepare(&s, "SELECT uid, login, cap, info FROM user ORDER BY login");
125 while( db_step(&s)==SQLITE_ROW ){
126 const char *zCap = db_column_text(&s, 2);
127 if( strstr(zCap, "s") ) zCap = "s";
128 @ <tr>
129 @ <td class="usetupListUser" style="text-align: right;padding-right: 20px;white-space:nowrap;">
130 if( g.okAdmin && (zCap[0]!='s' || g.okSetup) ){
131 @ <a href="setup_uedit?id=%d(db_column_int(&s,0))">
132 }
@@ -188,10 +187,12 @@
188 @ <tr><td valign="top"><b>v</b></td>
189 @ <td><i>Developer:</i> Inherit privileges of
190 @ user <tt>developer</tt></td></tr>
191 @ <tr><td valign="top"><b>w</b></td>
192 @ <td><i>Write-Tkt:</i> Edit tickets</td></tr>
 
 
193 @ <tr><td valign="top"><b>z</b></td>
194 @ <td><i>Zip download:</i> Download a baseline via the
195 @ <tt>/zip</tt> URL even without
196 @ check<span class="capability">o</span>ut
197 @ and <span class="capability">h</span>istory permissions</td></tr>
@@ -243,11 +244,11 @@
243 */
244 void user_edit(void){
245 const char *zId, *zLogin, *zInfo, *zCap, *zPw;
246 char *oaa, *oas, *oar, *oaw, *oan, *oai, *oaj, *oao, *oap;
247 char *oak, *oad, *oac, *oaf, *oam, *oah, *oag, *oae;
248 char *oat, *oau, *oav, *oab, *oaz;
249 const char *inherit[128];
250 int doWrite;
251 int uid;
252 int higherUser = 0; /* True if user being edited is SETUP and the */
253 /* user doing the editing is ADMIN. Disallow editing */
@@ -300,10 +301,11 @@
300 int ah = P("ah")!=0;
301 int ag = P("ag")!=0;
302 int at = P("at")!=0;
303 int au = P("au")!=0;
304 int av = P("av")!=0;
 
305 int az = P("az")!=0;
306 if( aa ){ zCap[i++] = 'a'; }
307 if( ab ){ zCap[i++] = 'b'; }
308 if( ac ){ zCap[i++] = 'c'; }
309 if( ad ){ zCap[i++] = 'd'; }
@@ -322,10 +324,11 @@
322 if( as ){ zCap[i++] = 's'; }
323 if( at ){ zCap[i++] = 't'; }
324 if( au ){ zCap[i++] = 'u'; }
325 if( av ){ zCap[i++] = 'v'; }
326 if( aw ){ zCap[i++] = 'w'; }
 
327 if( az ){ zCap[i++] = 'z'; }
328
329 zCap[i] = 0;
330 zPw = P("pw");
331 zLogin = P("login");
@@ -360,11 +363,11 @@
360 zLogin = "";
361 zInfo = "";
362 zCap = "";
363 zPw = "";
364 oaa = oab = oac = oad = oae = oaf = oag = oah = oai = oaj = oak = oam =
365 oan = oao = oap = oar = oas = oat = oau = oav = oaw = oaz = "";
366 if( uid ){
367 zLogin = db_text("", "SELECT login FROM user WHERE uid=%d", uid);
368 zInfo = db_text("", "SELECT info FROM user WHERE uid=%d", uid);
369 zCap = db_text("", "SELECT cap FROM user WHERE uid=%d", uid);
370 zPw = db_text("", "SELECT pw FROM user WHERE uid=%d", uid);
@@ -387,10 +390,11 @@
387 if( strchr(zCap, 's') ) oas = " checked=\"checked\"";
388 if( strchr(zCap, 't') ) oat = " checked=\"checked\"";
389 if( strchr(zCap, 'u') ) oau = " checked=\"checked\"";
390 if( strchr(zCap, 'v') ) oav = " checked=\"checked\"";
391 if( strchr(zCap, 'w') ) oaw = " checked=\"checked\"";
 
392 if( strchr(zCap, 'z') ) oaz = " checked=\"checked\"";
393 }
394
395 /* figure out inherited permissions */
396 memset(inherit, 0, sizeof(inherit));
@@ -484,10 +488,11 @@
484 @ <input type="checkbox" name="ar"%s(oar) />%s(B('r'))Read Ticket<br />
485 @ <input type="checkbox" name="an"%s(oan) />%s(B('n'))New Ticket<br />
486 @ <input type="checkbox" name="ac"%s(oac) />%s(B('c'))Append Ticket<br />
487 @ <input type="checkbox" name="aw"%s(oaw) />%s(B('w'))Write Ticket<br />
488 @ <input type="checkbox" name="at"%s(oat) />%s(B('t'))Ticket Report<br />
 
489 @ <input type="checkbox" name="az"%s(oaz) />%s(B('z'))Download Zip
490 @ </td>
491 @ </tr>
492 @ <tr>
493 @ <td align="right">Password:</td>
494
--- src/setup.c
+++ src/setup.c
@@ -122,11 +122,10 @@
122 @ <th class="usetupListCon" style="text-align: left;">Contact&nbsp;Info</th>
123 @ </tr>
124 db_prepare(&s, "SELECT uid, login, cap, info FROM user ORDER BY login");
125 while( db_step(&s)==SQLITE_ROW ){
126 const char *zCap = db_column_text(&s, 2);
 
127 @ <tr>
128 @ <td class="usetupListUser" style="text-align: right;padding-right: 20px;white-space:nowrap;">
129 if( g.okAdmin && (zCap[0]!='s' || g.okSetup) ){
130 @ <a href="setup_uedit?id=%d(db_column_int(&s,0))">
131 }
@@ -188,10 +187,12 @@
187 @ <tr><td valign="top"><b>v</b></td>
188 @ <td><i>Developer:</i> Inherit privileges of
189 @ user <tt>developer</tt></td></tr>
190 @ <tr><td valign="top"><b>w</b></td>
191 @ <td><i>Write-Tkt:</i> Edit tickets</td></tr>
192 @ <tr><td valign="top"><b>x</b></td>
193 @ <td><i>Private:</i> Push and/or pull private branches</td></tr>
194 @ <tr><td valign="top"><b>z</b></td>
195 @ <td><i>Zip download:</i> Download a baseline via the
196 @ <tt>/zip</tt> URL even without
197 @ check<span class="capability">o</span>ut
198 @ and <span class="capability">h</span>istory permissions</td></tr>
@@ -243,11 +244,11 @@
244 */
245 void user_edit(void){
246 const char *zId, *zLogin, *zInfo, *zCap, *zPw;
247 char *oaa, *oas, *oar, *oaw, *oan, *oai, *oaj, *oao, *oap;
248 char *oak, *oad, *oac, *oaf, *oam, *oah, *oag, *oae;
249 char *oat, *oau, *oav, *oab, *oax, *oaz;
250 const char *inherit[128];
251 int doWrite;
252 int uid;
253 int higherUser = 0; /* True if user being edited is SETUP and the */
254 /* user doing the editing is ADMIN. Disallow editing */
@@ -300,10 +301,11 @@
301 int ah = P("ah")!=0;
302 int ag = P("ag")!=0;
303 int at = P("at")!=0;
304 int au = P("au")!=0;
305 int av = P("av")!=0;
306 int ax = P("ax")!=0;
307 int az = P("az")!=0;
308 if( aa ){ zCap[i++] = 'a'; }
309 if( ab ){ zCap[i++] = 'b'; }
310 if( ac ){ zCap[i++] = 'c'; }
311 if( ad ){ zCap[i++] = 'd'; }
@@ -322,10 +324,11 @@
324 if( as ){ zCap[i++] = 's'; }
325 if( at ){ zCap[i++] = 't'; }
326 if( au ){ zCap[i++] = 'u'; }
327 if( av ){ zCap[i++] = 'v'; }
328 if( aw ){ zCap[i++] = 'w'; }
329 if( ax ){ zCap[i++] = 'x'; }
330 if( az ){ zCap[i++] = 'z'; }
331
332 zCap[i] = 0;
333 zPw = P("pw");
334 zLogin = P("login");
@@ -360,11 +363,11 @@
363 zLogin = "";
364 zInfo = "";
365 zCap = "";
366 zPw = "";
367 oaa = oab = oac = oad = oae = oaf = oag = oah = oai = oaj = oak = oam =
368 oan = oao = oap = oar = oas = oat = oau = oav = oaw = oax = oaz = "";
369 if( uid ){
370 zLogin = db_text("", "SELECT login FROM user WHERE uid=%d", uid);
371 zInfo = db_text("", "SELECT info FROM user WHERE uid=%d", uid);
372 zCap = db_text("", "SELECT cap FROM user WHERE uid=%d", uid);
373 zPw = db_text("", "SELECT pw FROM user WHERE uid=%d", uid);
@@ -387,10 +390,11 @@
390 if( strchr(zCap, 's') ) oas = " checked=\"checked\"";
391 if( strchr(zCap, 't') ) oat = " checked=\"checked\"";
392 if( strchr(zCap, 'u') ) oau = " checked=\"checked\"";
393 if( strchr(zCap, 'v') ) oav = " checked=\"checked\"";
394 if( strchr(zCap, 'w') ) oaw = " checked=\"checked\"";
395 if( strchr(zCap, 'x') ) oax = " checked=\"checked\"";
396 if( strchr(zCap, 'z') ) oaz = " checked=\"checked\"";
397 }
398
399 /* figure out inherited permissions */
400 memset(inherit, 0, sizeof(inherit));
@@ -484,10 +488,11 @@
488 @ <input type="checkbox" name="ar"%s(oar) />%s(B('r'))Read Ticket<br />
489 @ <input type="checkbox" name="an"%s(oan) />%s(B('n'))New Ticket<br />
490 @ <input type="checkbox" name="ac"%s(oac) />%s(B('c'))Append Ticket<br />
491 @ <input type="checkbox" name="aw"%s(oaw) />%s(B('w'))Write Ticket<br />
492 @ <input type="checkbox" name="at"%s(oat) />%s(B('t'))Ticket Report<br />
493 @ <input type="checkbox" name="ax"%s(oax) />%s(B('x'))Private<br />
494 @ <input type="checkbox" name="az"%s(oaz) />%s(B('z'))Download Zip
495 @ </td>
496 @ </tr>
497 @ <tr>
498 @ <td align="right">Password:</td>
499
+25 -9
--- src/sync.c
+++ src/sync.c
@@ -78,11 +78,11 @@
7878
configSync = CONFIGSET_SHUN;
7979
}
8080
#endif
8181
printf("Autosync: %s\n", g.urlCanonical);
8282
url_enable_proxy("via proxy: ");
83
- rc = client_sync((flags & AUTOSYNC_PUSH)!=0, 1, 0, configSync, 0);
83
+ rc = client_sync((flags & AUTOSYNC_PUSH)!=0, 1, 0, 0, configSync, 0);
8484
if( rc ) fossil_warning("Autosync failed");
8585
return rc;
8686
}
8787
8888
/*
@@ -89,16 +89,17 @@
8989
** This routine processes the command-line argument for push, pull,
9090
** and sync. If a command-line argument is given, that is the URL
9191
** of a server to sync against. If no argument is given, use the
9292
** most recently synced URL. Remember the current URL for next time.
9393
*/
94
-static int process_sync_args(void){
94
+static void process_sync_args(int *pConfigSync, int *pPrivate){
9595
const char *zUrl = 0;
9696
const char *zPw = 0;
9797
int configSync = 0;
9898
int urlOptional = find_option("autourl",0,0)!=0;
9999
g.dontKeepUrl = find_option("once",0,0)!=0;
100
+ *pPrivate = find_option("private",0,0)!=0;
100101
url_proxy_options();
101102
db_find_and_open_repository(0, 0);
102103
db_open_config(0);
103104
if( g.argc==2 ){
104105
zUrl = db_get("last-sync-url", 0);
@@ -126,11 +127,11 @@
126127
user_select();
127128
if( g.argc==2 ){
128129
printf("Server: %s\n", g.urlCanonical);
129130
}
130131
url_enable_proxy("via proxy: ");
131
- return configSync;
132
+ *pConfigSync = configSync;
132133
}
133134
134135
/*
135136
** COMMAND: pull
136137
**
@@ -145,16 +146,21 @@
145146
**
146147
** The URL specified normally becomes the new "remote-url" used for
147148
** subsequent push, pull, and sync operations. However, the "--once"
148149
** command-line option makes the URL a one-time-use URL that is not
149150
** saved.
151
+**
152
+** Use the --private option to pull private branches from the
153
+** remote repository.
150154
**
151155
** See also: clone, push, sync, remote-url
152156
*/
153157
void pull_cmd(void){
154
- int syncFlags = process_sync_args();
155
- client_sync(0,1,0,syncFlags,0);
158
+ int syncFlags;
159
+ int bPrivate;
160
+ process_sync_args(&syncFlags, &bPrivate);
161
+ client_sync(0,1,0,bPrivate,syncFlags,0);
156162
}
157163
158164
/*
159165
** COMMAND: push
160166
**
@@ -169,16 +175,21 @@
169175
**
170176
** The URL specified normally becomes the new "remote-url" used for
171177
** subsequent push, pull, and sync operations. However, the "--once"
172178
** command-line option makes the URL a one-time-use URL that is not
173179
** saved.
180
+**
181
+** Use the --private option to push private branches to the
182
+** remote repository.
174183
**
175184
** See also: clone, pull, sync, remote-url
176185
*/
177186
void push_cmd(void){
178
- process_sync_args();
179
- client_sync(1,0,0,0,0);
187
+ int syncFlags;
188
+ int bPrivate;
189
+ process_sync_args(&syncFlags, &bPrivate);
190
+ client_sync(1,0,0,bPrivate,0,0);
180191
}
181192
182193
183194
/*
184195
** COMMAND: sync
@@ -199,16 +210,21 @@
199210
**
200211
** The URL specified normally becomes the new "remote-url" used for
201212
** subsequent push, pull, and sync operations. However, the "--once"
202213
** command-line option makes the URL a one-time-use URL that is not
203214
** saved.
215
+**
216
+** Use the --private option to sync private branches with the
217
+** remote repository.
204218
**
205219
** See also: clone, push, pull, remote-url
206220
*/
207221
void sync_cmd(void){
208
- int syncFlags = process_sync_args();
209
- client_sync(1,1,0,syncFlags,0);
222
+ int syncFlags;
223
+ int bPrivate;
224
+ process_sync_args(&syncFlags, &bPrivate);
225
+ client_sync(1,1,0,bPrivate,syncFlags,0);
210226
}
211227
212228
/*
213229
** COMMAND: remote-url
214230
**
215231
--- src/sync.c
+++ src/sync.c
@@ -78,11 +78,11 @@
78 configSync = CONFIGSET_SHUN;
79 }
80 #endif
81 printf("Autosync: %s\n", g.urlCanonical);
82 url_enable_proxy("via proxy: ");
83 rc = client_sync((flags & AUTOSYNC_PUSH)!=0, 1, 0, configSync, 0);
84 if( rc ) fossil_warning("Autosync failed");
85 return rc;
86 }
87
88 /*
@@ -89,16 +89,17 @@
89 ** This routine processes the command-line argument for push, pull,
90 ** and sync. If a command-line argument is given, that is the URL
91 ** of a server to sync against. If no argument is given, use the
92 ** most recently synced URL. Remember the current URL for next time.
93 */
94 static int process_sync_args(void){
95 const char *zUrl = 0;
96 const char *zPw = 0;
97 int configSync = 0;
98 int urlOptional = find_option("autourl",0,0)!=0;
99 g.dontKeepUrl = find_option("once",0,0)!=0;
 
100 url_proxy_options();
101 db_find_and_open_repository(0, 0);
102 db_open_config(0);
103 if( g.argc==2 ){
104 zUrl = db_get("last-sync-url", 0);
@@ -126,11 +127,11 @@
126 user_select();
127 if( g.argc==2 ){
128 printf("Server: %s\n", g.urlCanonical);
129 }
130 url_enable_proxy("via proxy: ");
131 return configSync;
132 }
133
134 /*
135 ** COMMAND: pull
136 **
@@ -145,16 +146,21 @@
145 **
146 ** The URL specified normally becomes the new "remote-url" used for
147 ** subsequent push, pull, and sync operations. However, the "--once"
148 ** command-line option makes the URL a one-time-use URL that is not
149 ** saved.
 
 
 
150 **
151 ** See also: clone, push, sync, remote-url
152 */
153 void pull_cmd(void){
154 int syncFlags = process_sync_args();
155 client_sync(0,1,0,syncFlags,0);
 
 
156 }
157
158 /*
159 ** COMMAND: push
160 **
@@ -169,16 +175,21 @@
169 **
170 ** The URL specified normally becomes the new "remote-url" used for
171 ** subsequent push, pull, and sync operations. However, the "--once"
172 ** command-line option makes the URL a one-time-use URL that is not
173 ** saved.
 
 
 
174 **
175 ** See also: clone, pull, sync, remote-url
176 */
177 void push_cmd(void){
178 process_sync_args();
179 client_sync(1,0,0,0,0);
 
 
180 }
181
182
183 /*
184 ** COMMAND: sync
@@ -199,16 +210,21 @@
199 **
200 ** The URL specified normally becomes the new "remote-url" used for
201 ** subsequent push, pull, and sync operations. However, the "--once"
202 ** command-line option makes the URL a one-time-use URL that is not
203 ** saved.
 
 
 
204 **
205 ** See also: clone, push, pull, remote-url
206 */
207 void sync_cmd(void){
208 int syncFlags = process_sync_args();
209 client_sync(1,1,0,syncFlags,0);
 
 
210 }
211
212 /*
213 ** COMMAND: remote-url
214 **
215
--- src/sync.c
+++ src/sync.c
@@ -78,11 +78,11 @@
78 configSync = CONFIGSET_SHUN;
79 }
80 #endif
81 printf("Autosync: %s\n", g.urlCanonical);
82 url_enable_proxy("via proxy: ");
83 rc = client_sync((flags & AUTOSYNC_PUSH)!=0, 1, 0, 0, configSync, 0);
84 if( rc ) fossil_warning("Autosync failed");
85 return rc;
86 }
87
88 /*
@@ -89,16 +89,17 @@
89 ** This routine processes the command-line argument for push, pull,
90 ** and sync. If a command-line argument is given, that is the URL
91 ** of a server to sync against. If no argument is given, use the
92 ** most recently synced URL. Remember the current URL for next time.
93 */
94 static void process_sync_args(int *pConfigSync, int *pPrivate){
95 const char *zUrl = 0;
96 const char *zPw = 0;
97 int configSync = 0;
98 int urlOptional = find_option("autourl",0,0)!=0;
99 g.dontKeepUrl = find_option("once",0,0)!=0;
100 *pPrivate = find_option("private",0,0)!=0;
101 url_proxy_options();
102 db_find_and_open_repository(0, 0);
103 db_open_config(0);
104 if( g.argc==2 ){
105 zUrl = db_get("last-sync-url", 0);
@@ -126,11 +127,11 @@
127 user_select();
128 if( g.argc==2 ){
129 printf("Server: %s\n", g.urlCanonical);
130 }
131 url_enable_proxy("via proxy: ");
132 *pConfigSync = configSync;
133 }
134
135 /*
136 ** COMMAND: pull
137 **
@@ -145,16 +146,21 @@
146 **
147 ** The URL specified normally becomes the new "remote-url" used for
148 ** subsequent push, pull, and sync operations. However, the "--once"
149 ** command-line option makes the URL a one-time-use URL that is not
150 ** saved.
151 **
152 ** Use the --private option to pull private branches from the
153 ** remote repository.
154 **
155 ** See also: clone, push, sync, remote-url
156 */
157 void pull_cmd(void){
158 int syncFlags;
159 int bPrivate;
160 process_sync_args(&syncFlags, &bPrivate);
161 client_sync(0,1,0,bPrivate,syncFlags,0);
162 }
163
164 /*
165 ** COMMAND: push
166 **
@@ -169,16 +175,21 @@
175 **
176 ** The URL specified normally becomes the new "remote-url" used for
177 ** subsequent push, pull, and sync operations. However, the "--once"
178 ** command-line option makes the URL a one-time-use URL that is not
179 ** saved.
180 **
181 ** Use the --private option to push private branches to the
182 ** remote repository.
183 **
184 ** See also: clone, pull, sync, remote-url
185 */
186 void push_cmd(void){
187 int syncFlags;
188 int bPrivate;
189 process_sync_args(&syncFlags, &bPrivate);
190 client_sync(1,0,0,bPrivate,0,0);
191 }
192
193
194 /*
195 ** COMMAND: sync
@@ -199,16 +210,21 @@
210 **
211 ** The URL specified normally becomes the new "remote-url" used for
212 ** subsequent push, pull, and sync operations. However, the "--once"
213 ** command-line option makes the URL a one-time-use URL that is not
214 ** saved.
215 **
216 ** Use the --private option to sync private branches with the
217 ** remote repository.
218 **
219 ** See also: clone, push, pull, remote-url
220 */
221 void sync_cmd(void){
222 int syncFlags;
223 int bPrivate;
224 process_sync_args(&syncFlags, &bPrivate);
225 client_sync(1,1,0,bPrivate,syncFlags,0);
226 }
227
228 /*
229 ** COMMAND: remote-url
230 **
231
+6
--- src/tag.c
+++ src/tag.c
@@ -204,10 +204,16 @@
204204
break;
205205
}
206206
case TAG_USER: {
207207
zCol = "euser";
208208
break;
209
+ }
210
+ case TAG_PRIVATE: {
211
+ db_multi_exec(
212
+ "INSERT OR IGNORE INTO private(rid) VALUES(%d);",
213
+ rid
214
+ );
209215
}
210216
}
211217
if( zCol ){
212218
db_multi_exec("UPDATE event SET %s=%Q WHERE objid=%d", zCol, zValue, rid);
213219
if( tagid==TAG_COMMENT ){
214220
--- src/tag.c
+++ src/tag.c
@@ -204,10 +204,16 @@
204 break;
205 }
206 case TAG_USER: {
207 zCol = "euser";
208 break;
 
 
 
 
 
 
209 }
210 }
211 if( zCol ){
212 db_multi_exec("UPDATE event SET %s=%Q WHERE objid=%d", zCol, zValue, rid);
213 if( tagid==TAG_COMMENT ){
214
--- src/tag.c
+++ src/tag.c
@@ -204,10 +204,16 @@
204 break;
205 }
206 case TAG_USER: {
207 zCol = "euser";
208 break;
209 }
210 case TAG_PRIVATE: {
211 db_multi_exec(
212 "INSERT OR IGNORE INTO private(rid) VALUES(%d);",
213 rid
214 );
215 }
216 }
217 if( zCol ){
218 db_multi_exec("UPDATE event SET %s=%Q WHERE objid=%d", zCol, zValue, rid);
219 if( tagid==TAG_COMMENT ){
220
+185 -39
--- src/xfer.c
+++ src/xfer.c
@@ -38,10 +38,12 @@
3838
int nDeltaSent; /* Number of deltas sent */
3939
int nFileRcvd; /* Number of files received */
4040
int nDeltaRcvd; /* Number of deltas received */
4141
int nDanglingFile; /* Number of dangling deltas received */
4242
int mxSend; /* Stop sending "file" with pOut reaches this size */
43
+ u8 syncPrivate; /* True to enable syncing private content */
44
+ u8 nextIsPrivate; /* If true, next "file" received is a private */
4345
};
4446
4547
4648
/*
4749
** The input blob contains a UUID. Convert it into a record ID.
@@ -49,11 +51,11 @@
4951
** phantomize is true.
5052
**
5153
** Compare to uuid_to_rid(). This routine takes a blob argument
5254
** and does less error checking.
5355
*/
54
-static int rid_from_uuid(Blob *pUuid, int phantomize){
56
+static int rid_from_uuid(Blob *pUuid, int phantomize, int isPrivate){
5557
static Stmt q;
5658
int rid;
5759
5860
db_static_prepare(&q, "SELECT rid FROM blob WHERE uuid=:uuid");
5961
db_bind_str(&q, ":uuid", pUuid);
@@ -62,11 +64,11 @@
6264
}else{
6365
rid = 0;
6466
}
6567
db_reset(&q);
6668
if( rid==0 && phantomize ){
67
- rid = content_new(blob_str(pUuid), 0);
69
+ rid = content_new(blob_str(pUuid), isPrivate);
6870
}
6971
return rid;
7072
}
7173
7274
/*
@@ -106,11 +108,14 @@
106108
static void xfer_accept_file(Xfer *pXfer, int cloneFlag){
107109
int n;
108110
int rid;
109111
int srcid = 0;
110112
Blob content, hash;
113
+ int isPriv;
111114
115
+ isPriv = pXfer->nextIsPrivate;
116
+ pXfer->nextIsPrivate = 0;
112117
if( pXfer->nToken<3
113118
|| pXfer->nToken>4
114119
|| !blob_is_uuid(&pXfer->aToken[1])
115120
|| !blob_is_int(&pXfer->aToken[pXfer->nToken-1], &n)
116121
|| n<0
@@ -123,32 +128,38 @@
123128
blob_zero(&hash);
124129
blob_extract(pXfer->pIn, n, &content);
125130
if( !cloneFlag && uuid_is_shunned(blob_str(&pXfer->aToken[1])) ){
126131
/* Ignore files that have been shunned */
127132
return;
133
+ }
134
+ if( isPriv && !g.okPrivate ){
135
+ /* Do not accept private files if not authorized */
136
+ return;
128137
}
129138
if( cloneFlag ){
130139
if( pXfer->nToken==4 ){
131
- srcid = rid_from_uuid(&pXfer->aToken[2], 1);
140
+ srcid = rid_from_uuid(&pXfer->aToken[2], 1, isPriv);
132141
pXfer->nDeltaRcvd++;
133142
}else{
134143
srcid = 0;
135144
pXfer->nFileRcvd++;
136145
}
137
- rid = content_put_ex(&content, blob_str(&pXfer->aToken[1]), srcid, 0, 0);
146
+ rid = content_put_ex(&content, blob_str(&pXfer->aToken[1]), srcid,
147
+ 0, isPriv);
138148
remote_has(rid);
139149
blob_reset(&content);
140150
return;
141151
}
142152
if( pXfer->nToken==4 ){
143153
Blob src, next;
144
- srcid = rid_from_uuid(&pXfer->aToken[2], 1);
154
+ srcid = rid_from_uuid(&pXfer->aToken[2], 1, isPriv);
145155
if( content_get(srcid, &src)==0 ){
146
- rid = content_put_ex(&content, blob_str(&pXfer->aToken[1]), srcid, 0, 0);
156
+ rid = content_put_ex(&content, blob_str(&pXfer->aToken[1]), srcid,
157
+ 0, isPriv);
147158
pXfer->nDanglingFile++;
148159
db_multi_exec("DELETE FROM phantom WHERE rid=%d", rid);
149
- content_make_public(rid);
160
+ if( !isPriv ) content_make_public(rid);
150161
return;
151162
}
152163
pXfer->nDeltaRcvd++;
153164
blob_delta_apply(&src, &content, &next);
154165
blob_reset(&src);
@@ -159,17 +170,17 @@
159170
}
160171
sha1sum_blob(&content, &hash);
161172
if( !blob_eq_str(&pXfer->aToken[1], blob_str(&hash), -1) ){
162173
blob_appendf(&pXfer->err, "content does not match sha1 hash");
163174
}
164
- rid = content_put_ex(&content, blob_str(&hash), 0, 0, 0);
175
+ rid = content_put_ex(&content, blob_str(&hash), 0, 0, isPriv);
165176
blob_reset(&hash);
166177
if( rid==0 ){
167178
blob_appendf(&pXfer->err, "%s", g.zErrMsg);
168179
blob_reset(&content);
169180
}else{
170
- content_make_public(rid);
181
+ if( !isPriv ) content_make_public(rid);
171182
manifest_crosslink(rid, &content);
172183
}
173184
assert( blob_is_reset(&content) );
174185
remote_has(rid);
175186
}
@@ -201,11 +212,14 @@
201212
int szC; /* CSIZE */
202213
int szU; /* USIZE */
203214
int rid;
204215
int srcid = 0;
205216
Blob content;
217
+ int isPriv;
206218
219
+ isPriv = pXfer->nextIsPrivate;
220
+ pXfer->nextIsPrivate = 0;
207221
if( pXfer->nToken<4
208222
|| pXfer->nToken>5
209223
|| !blob_is_uuid(&pXfer->aToken[1])
210224
|| !blob_is_int(&pXfer->aToken[pXfer->nToken-2], &szU)
211225
|| !blob_is_int(&pXfer->aToken[pXfer->nToken-1], &szC)
@@ -212,25 +226,30 @@
212226
|| szC<0 || szU<0
213227
|| (pXfer->nToken==5 && !blob_is_uuid(&pXfer->aToken[2]))
214228
){
215229
blob_appendf(&pXfer->err, "malformed cfile line");
216230
return;
231
+ }
232
+ if( isPriv && !g.okPrivate ){
233
+ /* Do not accept private files if not authorized */
234
+ return;
217235
}
218236
blob_zero(&content);
219237
blob_extract(pXfer->pIn, szC, &content);
220238
if( uuid_is_shunned(blob_str(&pXfer->aToken[1])) ){
221239
/* Ignore files that have been shunned */
222240
return;
223241
}
224242
if( pXfer->nToken==5 ){
225
- srcid = rid_from_uuid(&pXfer->aToken[2], 1);
243
+ srcid = rid_from_uuid(&pXfer->aToken[2], 1, isPriv);
226244
pXfer->nDeltaRcvd++;
227245
}else{
228246
srcid = 0;
229247
pXfer->nFileRcvd++;
230248
}
231
- rid = content_put_ex(&content, blob_str(&pXfer->aToken[1]), srcid, szC, 0);
249
+ rid = content_put_ex(&content, blob_str(&pXfer->aToken[1]), srcid,
250
+ szC, isPriv);
232251
remote_has(rid);
233252
blob_reset(&content);
234253
}
235254
236255
/*
@@ -242,10 +261,11 @@
242261
** Never send a delta against a private artifact.
243262
*/
244263
static int send_delta_parent(
245264
Xfer *pXfer, /* The transfer context */
246265
int rid, /* record id of the file to send */
266
+ int isPrivate, /* True if rid is a private artifact */
247267
Blob *pContent, /* The content of the file to send */
248268
Blob *pUuid /* The UUID of the file to send */
249269
){
250270
static const char *azQuery[] = {
251271
"SELECT pid FROM plink x"
@@ -266,22 +286,25 @@
266286
int srcId = 0;
267287
268288
for(i=0; srcId==0 && i<count(azQuery); i++){
269289
srcId = db_int(0, azQuery[i], rid);
270290
}
271
- if( srcId>0 && !content_is_private(srcId) && content_get(srcId, &src) ){
291
+ if( srcId>0
292
+ && (pXfer->syncPrivate || !content_is_private(srcId))
293
+ && content_get(srcId, &src)
294
+ ){
272295
char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", srcId);
273296
blob_delta_create(&src, pContent, &delta);
274297
size = blob_size(&delta);
275298
if( size>=blob_size(pContent)-50 ){
276299
size = 0;
277300
}else if( uuid_is_shunned(zUuid) ){
278301
size = 0;
279302
}else{
303
+ if( isPrivate ) blob_append(pXfer->pOut, "private\n", -1);
280304
blob_appendf(pXfer->pOut, "file %b %s %d\n", pUuid, zUuid, size);
281305
blob_append(pXfer->pOut, blob_buffer(&delta), size);
282
- /* blob_appendf(pXfer->pOut, "\n", 1); */
283306
}
284307
blob_reset(&delta);
285308
free(zUuid);
286309
blob_reset(&src);
287310
}
@@ -297,27 +320,31 @@
297320
** Never send a delta against a private artifact.
298321
*/
299322
static int send_delta_native(
300323
Xfer *pXfer, /* The transfer context */
301324
int rid, /* record id of the file to send */
325
+ int isPrivate, /* True if rid is a private artifact */
302326
Blob *pUuid /* The UUID of the file to send */
303327
){
304328
Blob src, delta;
305329
int size = 0;
306330
int srcId;
307331
308332
srcId = db_int(0, "SELECT srcid FROM delta WHERE rid=%d", rid);
309
- if( srcId>0 && !content_is_private(srcId) ){
333
+ if( srcId>0
334
+ && (pXfer->syncPrivate || !content_is_private(srcId))
335
+ ){
310336
blob_zero(&src);
311337
db_blob(&src, "SELECT uuid FROM blob WHERE rid=%d", srcId);
312338
if( uuid_is_shunned(blob_str(&src)) ){
313339
blob_reset(&src);
314340
return 0;
315341
}
316342
blob_zero(&delta);
317343
db_blob(&delta, "SELECT content FROM blob WHERE rid=%d", rid);
318344
blob_uncompress(&delta, &delta);
345
+ if( isPrivate ) blob_append(pXfer->pOut, "private\n", -1);
319346
blob_appendf(pXfer->pOut, "file %b %b %d\n",
320347
pUuid, &src, blob_size(&delta));
321348
blob_append(pXfer->pOut, blob_buffer(&delta), blob_size(&delta));
322349
size = blob_size(&delta);
323350
blob_reset(&delta);
@@ -342,12 +369,13 @@
342369
** this routine becomes a no-op.
343370
*/
344371
static void send_file(Xfer *pXfer, int rid, Blob *pUuid, int nativeDelta){
345372
Blob content, uuid;
346373
int size = 0;
374
+ int isPriv = content_is_private(rid);
347375
348
- if( content_is_private(rid) ) return;
376
+ if( pXfer->syncPrivate==0 && isPriv ) return;
349377
if( db_exists("SELECT 1 FROM onremote WHERE rid=%d", rid) ){
350378
return;
351379
}
352380
blob_zero(&uuid);
353381
db_blob(&uuid, "SELECT uuid FROM blob WHERE rid=%d AND size>=0", rid);
@@ -365,38 +393,45 @@
365393
if( uuid_is_shunned(blob_str(pUuid)) ){
366394
blob_reset(&uuid);
367395
return;
368396
}
369397
if( pXfer->mxSend<=blob_size(pXfer->pOut) ){
370
- blob_appendf(pXfer->pOut, "igot %b\n", pUuid);
398
+ const char *zFormat = isPriv ? "igot %b 1\n" : "igot %b\n";
399
+ blob_appendf(pXfer->pOut, zFormat, pUuid);
371400
pXfer->nIGotSent++;
372401
blob_reset(&uuid);
373402
return;
374403
}
375404
if( nativeDelta ){
376
- size = send_delta_native(pXfer, rid, pUuid);
405
+ size = send_delta_native(pXfer, rid, isPriv, pUuid);
377406
if( size ){
378407
pXfer->nDeltaSent++;
379408
}
380409
}
381410
if( size==0 ){
382411
content_get(rid, &content);
383412
384413
if( !nativeDelta && blob_size(&content)>100 ){
385
- size = send_delta_parent(pXfer, rid, &content, pUuid);
414
+ size = send_delta_parent(pXfer, rid, isPriv, &content, pUuid);
386415
}
387416
if( size==0 ){
388417
int size = blob_size(&content);
418
+ if( isPriv ) blob_append(pXfer->pOut, "private\n", -1);
389419
blob_appendf(pXfer->pOut, "file %b %d\n", pUuid, size);
390420
blob_append(pXfer->pOut, blob_buffer(&content), size);
391421
pXfer->nFileSent++;
392422
}else{
393423
pXfer->nDeltaSent++;
394424
}
395425
}
396426
remote_has(rid);
397427
blob_reset(&uuid);
428
+#if 0
429
+ if( blob_buffer(pXfer->pOut)[blob_size(pXfer->pOut)-1]!='\n' ){
430
+ blob_appendf(pXfer->pOut, "\n", 1);
431
+ }
432
+#endif
398433
}
399434
400435
/*
401436
** Send the file identified by rid as a compressed artifact. Basically,
402437
** send the content exactly as it appears in the BLOB table using
@@ -407,57 +442,62 @@
407442
const char *zUuid;
408443
const char *zDelta;
409444
int szU;
410445
int szC;
411446
int rc;
447
+ int isPrivate;
412448
static Stmt q1;
413449
450
+ isPrivate = content_is_private(rid);
451
+ if( isPrivate && pXfer->syncPrivate==0 ) return;
414452
db_static_prepare(&q1,
415453
"SELECT uuid, size, content,"
416454
" (SELECT uuid FROM delta, blob"
417455
" WHERE delta.rid=:rid AND delta.srcid=blob.rid)"
418456
" FROM blob"
419457
" WHERE rid=:rid"
420458
" AND size>=0"
421459
" AND uuid NOT IN shun"
422
- " AND rid NOT IN private",
423
- rid
424460
);
425461
db_bind_int(&q1, ":rid", rid);
426462
rc = db_step(&q1);
427463
if( rc==SQLITE_ROW ){
428464
zUuid = db_column_text(&q1, 0);
429465
szU = db_column_int(&q1, 1);
430466
szC = db_column_bytes(&q1, 2);
431467
zContent = db_column_raw(&q1, 2);
432468
zDelta = db_column_text(&q1, 3);
469
+ if( isPrivate ) blob_append(pXfer->pOut, "private\n", -1);
433470
blob_appendf(pXfer->pOut, "cfile %s ", zUuid);
434
- if( zDelta ){
471
+ if( zDelta ){
435472
blob_appendf(pXfer->pOut, "%s ", zDelta);
436473
pXfer->nDeltaSent++;
437474
}else{
438475
pXfer->nFileSent++;
439476
}
440477
blob_appendf(pXfer->pOut, "%d %d\n", szU, szC);
441478
blob_append(pXfer->pOut, zContent, szC);
442
- blob_append(pXfer->pOut, "\n", 1);
479
+ if( blob_buffer(pXfer->pOut)[blob_size(pXfer->pOut)-1]!='\n' ){
480
+ blob_appendf(pXfer->pOut, "\n", 1);
481
+ }
443482
}
444483
db_reset(&q1);
445484
}
446485
447486
/*
448487
** Send a gimme message for every phantom.
449488
**
450
-** It should not be possible to have a private phantom. But just to be
451
-** sure, take care not to send any "gimme" messagse on private artifacts.
489
+** Except: do not request shunned artifacts. And do not request
490
+** private artifacts if we are not doing a private transfer.
452491
*/
453492
static void request_phantoms(Xfer *pXfer, int maxReq){
454493
Stmt q;
455494
db_prepare(&q,
456495
"SELECT uuid FROM phantom JOIN blob USING(rid)"
457
- " WHERE NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid)"
458
- " AND NOT EXISTS(SELECT 1 FROM private WHERE rid=blob.rid)"
496
+ " WHERE NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid) %s",
497
+ (pXfer->syncPrivate ? "" :
498
+ " AND NOT EXISTS(SELECT 1 FROM private WHERE rid=blob.rid)")
459499
);
460500
while( db_step(&q)==SQLITE_ROW && maxReq-- > 0 ){
461501
const char *zUuid = db_column_text(&q, 0);
462502
blob_appendf(pXfer->pOut, "gimme %s\n", zUuid);
463503
pXfer->nGimmeSent++;
@@ -641,10 +681,27 @@
641681
content_put(&cluster);
642682
blob_reset(&cluster);
643683
}
644684
}
645685
}
686
+
687
+/*
688
+** Send igot messages for every private artifact
689
+*/
690
+static int send_private(Xfer *pXfer){
691
+ int cnt = 0;
692
+ Stmt q;
693
+ if( pXfer->syncPrivate ){
694
+ db_prepare(&q, "SELECT uuid FROM private JOIN blob USING(rid)");
695
+ while( db_step(&q)==SQLITE_ROW ){
696
+ blob_appendf(pXfer->pOut, "igot %s 1\n", db_column_text(&q,0));
697
+ cnt++;
698
+ }
699
+ db_finalize(&q);
700
+ }
701
+ return cnt;
702
+}
646703
647704
/*
648705
** Send an igot message for every entry in unclustered table.
649706
** Return the number of cards sent.
650707
*/
@@ -652,12 +709,12 @@
652709
Stmt q;
653710
int cnt = 0;
654711
db_prepare(&q,
655712
"SELECT uuid FROM unclustered JOIN blob USING(rid)"
656713
" WHERE NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid)"
657
- " AND NOT EXISTS(SELECT 1 FROM private WHERE rid=blob.rid)"
658714
" AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=blob.rid)"
715
+ " AND NOT EXISTS(SELECT 1 FROM private WHERE rid=blob.rid)"
659716
);
660717
while( db_step(&q)==SQLITE_ROW ){
661718
blob_appendf(pXfer->pOut, "igot %s\n", db_column_text(&q, 0));
662719
cnt++;
663720
}
@@ -704,10 +761,19 @@
704761
blob_size(&content), blob_str(&content));
705762
blob_reset(&content);
706763
}
707764
}
708765
766
+
767
+/*
768
+** Called when there is an attempt to transfer private content to and
769
+** from a server without authorization.
770
+*/
771
+static void server_private_xfer_not_authorized(void){
772
+ @ error not\sauthorized\sto\ssync\sprivate\scontent
773
+}
774
+
709775
710776
/*
711777
** If this variable is set, disable login checks. Used for debugging
712778
** only.
713779
*/
@@ -757,10 +823,11 @@
757823
"CREATE TEMP TABLE onremote(rid INTEGER PRIMARY KEY);"
758824
);
759825
manifest_crosslink_begin();
760826
while( blob_line(xfer.pIn, &xfer.line) ){
761827
if( blob_buffer(&xfer.line)[0]=='#' ) continue;
828
+ if( blob_size(&xfer.line)==0 ) continue;
762829
xfer.nToken = blob_tokenize(&xfer.line, xfer.aToken, count(xfer.aToken));
763830
764831
/* file UUID SIZE \n CONTENT
765832
** file UUID DELTASRC SIZE \n CONTENT
766833
**
@@ -811,27 +878,34 @@
811878
&& xfer.nToken==2
812879
&& blob_is_uuid(&xfer.aToken[1])
813880
){
814881
nGimme++;
815882
if( isPull ){
816
- int rid = rid_from_uuid(&xfer.aToken[1], 0);
883
+ int rid = rid_from_uuid(&xfer.aToken[1], 0, 0);
817884
if( rid ){
818885
send_file(&xfer, rid, &xfer.aToken[1], deltaFlag);
819886
}
820887
}
821888
}else
822889
823
- /* igot UUID
890
+ /* igot UUID ?ISPRIVATE?
824891
**
825
- ** Client announces that it has a particular file.
892
+ ** Client announces that it has a particular file. If the ISPRIVATE
893
+ ** argument exists and is non-zero, then the file is a private file.
826894
*/
827
- if( xfer.nToken==2
895
+ if( xfer.nToken>=2
828896
&& blob_eq(&xfer.aToken[0], "igot")
829897
&& blob_is_uuid(&xfer.aToken[1])
830898
){
831899
if( isPush ){
832
- rid_from_uuid(&xfer.aToken[1], 1);
900
+ if( xfer.nToken==2 || blob_eq(&xfer.aToken[2],"1")==0 ){
901
+ rid_from_uuid(&xfer.aToken[1], 1, 0);
902
+ }else if( g.okPrivate ){
903
+ rid_from_uuid(&xfer.aToken[1], 1, 1);
904
+ }else{
905
+ server_private_xfer_not_authorized();
906
+ }
833907
}
834908
}else
835909
836910
837911
/* pull SERVERCODE PROJECTCODE
@@ -929,11 +1003,11 @@
9291003
*/
9301004
if( blob_eq(&xfer.aToken[0], "login")
9311005
&& xfer.nToken==4
9321006
){
9331007
if( disableLogin ){
934
- g.okRead = g.okWrite = 1;
1008
+ g.okRead = g.okWrite = g.okPrivate = 1;
9351009
}else{
9361010
if( check_tail_hash(&xfer.aToken[2], xfer.pIn)
9371011
|| check_login(&xfer.aToken[1], &xfer.aToken[2], &xfer.aToken[3])
9381012
){
9391013
cgi_reset_content();
@@ -1029,10 +1103,48 @@
10291103
*/
10301104
if( blob_eq(&xfer.aToken[0], "cookie") && xfer.nToken==2 ){
10311105
/* Process the cookie */
10321106
}else
10331107
1108
+
1109
+ /* private
1110
+ **
1111
+ ** This card indicates that the next "file" or "cfile" will contain
1112
+ ** private content.
1113
+ */
1114
+ if( blob_eq(&xfer.aToken[0], "private") ){
1115
+ if( !g.okPrivate ){
1116
+ server_private_xfer_not_authorized();
1117
+ }else{
1118
+ xfer.nextIsPrivate = 1;
1119
+ }
1120
+ }else
1121
+
1122
+
1123
+ /* pragma NAME VALUE...
1124
+ **
1125
+ ** The client issue pragmas to try to influence the behavior of the
1126
+ ** server. These are requests only. Unknown pragmas are silently
1127
+ ** ignored.
1128
+ */
1129
+ if( blob_eq(&xfer.aToken[0], "pragma") && xfer.nToken>=2 ){
1130
+ /* pragma send-private
1131
+ **
1132
+ ** If the user has the "x" privilege (which must be set explicitly -
1133
+ ** it is not automatic with "a" or "s") then this pragma causes
1134
+ ** private information to be pulled in addition to public records.
1135
+ */
1136
+ if( blob_eq(&xfer.aToken[1], "send-private") ){
1137
+ login_check_credentials();
1138
+ if( !g.okPrivate ){
1139
+ server_private_xfer_not_authorized();
1140
+ }else{
1141
+ xfer.syncPrivate = 1;
1142
+ }
1143
+ }
1144
+ }else
1145
+
10341146
/* Unknown message
10351147
*/
10361148
{
10371149
cgi_reset_content();
10381150
@ error bad\scommand:\s%F(blob_str(&xfer.line))
@@ -1049,13 +1161,15 @@
10491161
** cause the client to create phantoms for all artifacts, which will
10501162
** in turn make sure that the entire repository is sent efficiently
10511163
** and expeditiously.
10521164
*/
10531165
send_all(&xfer);
1166
+ if( xfer.syncPrivate ) send_private(&xfer);
10541167
}else if( isPull ){
10551168
create_cluster();
10561169
send_unclustered(&xfer);
1170
+ if( xfer.syncPrivate ) send_private(&xfer);
10571171
}
10581172
if( recvConfig ){
10591173
configure_finalize_receive();
10601174
}
10611175
manifest_crosslink_end();
@@ -1120,10 +1234,11 @@
11201234
*/
11211235
int client_sync(
11221236
int pushFlag, /* True to do a push (or a sync) */
11231237
int pullFlag, /* True to do a pull (or a sync) */
11241238
int cloneFlag, /* True if this is a clone */
1239
+ int privateFlag, /* True to exchange private branches */
11251240
int configRcvMask, /* Receive these configuration items */
11261241
int configSendMask /* Send these configuration items */
11271242
){
11281243
int go = 1; /* Loop until zero */
11291244
int nCardSent = 0; /* Number of cards sent */
@@ -1155,10 +1270,14 @@
11551270
socket_global_init();
11561271
memset(&xfer, 0, sizeof(xfer));
11571272
xfer.pIn = &recv;
11581273
xfer.pOut = &send;
11591274
xfer.mxSend = db_get_int("max-upload", 250000);
1275
+ if( privateFlag ){
1276
+ g.okPrivate = 1;
1277
+ xfer.syncPrivate = 1;
1278
+ }
11601279
11611280
assert( pushFlag | pullFlag | cloneFlag | configRcvMask | configSendMask );
11621281
db_begin_transaction();
11631282
db_record_repository_filename(0);
11641283
db_multi_exec(
@@ -1169,10 +1288,14 @@
11691288
blob_zero(&recv);
11701289
blob_zero(&xfer.err);
11711290
blob_zero(&xfer.line);
11721291
origConfigRcvMask = 0;
11731292
1293
+
1294
+ /* Send the send-private pragma if we are trying to sync private data */
1295
+ if( privateFlag ) blob_append(&send, "pragma send-private\n", -1);
1296
+
11741297
/*
11751298
** Always begin with a clone, pull, or push message
11761299
*/
11771300
if( cloneFlag ){
11781301
blob_appendf(&send, "clone 3 %d\n", cloneSeqno);
@@ -1212,10 +1335,11 @@
12121335
request_phantoms(&xfer, mxPhantomReq);
12131336
}
12141337
if( pushFlag ){
12151338
send_unsent(&xfer);
12161339
nCardSent += send_unclustered(&xfer);
1340
+ if( privateFlag ) send_private(&xfer);
12171341
}
12181342
12191343
/* Send configuration parameter requests. On a clone, delay sending
12201344
** this until the second cycle since the login card might fail on
12211345
** the first cycle.
@@ -1275,10 +1399,13 @@
12751399
break;
12761400
}
12771401
lastPctDone = -1;
12781402
blob_reset(&send);
12791403
rArrivalTime = db_double(0.0, "SELECT julianday('now')");
1404
+
1405
+ /* Send the send-private pragma if we are trying to sync private data */
1406
+ if( privateFlag ) blob_append(&send, "pragma send-private\n", -1);
12801407
12811408
/* Begin constructing the next message (which might never be
12821409
** sent) by beginning with the pull or push cards
12831410
*/
12841411
if( pullFlag ){
@@ -1349,32 +1476,40 @@
13491476
if( blob_eq(&xfer.aToken[0], "gimme")
13501477
&& xfer.nToken==2
13511478
&& blob_is_uuid(&xfer.aToken[1])
13521479
){
13531480
if( pushFlag ){
1354
- int rid = rid_from_uuid(&xfer.aToken[1], 0);
1481
+ int rid = rid_from_uuid(&xfer.aToken[1], 0, 0);
13551482
if( rid ) send_file(&xfer, rid, &xfer.aToken[1], 0);
13561483
}
13571484
}else
13581485
1359
- /* igot UUID
1486
+ /* igot UUID ?PRIVATEFLAG?
13601487
**
13611488
** Server announces that it has a particular file. If this is
13621489
** not a file that we have and we are pulling, then create a
13631490
** phantom to cause this file to be requested on the next cycle.
13641491
** Always remember that the server has this file so that we do
13651492
** not transmit it by accident.
1493
+ **
1494
+ ** If the PRIVATE argument exists and is 1, then the file is
1495
+ ** private. Pretend it does not exists if we are not pulling
1496
+ ** private files.
13661497
*/
1367
- if( xfer.nToken==2
1498
+ if( xfer.nToken>=2
13681499
&& blob_eq(&xfer.aToken[0], "igot")
13691500
&& blob_is_uuid(&xfer.aToken[1])
13701501
){
1371
- int rid = rid_from_uuid(&xfer.aToken[1], 0);
1502
+ int rid;
1503
+ int isPriv = xfer.nToken>=3 && blob_eq(&xfer.aToken[2],"1");
1504
+ rid = rid_from_uuid(&xfer.aToken[1], 0, 0);
13721505
if( rid>0 ){
1373
- content_make_public(rid);
1506
+ if( !isPriv ) content_make_public(rid);
1507
+ }else if( isPriv && !g.okPrivate ){
1508
+ /* ignore private files */
13741509
}else if( pullFlag || cloneFlag ){
1375
- rid = content_new(blob_str(&xfer.aToken[1]), 0);
1510
+ rid = content_new(blob_str(&xfer.aToken[1]), isPriv);
13761511
if( rid ) newPhantom = 1;
13771512
}
13781513
remote_has(rid);
13791514
}else
13801515
@@ -1454,10 +1589,21 @@
14541589
** same server.
14551590
*/
14561591
if( blob_eq(&xfer.aToken[0], "cookie") && xfer.nToken==2 ){
14571592
db_set("cookie", blob_str(&xfer.aToken[1]), 0);
14581593
}else
1594
+
1595
+
1596
+ /* private
1597
+ **
1598
+ ** This card indicates that the next "file" or "cfile" will contain
1599
+ ** private content.
1600
+ */
1601
+ if( blob_eq(&xfer.aToken[0], "private") ){
1602
+ xfer.nextIsPrivate = 1;
1603
+ }else
1604
+
14591605
14601606
/* clone_seqno N
14611607
**
14621608
** When doing a clone, the server tries to send all of its artifacts
14631609
** in sequence. This card indicates the sequence number of the next
14641610
--- src/xfer.c
+++ src/xfer.c
@@ -38,10 +38,12 @@
38 int nDeltaSent; /* Number of deltas sent */
39 int nFileRcvd; /* Number of files received */
40 int nDeltaRcvd; /* Number of deltas received */
41 int nDanglingFile; /* Number of dangling deltas received */
42 int mxSend; /* Stop sending "file" with pOut reaches this size */
 
 
43 };
44
45
46 /*
47 ** The input blob contains a UUID. Convert it into a record ID.
@@ -49,11 +51,11 @@
49 ** phantomize is true.
50 **
51 ** Compare to uuid_to_rid(). This routine takes a blob argument
52 ** and does less error checking.
53 */
54 static int rid_from_uuid(Blob *pUuid, int phantomize){
55 static Stmt q;
56 int rid;
57
58 db_static_prepare(&q, "SELECT rid FROM blob WHERE uuid=:uuid");
59 db_bind_str(&q, ":uuid", pUuid);
@@ -62,11 +64,11 @@
62 }else{
63 rid = 0;
64 }
65 db_reset(&q);
66 if( rid==0 && phantomize ){
67 rid = content_new(blob_str(pUuid), 0);
68 }
69 return rid;
70 }
71
72 /*
@@ -106,11 +108,14 @@
106 static void xfer_accept_file(Xfer *pXfer, int cloneFlag){
107 int n;
108 int rid;
109 int srcid = 0;
110 Blob content, hash;
 
111
 
 
112 if( pXfer->nToken<3
113 || pXfer->nToken>4
114 || !blob_is_uuid(&pXfer->aToken[1])
115 || !blob_is_int(&pXfer->aToken[pXfer->nToken-1], &n)
116 || n<0
@@ -123,32 +128,38 @@
123 blob_zero(&hash);
124 blob_extract(pXfer->pIn, n, &content);
125 if( !cloneFlag && uuid_is_shunned(blob_str(&pXfer->aToken[1])) ){
126 /* Ignore files that have been shunned */
127 return;
 
 
 
 
128 }
129 if( cloneFlag ){
130 if( pXfer->nToken==4 ){
131 srcid = rid_from_uuid(&pXfer->aToken[2], 1);
132 pXfer->nDeltaRcvd++;
133 }else{
134 srcid = 0;
135 pXfer->nFileRcvd++;
136 }
137 rid = content_put_ex(&content, blob_str(&pXfer->aToken[1]), srcid, 0, 0);
 
138 remote_has(rid);
139 blob_reset(&content);
140 return;
141 }
142 if( pXfer->nToken==4 ){
143 Blob src, next;
144 srcid = rid_from_uuid(&pXfer->aToken[2], 1);
145 if( content_get(srcid, &src)==0 ){
146 rid = content_put_ex(&content, blob_str(&pXfer->aToken[1]), srcid, 0, 0);
 
147 pXfer->nDanglingFile++;
148 db_multi_exec("DELETE FROM phantom WHERE rid=%d", rid);
149 content_make_public(rid);
150 return;
151 }
152 pXfer->nDeltaRcvd++;
153 blob_delta_apply(&src, &content, &next);
154 blob_reset(&src);
@@ -159,17 +170,17 @@
159 }
160 sha1sum_blob(&content, &hash);
161 if( !blob_eq_str(&pXfer->aToken[1], blob_str(&hash), -1) ){
162 blob_appendf(&pXfer->err, "content does not match sha1 hash");
163 }
164 rid = content_put_ex(&content, blob_str(&hash), 0, 0, 0);
165 blob_reset(&hash);
166 if( rid==0 ){
167 blob_appendf(&pXfer->err, "%s", g.zErrMsg);
168 blob_reset(&content);
169 }else{
170 content_make_public(rid);
171 manifest_crosslink(rid, &content);
172 }
173 assert( blob_is_reset(&content) );
174 remote_has(rid);
175 }
@@ -201,11 +212,14 @@
201 int szC; /* CSIZE */
202 int szU; /* USIZE */
203 int rid;
204 int srcid = 0;
205 Blob content;
 
206
 
 
207 if( pXfer->nToken<4
208 || pXfer->nToken>5
209 || !blob_is_uuid(&pXfer->aToken[1])
210 || !blob_is_int(&pXfer->aToken[pXfer->nToken-2], &szU)
211 || !blob_is_int(&pXfer->aToken[pXfer->nToken-1], &szC)
@@ -212,25 +226,30 @@
212 || szC<0 || szU<0
213 || (pXfer->nToken==5 && !blob_is_uuid(&pXfer->aToken[2]))
214 ){
215 blob_appendf(&pXfer->err, "malformed cfile line");
216 return;
 
 
 
 
217 }
218 blob_zero(&content);
219 blob_extract(pXfer->pIn, szC, &content);
220 if( uuid_is_shunned(blob_str(&pXfer->aToken[1])) ){
221 /* Ignore files that have been shunned */
222 return;
223 }
224 if( pXfer->nToken==5 ){
225 srcid = rid_from_uuid(&pXfer->aToken[2], 1);
226 pXfer->nDeltaRcvd++;
227 }else{
228 srcid = 0;
229 pXfer->nFileRcvd++;
230 }
231 rid = content_put_ex(&content, blob_str(&pXfer->aToken[1]), srcid, szC, 0);
 
232 remote_has(rid);
233 blob_reset(&content);
234 }
235
236 /*
@@ -242,10 +261,11 @@
242 ** Never send a delta against a private artifact.
243 */
244 static int send_delta_parent(
245 Xfer *pXfer, /* The transfer context */
246 int rid, /* record id of the file to send */
 
247 Blob *pContent, /* The content of the file to send */
248 Blob *pUuid /* The UUID of the file to send */
249 ){
250 static const char *azQuery[] = {
251 "SELECT pid FROM plink x"
@@ -266,22 +286,25 @@
266 int srcId = 0;
267
268 for(i=0; srcId==0 && i<count(azQuery); i++){
269 srcId = db_int(0, azQuery[i], rid);
270 }
271 if( srcId>0 && !content_is_private(srcId) && content_get(srcId, &src) ){
 
 
 
272 char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", srcId);
273 blob_delta_create(&src, pContent, &delta);
274 size = blob_size(&delta);
275 if( size>=blob_size(pContent)-50 ){
276 size = 0;
277 }else if( uuid_is_shunned(zUuid) ){
278 size = 0;
279 }else{
 
280 blob_appendf(pXfer->pOut, "file %b %s %d\n", pUuid, zUuid, size);
281 blob_append(pXfer->pOut, blob_buffer(&delta), size);
282 /* blob_appendf(pXfer->pOut, "\n", 1); */
283 }
284 blob_reset(&delta);
285 free(zUuid);
286 blob_reset(&src);
287 }
@@ -297,27 +320,31 @@
297 ** Never send a delta against a private artifact.
298 */
299 static int send_delta_native(
300 Xfer *pXfer, /* The transfer context */
301 int rid, /* record id of the file to send */
 
302 Blob *pUuid /* The UUID of the file to send */
303 ){
304 Blob src, delta;
305 int size = 0;
306 int srcId;
307
308 srcId = db_int(0, "SELECT srcid FROM delta WHERE rid=%d", rid);
309 if( srcId>0 && !content_is_private(srcId) ){
 
 
310 blob_zero(&src);
311 db_blob(&src, "SELECT uuid FROM blob WHERE rid=%d", srcId);
312 if( uuid_is_shunned(blob_str(&src)) ){
313 blob_reset(&src);
314 return 0;
315 }
316 blob_zero(&delta);
317 db_blob(&delta, "SELECT content FROM blob WHERE rid=%d", rid);
318 blob_uncompress(&delta, &delta);
 
319 blob_appendf(pXfer->pOut, "file %b %b %d\n",
320 pUuid, &src, blob_size(&delta));
321 blob_append(pXfer->pOut, blob_buffer(&delta), blob_size(&delta));
322 size = blob_size(&delta);
323 blob_reset(&delta);
@@ -342,12 +369,13 @@
342 ** this routine becomes a no-op.
343 */
344 static void send_file(Xfer *pXfer, int rid, Blob *pUuid, int nativeDelta){
345 Blob content, uuid;
346 int size = 0;
 
347
348 if( content_is_private(rid) ) return;
349 if( db_exists("SELECT 1 FROM onremote WHERE rid=%d", rid) ){
350 return;
351 }
352 blob_zero(&uuid);
353 db_blob(&uuid, "SELECT uuid FROM blob WHERE rid=%d AND size>=0", rid);
@@ -365,38 +393,45 @@
365 if( uuid_is_shunned(blob_str(pUuid)) ){
366 blob_reset(&uuid);
367 return;
368 }
369 if( pXfer->mxSend<=blob_size(pXfer->pOut) ){
370 blob_appendf(pXfer->pOut, "igot %b\n", pUuid);
 
371 pXfer->nIGotSent++;
372 blob_reset(&uuid);
373 return;
374 }
375 if( nativeDelta ){
376 size = send_delta_native(pXfer, rid, pUuid);
377 if( size ){
378 pXfer->nDeltaSent++;
379 }
380 }
381 if( size==0 ){
382 content_get(rid, &content);
383
384 if( !nativeDelta && blob_size(&content)>100 ){
385 size = send_delta_parent(pXfer, rid, &content, pUuid);
386 }
387 if( size==0 ){
388 int size = blob_size(&content);
 
389 blob_appendf(pXfer->pOut, "file %b %d\n", pUuid, size);
390 blob_append(pXfer->pOut, blob_buffer(&content), size);
391 pXfer->nFileSent++;
392 }else{
393 pXfer->nDeltaSent++;
394 }
395 }
396 remote_has(rid);
397 blob_reset(&uuid);
 
 
 
 
 
398 }
399
400 /*
401 ** Send the file identified by rid as a compressed artifact. Basically,
402 ** send the content exactly as it appears in the BLOB table using
@@ -407,57 +442,62 @@
407 const char *zUuid;
408 const char *zDelta;
409 int szU;
410 int szC;
411 int rc;
 
412 static Stmt q1;
413
 
 
414 db_static_prepare(&q1,
415 "SELECT uuid, size, content,"
416 " (SELECT uuid FROM delta, blob"
417 " WHERE delta.rid=:rid AND delta.srcid=blob.rid)"
418 " FROM blob"
419 " WHERE rid=:rid"
420 " AND size>=0"
421 " AND uuid NOT IN shun"
422 " AND rid NOT IN private",
423 rid
424 );
425 db_bind_int(&q1, ":rid", rid);
426 rc = db_step(&q1);
427 if( rc==SQLITE_ROW ){
428 zUuid = db_column_text(&q1, 0);
429 szU = db_column_int(&q1, 1);
430 szC = db_column_bytes(&q1, 2);
431 zContent = db_column_raw(&q1, 2);
432 zDelta = db_column_text(&q1, 3);
 
433 blob_appendf(pXfer->pOut, "cfile %s ", zUuid);
434 if( zDelta ){
435 blob_appendf(pXfer->pOut, "%s ", zDelta);
436 pXfer->nDeltaSent++;
437 }else{
438 pXfer->nFileSent++;
439 }
440 blob_appendf(pXfer->pOut, "%d %d\n", szU, szC);
441 blob_append(pXfer->pOut, zContent, szC);
442 blob_append(pXfer->pOut, "\n", 1);
 
 
443 }
444 db_reset(&q1);
445 }
446
447 /*
448 ** Send a gimme message for every phantom.
449 **
450 ** It should not be possible to have a private phantom. But just to be
451 ** sure, take care not to send any "gimme" messagse on private artifacts.
452 */
453 static void request_phantoms(Xfer *pXfer, int maxReq){
454 Stmt q;
455 db_prepare(&q,
456 "SELECT uuid FROM phantom JOIN blob USING(rid)"
457 " WHERE NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid)"
458 " AND NOT EXISTS(SELECT 1 FROM private WHERE rid=blob.rid)"
 
459 );
460 while( db_step(&q)==SQLITE_ROW && maxReq-- > 0 ){
461 const char *zUuid = db_column_text(&q, 0);
462 blob_appendf(pXfer->pOut, "gimme %s\n", zUuid);
463 pXfer->nGimmeSent++;
@@ -641,10 +681,27 @@
641 content_put(&cluster);
642 blob_reset(&cluster);
643 }
644 }
645 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
646
647 /*
648 ** Send an igot message for every entry in unclustered table.
649 ** Return the number of cards sent.
650 */
@@ -652,12 +709,12 @@
652 Stmt q;
653 int cnt = 0;
654 db_prepare(&q,
655 "SELECT uuid FROM unclustered JOIN blob USING(rid)"
656 " WHERE NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid)"
657 " AND NOT EXISTS(SELECT 1 FROM private WHERE rid=blob.rid)"
658 " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=blob.rid)"
 
659 );
660 while( db_step(&q)==SQLITE_ROW ){
661 blob_appendf(pXfer->pOut, "igot %s\n", db_column_text(&q, 0));
662 cnt++;
663 }
@@ -704,10 +761,19 @@
704 blob_size(&content), blob_str(&content));
705 blob_reset(&content);
706 }
707 }
708
 
 
 
 
 
 
 
 
 
709
710 /*
711 ** If this variable is set, disable login checks. Used for debugging
712 ** only.
713 */
@@ -757,10 +823,11 @@
757 "CREATE TEMP TABLE onremote(rid INTEGER PRIMARY KEY);"
758 );
759 manifest_crosslink_begin();
760 while( blob_line(xfer.pIn, &xfer.line) ){
761 if( blob_buffer(&xfer.line)[0]=='#' ) continue;
 
762 xfer.nToken = blob_tokenize(&xfer.line, xfer.aToken, count(xfer.aToken));
763
764 /* file UUID SIZE \n CONTENT
765 ** file UUID DELTASRC SIZE \n CONTENT
766 **
@@ -811,27 +878,34 @@
811 && xfer.nToken==2
812 && blob_is_uuid(&xfer.aToken[1])
813 ){
814 nGimme++;
815 if( isPull ){
816 int rid = rid_from_uuid(&xfer.aToken[1], 0);
817 if( rid ){
818 send_file(&xfer, rid, &xfer.aToken[1], deltaFlag);
819 }
820 }
821 }else
822
823 /* igot UUID
824 **
825 ** Client announces that it has a particular file.
 
826 */
827 if( xfer.nToken==2
828 && blob_eq(&xfer.aToken[0], "igot")
829 && blob_is_uuid(&xfer.aToken[1])
830 ){
831 if( isPush ){
832 rid_from_uuid(&xfer.aToken[1], 1);
 
 
 
 
 
 
833 }
834 }else
835
836
837 /* pull SERVERCODE PROJECTCODE
@@ -929,11 +1003,11 @@
929 */
930 if( blob_eq(&xfer.aToken[0], "login")
931 && xfer.nToken==4
932 ){
933 if( disableLogin ){
934 g.okRead = g.okWrite = 1;
935 }else{
936 if( check_tail_hash(&xfer.aToken[2], xfer.pIn)
937 || check_login(&xfer.aToken[1], &xfer.aToken[2], &xfer.aToken[3])
938 ){
939 cgi_reset_content();
@@ -1029,10 +1103,48 @@
1029 */
1030 if( blob_eq(&xfer.aToken[0], "cookie") && xfer.nToken==2 ){
1031 /* Process the cookie */
1032 }else
1033
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1034 /* Unknown message
1035 */
1036 {
1037 cgi_reset_content();
1038 @ error bad\scommand:\s%F(blob_str(&xfer.line))
@@ -1049,13 +1161,15 @@
1049 ** cause the client to create phantoms for all artifacts, which will
1050 ** in turn make sure that the entire repository is sent efficiently
1051 ** and expeditiously.
1052 */
1053 send_all(&xfer);
 
1054 }else if( isPull ){
1055 create_cluster();
1056 send_unclustered(&xfer);
 
1057 }
1058 if( recvConfig ){
1059 configure_finalize_receive();
1060 }
1061 manifest_crosslink_end();
@@ -1120,10 +1234,11 @@
1120 */
1121 int client_sync(
1122 int pushFlag, /* True to do a push (or a sync) */
1123 int pullFlag, /* True to do a pull (or a sync) */
1124 int cloneFlag, /* True if this is a clone */
 
1125 int configRcvMask, /* Receive these configuration items */
1126 int configSendMask /* Send these configuration items */
1127 ){
1128 int go = 1; /* Loop until zero */
1129 int nCardSent = 0; /* Number of cards sent */
@@ -1155,10 +1270,14 @@
1155 socket_global_init();
1156 memset(&xfer, 0, sizeof(xfer));
1157 xfer.pIn = &recv;
1158 xfer.pOut = &send;
1159 xfer.mxSend = db_get_int("max-upload", 250000);
 
 
 
 
1160
1161 assert( pushFlag | pullFlag | cloneFlag | configRcvMask | configSendMask );
1162 db_begin_transaction();
1163 db_record_repository_filename(0);
1164 db_multi_exec(
@@ -1169,10 +1288,14 @@
1169 blob_zero(&recv);
1170 blob_zero(&xfer.err);
1171 blob_zero(&xfer.line);
1172 origConfigRcvMask = 0;
1173
 
 
 
 
1174 /*
1175 ** Always begin with a clone, pull, or push message
1176 */
1177 if( cloneFlag ){
1178 blob_appendf(&send, "clone 3 %d\n", cloneSeqno);
@@ -1212,10 +1335,11 @@
1212 request_phantoms(&xfer, mxPhantomReq);
1213 }
1214 if( pushFlag ){
1215 send_unsent(&xfer);
1216 nCardSent += send_unclustered(&xfer);
 
1217 }
1218
1219 /* Send configuration parameter requests. On a clone, delay sending
1220 ** this until the second cycle since the login card might fail on
1221 ** the first cycle.
@@ -1275,10 +1399,13 @@
1275 break;
1276 }
1277 lastPctDone = -1;
1278 blob_reset(&send);
1279 rArrivalTime = db_double(0.0, "SELECT julianday('now')");
 
 
 
1280
1281 /* Begin constructing the next message (which might never be
1282 ** sent) by beginning with the pull or push cards
1283 */
1284 if( pullFlag ){
@@ -1349,32 +1476,40 @@
1349 if( blob_eq(&xfer.aToken[0], "gimme")
1350 && xfer.nToken==2
1351 && blob_is_uuid(&xfer.aToken[1])
1352 ){
1353 if( pushFlag ){
1354 int rid = rid_from_uuid(&xfer.aToken[1], 0);
1355 if( rid ) send_file(&xfer, rid, &xfer.aToken[1], 0);
1356 }
1357 }else
1358
1359 /* igot UUID
1360 **
1361 ** Server announces that it has a particular file. If this is
1362 ** not a file that we have and we are pulling, then create a
1363 ** phantom to cause this file to be requested on the next cycle.
1364 ** Always remember that the server has this file so that we do
1365 ** not transmit it by accident.
 
 
 
 
1366 */
1367 if( xfer.nToken==2
1368 && blob_eq(&xfer.aToken[0], "igot")
1369 && blob_is_uuid(&xfer.aToken[1])
1370 ){
1371 int rid = rid_from_uuid(&xfer.aToken[1], 0);
 
 
1372 if( rid>0 ){
1373 content_make_public(rid);
 
 
1374 }else if( pullFlag || cloneFlag ){
1375 rid = content_new(blob_str(&xfer.aToken[1]), 0);
1376 if( rid ) newPhantom = 1;
1377 }
1378 remote_has(rid);
1379 }else
1380
@@ -1454,10 +1589,21 @@
1454 ** same server.
1455 */
1456 if( blob_eq(&xfer.aToken[0], "cookie") && xfer.nToken==2 ){
1457 db_set("cookie", blob_str(&xfer.aToken[1]), 0);
1458 }else
 
 
 
 
 
 
 
 
 
 
 
1459
1460 /* clone_seqno N
1461 **
1462 ** When doing a clone, the server tries to send all of its artifacts
1463 ** in sequence. This card indicates the sequence number of the next
1464
--- src/xfer.c
+++ src/xfer.c
@@ -38,10 +38,12 @@
38 int nDeltaSent; /* Number of deltas sent */
39 int nFileRcvd; /* Number of files received */
40 int nDeltaRcvd; /* Number of deltas received */
41 int nDanglingFile; /* Number of dangling deltas received */
42 int mxSend; /* Stop sending "file" with pOut reaches this size */
43 u8 syncPrivate; /* True to enable syncing private content */
44 u8 nextIsPrivate; /* If true, next "file" received is a private */
45 };
46
47
48 /*
49 ** The input blob contains a UUID. Convert it into a record ID.
@@ -49,11 +51,11 @@
51 ** phantomize is true.
52 **
53 ** Compare to uuid_to_rid(). This routine takes a blob argument
54 ** and does less error checking.
55 */
56 static int rid_from_uuid(Blob *pUuid, int phantomize, int isPrivate){
57 static Stmt q;
58 int rid;
59
60 db_static_prepare(&q, "SELECT rid FROM blob WHERE uuid=:uuid");
61 db_bind_str(&q, ":uuid", pUuid);
@@ -62,11 +64,11 @@
64 }else{
65 rid = 0;
66 }
67 db_reset(&q);
68 if( rid==0 && phantomize ){
69 rid = content_new(blob_str(pUuid), isPrivate);
70 }
71 return rid;
72 }
73
74 /*
@@ -106,11 +108,14 @@
108 static void xfer_accept_file(Xfer *pXfer, int cloneFlag){
109 int n;
110 int rid;
111 int srcid = 0;
112 Blob content, hash;
113 int isPriv;
114
115 isPriv = pXfer->nextIsPrivate;
116 pXfer->nextIsPrivate = 0;
117 if( pXfer->nToken<3
118 || pXfer->nToken>4
119 || !blob_is_uuid(&pXfer->aToken[1])
120 || !blob_is_int(&pXfer->aToken[pXfer->nToken-1], &n)
121 || n<0
@@ -123,32 +128,38 @@
128 blob_zero(&hash);
129 blob_extract(pXfer->pIn, n, &content);
130 if( !cloneFlag && uuid_is_shunned(blob_str(&pXfer->aToken[1])) ){
131 /* Ignore files that have been shunned */
132 return;
133 }
134 if( isPriv && !g.okPrivate ){
135 /* Do not accept private files if not authorized */
136 return;
137 }
138 if( cloneFlag ){
139 if( pXfer->nToken==4 ){
140 srcid = rid_from_uuid(&pXfer->aToken[2], 1, isPriv);
141 pXfer->nDeltaRcvd++;
142 }else{
143 srcid = 0;
144 pXfer->nFileRcvd++;
145 }
146 rid = content_put_ex(&content, blob_str(&pXfer->aToken[1]), srcid,
147 0, isPriv);
148 remote_has(rid);
149 blob_reset(&content);
150 return;
151 }
152 if( pXfer->nToken==4 ){
153 Blob src, next;
154 srcid = rid_from_uuid(&pXfer->aToken[2], 1, isPriv);
155 if( content_get(srcid, &src)==0 ){
156 rid = content_put_ex(&content, blob_str(&pXfer->aToken[1]), srcid,
157 0, isPriv);
158 pXfer->nDanglingFile++;
159 db_multi_exec("DELETE FROM phantom WHERE rid=%d", rid);
160 if( !isPriv ) content_make_public(rid);
161 return;
162 }
163 pXfer->nDeltaRcvd++;
164 blob_delta_apply(&src, &content, &next);
165 blob_reset(&src);
@@ -159,17 +170,17 @@
170 }
171 sha1sum_blob(&content, &hash);
172 if( !blob_eq_str(&pXfer->aToken[1], blob_str(&hash), -1) ){
173 blob_appendf(&pXfer->err, "content does not match sha1 hash");
174 }
175 rid = content_put_ex(&content, blob_str(&hash), 0, 0, isPriv);
176 blob_reset(&hash);
177 if( rid==0 ){
178 blob_appendf(&pXfer->err, "%s", g.zErrMsg);
179 blob_reset(&content);
180 }else{
181 if( !isPriv ) content_make_public(rid);
182 manifest_crosslink(rid, &content);
183 }
184 assert( blob_is_reset(&content) );
185 remote_has(rid);
186 }
@@ -201,11 +212,14 @@
212 int szC; /* CSIZE */
213 int szU; /* USIZE */
214 int rid;
215 int srcid = 0;
216 Blob content;
217 int isPriv;
218
219 isPriv = pXfer->nextIsPrivate;
220 pXfer->nextIsPrivate = 0;
221 if( pXfer->nToken<4
222 || pXfer->nToken>5
223 || !blob_is_uuid(&pXfer->aToken[1])
224 || !blob_is_int(&pXfer->aToken[pXfer->nToken-2], &szU)
225 || !blob_is_int(&pXfer->aToken[pXfer->nToken-1], &szC)
@@ -212,25 +226,30 @@
226 || szC<0 || szU<0
227 || (pXfer->nToken==5 && !blob_is_uuid(&pXfer->aToken[2]))
228 ){
229 blob_appendf(&pXfer->err, "malformed cfile line");
230 return;
231 }
232 if( isPriv && !g.okPrivate ){
233 /* Do not accept private files if not authorized */
234 return;
235 }
236 blob_zero(&content);
237 blob_extract(pXfer->pIn, szC, &content);
238 if( uuid_is_shunned(blob_str(&pXfer->aToken[1])) ){
239 /* Ignore files that have been shunned */
240 return;
241 }
242 if( pXfer->nToken==5 ){
243 srcid = rid_from_uuid(&pXfer->aToken[2], 1, isPriv);
244 pXfer->nDeltaRcvd++;
245 }else{
246 srcid = 0;
247 pXfer->nFileRcvd++;
248 }
249 rid = content_put_ex(&content, blob_str(&pXfer->aToken[1]), srcid,
250 szC, isPriv);
251 remote_has(rid);
252 blob_reset(&content);
253 }
254
255 /*
@@ -242,10 +261,11 @@
261 ** Never send a delta against a private artifact.
262 */
263 static int send_delta_parent(
264 Xfer *pXfer, /* The transfer context */
265 int rid, /* record id of the file to send */
266 int isPrivate, /* True if rid is a private artifact */
267 Blob *pContent, /* The content of the file to send */
268 Blob *pUuid /* The UUID of the file to send */
269 ){
270 static const char *azQuery[] = {
271 "SELECT pid FROM plink x"
@@ -266,22 +286,25 @@
286 int srcId = 0;
287
288 for(i=0; srcId==0 && i<count(azQuery); i++){
289 srcId = db_int(0, azQuery[i], rid);
290 }
291 if( srcId>0
292 && (pXfer->syncPrivate || !content_is_private(srcId))
293 && content_get(srcId, &src)
294 ){
295 char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", srcId);
296 blob_delta_create(&src, pContent, &delta);
297 size = blob_size(&delta);
298 if( size>=blob_size(pContent)-50 ){
299 size = 0;
300 }else if( uuid_is_shunned(zUuid) ){
301 size = 0;
302 }else{
303 if( isPrivate ) blob_append(pXfer->pOut, "private\n", -1);
304 blob_appendf(pXfer->pOut, "file %b %s %d\n", pUuid, zUuid, size);
305 blob_append(pXfer->pOut, blob_buffer(&delta), size);
 
306 }
307 blob_reset(&delta);
308 free(zUuid);
309 blob_reset(&src);
310 }
@@ -297,27 +320,31 @@
320 ** Never send a delta against a private artifact.
321 */
322 static int send_delta_native(
323 Xfer *pXfer, /* The transfer context */
324 int rid, /* record id of the file to send */
325 int isPrivate, /* True if rid is a private artifact */
326 Blob *pUuid /* The UUID of the file to send */
327 ){
328 Blob src, delta;
329 int size = 0;
330 int srcId;
331
332 srcId = db_int(0, "SELECT srcid FROM delta WHERE rid=%d", rid);
333 if( srcId>0
334 && (pXfer->syncPrivate || !content_is_private(srcId))
335 ){
336 blob_zero(&src);
337 db_blob(&src, "SELECT uuid FROM blob WHERE rid=%d", srcId);
338 if( uuid_is_shunned(blob_str(&src)) ){
339 blob_reset(&src);
340 return 0;
341 }
342 blob_zero(&delta);
343 db_blob(&delta, "SELECT content FROM blob WHERE rid=%d", rid);
344 blob_uncompress(&delta, &delta);
345 if( isPrivate ) blob_append(pXfer->pOut, "private\n", -1);
346 blob_appendf(pXfer->pOut, "file %b %b %d\n",
347 pUuid, &src, blob_size(&delta));
348 blob_append(pXfer->pOut, blob_buffer(&delta), blob_size(&delta));
349 size = blob_size(&delta);
350 blob_reset(&delta);
@@ -342,12 +369,13 @@
369 ** this routine becomes a no-op.
370 */
371 static void send_file(Xfer *pXfer, int rid, Blob *pUuid, int nativeDelta){
372 Blob content, uuid;
373 int size = 0;
374 int isPriv = content_is_private(rid);
375
376 if( pXfer->syncPrivate==0 && isPriv ) return;
377 if( db_exists("SELECT 1 FROM onremote WHERE rid=%d", rid) ){
378 return;
379 }
380 blob_zero(&uuid);
381 db_blob(&uuid, "SELECT uuid FROM blob WHERE rid=%d AND size>=0", rid);
@@ -365,38 +393,45 @@
393 if( uuid_is_shunned(blob_str(pUuid)) ){
394 blob_reset(&uuid);
395 return;
396 }
397 if( pXfer->mxSend<=blob_size(pXfer->pOut) ){
398 const char *zFormat = isPriv ? "igot %b 1\n" : "igot %b\n";
399 blob_appendf(pXfer->pOut, zFormat, pUuid);
400 pXfer->nIGotSent++;
401 blob_reset(&uuid);
402 return;
403 }
404 if( nativeDelta ){
405 size = send_delta_native(pXfer, rid, isPriv, pUuid);
406 if( size ){
407 pXfer->nDeltaSent++;
408 }
409 }
410 if( size==0 ){
411 content_get(rid, &content);
412
413 if( !nativeDelta && blob_size(&content)>100 ){
414 size = send_delta_parent(pXfer, rid, isPriv, &content, pUuid);
415 }
416 if( size==0 ){
417 int size = blob_size(&content);
418 if( isPriv ) blob_append(pXfer->pOut, "private\n", -1);
419 blob_appendf(pXfer->pOut, "file %b %d\n", pUuid, size);
420 blob_append(pXfer->pOut, blob_buffer(&content), size);
421 pXfer->nFileSent++;
422 }else{
423 pXfer->nDeltaSent++;
424 }
425 }
426 remote_has(rid);
427 blob_reset(&uuid);
428 #if 0
429 if( blob_buffer(pXfer->pOut)[blob_size(pXfer->pOut)-1]!='\n' ){
430 blob_appendf(pXfer->pOut, "\n", 1);
431 }
432 #endif
433 }
434
435 /*
436 ** Send the file identified by rid as a compressed artifact. Basically,
437 ** send the content exactly as it appears in the BLOB table using
@@ -407,57 +442,62 @@
442 const char *zUuid;
443 const char *zDelta;
444 int szU;
445 int szC;
446 int rc;
447 int isPrivate;
448 static Stmt q1;
449
450 isPrivate = content_is_private(rid);
451 if( isPrivate && pXfer->syncPrivate==0 ) return;
452 db_static_prepare(&q1,
453 "SELECT uuid, size, content,"
454 " (SELECT uuid FROM delta, blob"
455 " WHERE delta.rid=:rid AND delta.srcid=blob.rid)"
456 " FROM blob"
457 " WHERE rid=:rid"
458 " AND size>=0"
459 " AND uuid NOT IN shun"
 
 
460 );
461 db_bind_int(&q1, ":rid", rid);
462 rc = db_step(&q1);
463 if( rc==SQLITE_ROW ){
464 zUuid = db_column_text(&q1, 0);
465 szU = db_column_int(&q1, 1);
466 szC = db_column_bytes(&q1, 2);
467 zContent = db_column_raw(&q1, 2);
468 zDelta = db_column_text(&q1, 3);
469 if( isPrivate ) blob_append(pXfer->pOut, "private\n", -1);
470 blob_appendf(pXfer->pOut, "cfile %s ", zUuid);
471 if( zDelta ){
472 blob_appendf(pXfer->pOut, "%s ", zDelta);
473 pXfer->nDeltaSent++;
474 }else{
475 pXfer->nFileSent++;
476 }
477 blob_appendf(pXfer->pOut, "%d %d\n", szU, szC);
478 blob_append(pXfer->pOut, zContent, szC);
479 if( blob_buffer(pXfer->pOut)[blob_size(pXfer->pOut)-1]!='\n' ){
480 blob_appendf(pXfer->pOut, "\n", 1);
481 }
482 }
483 db_reset(&q1);
484 }
485
486 /*
487 ** Send a gimme message for every phantom.
488 **
489 ** Except: do not request shunned artifacts. And do not request
490 ** private artifacts if we are not doing a private transfer.
491 */
492 static void request_phantoms(Xfer *pXfer, int maxReq){
493 Stmt q;
494 db_prepare(&q,
495 "SELECT uuid FROM phantom JOIN blob USING(rid)"
496 " WHERE NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid) %s",
497 (pXfer->syncPrivate ? "" :
498 " AND NOT EXISTS(SELECT 1 FROM private WHERE rid=blob.rid)")
499 );
500 while( db_step(&q)==SQLITE_ROW && maxReq-- > 0 ){
501 const char *zUuid = db_column_text(&q, 0);
502 blob_appendf(pXfer->pOut, "gimme %s\n", zUuid);
503 pXfer->nGimmeSent++;
@@ -641,10 +681,27 @@
681 content_put(&cluster);
682 blob_reset(&cluster);
683 }
684 }
685 }
686
687 /*
688 ** Send igot messages for every private artifact
689 */
690 static int send_private(Xfer *pXfer){
691 int cnt = 0;
692 Stmt q;
693 if( pXfer->syncPrivate ){
694 db_prepare(&q, "SELECT uuid FROM private JOIN blob USING(rid)");
695 while( db_step(&q)==SQLITE_ROW ){
696 blob_appendf(pXfer->pOut, "igot %s 1\n", db_column_text(&q,0));
697 cnt++;
698 }
699 db_finalize(&q);
700 }
701 return cnt;
702 }
703
704 /*
705 ** Send an igot message for every entry in unclustered table.
706 ** Return the number of cards sent.
707 */
@@ -652,12 +709,12 @@
709 Stmt q;
710 int cnt = 0;
711 db_prepare(&q,
712 "SELECT uuid FROM unclustered JOIN blob USING(rid)"
713 " WHERE NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid)"
 
714 " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=blob.rid)"
715 " AND NOT EXISTS(SELECT 1 FROM private WHERE rid=blob.rid)"
716 );
717 while( db_step(&q)==SQLITE_ROW ){
718 blob_appendf(pXfer->pOut, "igot %s\n", db_column_text(&q, 0));
719 cnt++;
720 }
@@ -704,10 +761,19 @@
761 blob_size(&content), blob_str(&content));
762 blob_reset(&content);
763 }
764 }
765
766
767 /*
768 ** Called when there is an attempt to transfer private content to and
769 ** from a server without authorization.
770 */
771 static void server_private_xfer_not_authorized(void){
772 @ error not\sauthorized\sto\ssync\sprivate\scontent
773 }
774
775
776 /*
777 ** If this variable is set, disable login checks. Used for debugging
778 ** only.
779 */
@@ -757,10 +823,11 @@
823 "CREATE TEMP TABLE onremote(rid INTEGER PRIMARY KEY);"
824 );
825 manifest_crosslink_begin();
826 while( blob_line(xfer.pIn, &xfer.line) ){
827 if( blob_buffer(&xfer.line)[0]=='#' ) continue;
828 if( blob_size(&xfer.line)==0 ) continue;
829 xfer.nToken = blob_tokenize(&xfer.line, xfer.aToken, count(xfer.aToken));
830
831 /* file UUID SIZE \n CONTENT
832 ** file UUID DELTASRC SIZE \n CONTENT
833 **
@@ -811,27 +878,34 @@
878 && xfer.nToken==2
879 && blob_is_uuid(&xfer.aToken[1])
880 ){
881 nGimme++;
882 if( isPull ){
883 int rid = rid_from_uuid(&xfer.aToken[1], 0, 0);
884 if( rid ){
885 send_file(&xfer, rid, &xfer.aToken[1], deltaFlag);
886 }
887 }
888 }else
889
890 /* igot UUID ?ISPRIVATE?
891 **
892 ** Client announces that it has a particular file. If the ISPRIVATE
893 ** argument exists and is non-zero, then the file is a private file.
894 */
895 if( xfer.nToken>=2
896 && blob_eq(&xfer.aToken[0], "igot")
897 && blob_is_uuid(&xfer.aToken[1])
898 ){
899 if( isPush ){
900 if( xfer.nToken==2 || blob_eq(&xfer.aToken[2],"1")==0 ){
901 rid_from_uuid(&xfer.aToken[1], 1, 0);
902 }else if( g.okPrivate ){
903 rid_from_uuid(&xfer.aToken[1], 1, 1);
904 }else{
905 server_private_xfer_not_authorized();
906 }
907 }
908 }else
909
910
911 /* pull SERVERCODE PROJECTCODE
@@ -929,11 +1003,11 @@
1003 */
1004 if( blob_eq(&xfer.aToken[0], "login")
1005 && xfer.nToken==4
1006 ){
1007 if( disableLogin ){
1008 g.okRead = g.okWrite = g.okPrivate = 1;
1009 }else{
1010 if( check_tail_hash(&xfer.aToken[2], xfer.pIn)
1011 || check_login(&xfer.aToken[1], &xfer.aToken[2], &xfer.aToken[3])
1012 ){
1013 cgi_reset_content();
@@ -1029,10 +1103,48 @@
1103 */
1104 if( blob_eq(&xfer.aToken[0], "cookie") && xfer.nToken==2 ){
1105 /* Process the cookie */
1106 }else
1107
1108
1109 /* private
1110 **
1111 ** This card indicates that the next "file" or "cfile" will contain
1112 ** private content.
1113 */
1114 if( blob_eq(&xfer.aToken[0], "private") ){
1115 if( !g.okPrivate ){
1116 server_private_xfer_not_authorized();
1117 }else{
1118 xfer.nextIsPrivate = 1;
1119 }
1120 }else
1121
1122
1123 /* pragma NAME VALUE...
1124 **
1125 ** The client issue pragmas to try to influence the behavior of the
1126 ** server. These are requests only. Unknown pragmas are silently
1127 ** ignored.
1128 */
1129 if( blob_eq(&xfer.aToken[0], "pragma") && xfer.nToken>=2 ){
1130 /* pragma send-private
1131 **
1132 ** If the user has the "x" privilege (which must be set explicitly -
1133 ** it is not automatic with "a" or "s") then this pragma causes
1134 ** private information to be pulled in addition to public records.
1135 */
1136 if( blob_eq(&xfer.aToken[1], "send-private") ){
1137 login_check_credentials();
1138 if( !g.okPrivate ){
1139 server_private_xfer_not_authorized();
1140 }else{
1141 xfer.syncPrivate = 1;
1142 }
1143 }
1144 }else
1145
1146 /* Unknown message
1147 */
1148 {
1149 cgi_reset_content();
1150 @ error bad\scommand:\s%F(blob_str(&xfer.line))
@@ -1049,13 +1161,15 @@
1161 ** cause the client to create phantoms for all artifacts, which will
1162 ** in turn make sure that the entire repository is sent efficiently
1163 ** and expeditiously.
1164 */
1165 send_all(&xfer);
1166 if( xfer.syncPrivate ) send_private(&xfer);
1167 }else if( isPull ){
1168 create_cluster();
1169 send_unclustered(&xfer);
1170 if( xfer.syncPrivate ) send_private(&xfer);
1171 }
1172 if( recvConfig ){
1173 configure_finalize_receive();
1174 }
1175 manifest_crosslink_end();
@@ -1120,10 +1234,11 @@
1234 */
1235 int client_sync(
1236 int pushFlag, /* True to do a push (or a sync) */
1237 int pullFlag, /* True to do a pull (or a sync) */
1238 int cloneFlag, /* True if this is a clone */
1239 int privateFlag, /* True to exchange private branches */
1240 int configRcvMask, /* Receive these configuration items */
1241 int configSendMask /* Send these configuration items */
1242 ){
1243 int go = 1; /* Loop until zero */
1244 int nCardSent = 0; /* Number of cards sent */
@@ -1155,10 +1270,14 @@
1270 socket_global_init();
1271 memset(&xfer, 0, sizeof(xfer));
1272 xfer.pIn = &recv;
1273 xfer.pOut = &send;
1274 xfer.mxSend = db_get_int("max-upload", 250000);
1275 if( privateFlag ){
1276 g.okPrivate = 1;
1277 xfer.syncPrivate = 1;
1278 }
1279
1280 assert( pushFlag | pullFlag | cloneFlag | configRcvMask | configSendMask );
1281 db_begin_transaction();
1282 db_record_repository_filename(0);
1283 db_multi_exec(
@@ -1169,10 +1288,14 @@
1288 blob_zero(&recv);
1289 blob_zero(&xfer.err);
1290 blob_zero(&xfer.line);
1291 origConfigRcvMask = 0;
1292
1293
1294 /* Send the send-private pragma if we are trying to sync private data */
1295 if( privateFlag ) blob_append(&send, "pragma send-private\n", -1);
1296
1297 /*
1298 ** Always begin with a clone, pull, or push message
1299 */
1300 if( cloneFlag ){
1301 blob_appendf(&send, "clone 3 %d\n", cloneSeqno);
@@ -1212,10 +1335,11 @@
1335 request_phantoms(&xfer, mxPhantomReq);
1336 }
1337 if( pushFlag ){
1338 send_unsent(&xfer);
1339 nCardSent += send_unclustered(&xfer);
1340 if( privateFlag ) send_private(&xfer);
1341 }
1342
1343 /* Send configuration parameter requests. On a clone, delay sending
1344 ** this until the second cycle since the login card might fail on
1345 ** the first cycle.
@@ -1275,10 +1399,13 @@
1399 break;
1400 }
1401 lastPctDone = -1;
1402 blob_reset(&send);
1403 rArrivalTime = db_double(0.0, "SELECT julianday('now')");
1404
1405 /* Send the send-private pragma if we are trying to sync private data */
1406 if( privateFlag ) blob_append(&send, "pragma send-private\n", -1);
1407
1408 /* Begin constructing the next message (which might never be
1409 ** sent) by beginning with the pull or push cards
1410 */
1411 if( pullFlag ){
@@ -1349,32 +1476,40 @@
1476 if( blob_eq(&xfer.aToken[0], "gimme")
1477 && xfer.nToken==2
1478 && blob_is_uuid(&xfer.aToken[1])
1479 ){
1480 if( pushFlag ){
1481 int rid = rid_from_uuid(&xfer.aToken[1], 0, 0);
1482 if( rid ) send_file(&xfer, rid, &xfer.aToken[1], 0);
1483 }
1484 }else
1485
1486 /* igot UUID ?PRIVATEFLAG?
1487 **
1488 ** Server announces that it has a particular file. If this is
1489 ** not a file that we have and we are pulling, then create a
1490 ** phantom to cause this file to be requested on the next cycle.
1491 ** Always remember that the server has this file so that we do
1492 ** not transmit it by accident.
1493 **
1494 ** If the PRIVATE argument exists and is 1, then the file is
1495 ** private. Pretend it does not exists if we are not pulling
1496 ** private files.
1497 */
1498 if( xfer.nToken>=2
1499 && blob_eq(&xfer.aToken[0], "igot")
1500 && blob_is_uuid(&xfer.aToken[1])
1501 ){
1502 int rid;
1503 int isPriv = xfer.nToken>=3 && blob_eq(&xfer.aToken[2],"1");
1504 rid = rid_from_uuid(&xfer.aToken[1], 0, 0);
1505 if( rid>0 ){
1506 if( !isPriv ) content_make_public(rid);
1507 }else if( isPriv && !g.okPrivate ){
1508 /* ignore private files */
1509 }else if( pullFlag || cloneFlag ){
1510 rid = content_new(blob_str(&xfer.aToken[1]), isPriv);
1511 if( rid ) newPhantom = 1;
1512 }
1513 remote_has(rid);
1514 }else
1515
@@ -1454,10 +1589,21 @@
1589 ** same server.
1590 */
1591 if( blob_eq(&xfer.aToken[0], "cookie") && xfer.nToken==2 ){
1592 db_set("cookie", blob_str(&xfer.aToken[1]), 0);
1593 }else
1594
1595
1596 /* private
1597 **
1598 ** This card indicates that the next "file" or "cfile" will contain
1599 ** private content.
1600 */
1601 if( blob_eq(&xfer.aToken[0], "private") ){
1602 xfer.nextIsPrivate = 1;
1603 }else
1604
1605
1606 /* clone_seqno N
1607 **
1608 ** When doing a clone, the server tries to send all of its artifacts
1609 ** in sequence. This card indicates the sequence number of the next
1610

Keyboard Shortcuts

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