| | @@ -38,10 +38,12 @@ |
| 38 | 38 | int nDeltaSent; /* Number of deltas sent */ |
| 39 | 39 | int nFileRcvd; /* Number of files received */ |
| 40 | 40 | int nDeltaRcvd; /* Number of deltas received */ |
| 41 | 41 | int nDanglingFile; /* Number of dangling deltas received */ |
| 42 | 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 */ |
| 43 | 45 | }; |
| 44 | 46 | |
| 45 | 47 | |
| 46 | 48 | /* |
| 47 | 49 | ** The input blob contains a UUID. Convert it into a record ID. |
| | @@ -49,11 +51,11 @@ |
| 49 | 51 | ** phantomize is true. |
| 50 | 52 | ** |
| 51 | 53 | ** Compare to uuid_to_rid(). This routine takes a blob argument |
| 52 | 54 | ** and does less error checking. |
| 53 | 55 | */ |
| 54 | | -static int rid_from_uuid(Blob *pUuid, int phantomize){ |
| 56 | +static int rid_from_uuid(Blob *pUuid, int phantomize, int isPrivate){ |
| 55 | 57 | static Stmt q; |
| 56 | 58 | int rid; |
| 57 | 59 | |
| 58 | 60 | db_static_prepare(&q, "SELECT rid FROM blob WHERE uuid=:uuid"); |
| 59 | 61 | db_bind_str(&q, ":uuid", pUuid); |
| | @@ -62,11 +64,11 @@ |
| 62 | 64 | }else{ |
| 63 | 65 | rid = 0; |
| 64 | 66 | } |
| 65 | 67 | db_reset(&q); |
| 66 | 68 | if( rid==0 && phantomize ){ |
| 67 | | - rid = content_new(blob_str(pUuid), 0); |
| 69 | + rid = content_new(blob_str(pUuid), isPrivate); |
| 68 | 70 | } |
| 69 | 71 | return rid; |
| 70 | 72 | } |
| 71 | 73 | |
| 72 | 74 | /* |
| | @@ -106,11 +108,14 @@ |
| 106 | 108 | static void xfer_accept_file(Xfer *pXfer, int cloneFlag){ |
| 107 | 109 | int n; |
| 108 | 110 | int rid; |
| 109 | 111 | int srcid = 0; |
| 110 | 112 | Blob content, hash; |
| 113 | + int isPriv; |
| 111 | 114 | |
| 115 | + isPriv = pXfer->nextIsPrivate; |
| 116 | + pXfer->nextIsPrivate = 0; |
| 112 | 117 | if( pXfer->nToken<3 |
| 113 | 118 | || pXfer->nToken>4 |
| 114 | 119 | || !blob_is_uuid(&pXfer->aToken[1]) |
| 115 | 120 | || !blob_is_int(&pXfer->aToken[pXfer->nToken-1], &n) |
| 116 | 121 | || n<0 |
| | @@ -123,32 +128,38 @@ |
| 123 | 128 | blob_zero(&hash); |
| 124 | 129 | blob_extract(pXfer->pIn, n, &content); |
| 125 | 130 | if( !cloneFlag && uuid_is_shunned(blob_str(&pXfer->aToken[1])) ){ |
| 126 | 131 | /* Ignore files that have been shunned */ |
| 127 | 132 | return; |
| 133 | + } |
| 134 | + if( isPriv && !g.okPrivate ){ |
| 135 | + /* Do not accept private files if not authorized */ |
| 136 | + return; |
| 128 | 137 | } |
| 129 | 138 | if( cloneFlag ){ |
| 130 | 139 | if( pXfer->nToken==4 ){ |
| 131 | | - srcid = rid_from_uuid(&pXfer->aToken[2], 1); |
| 140 | + srcid = rid_from_uuid(&pXfer->aToken[2], 1, isPriv); |
| 132 | 141 | pXfer->nDeltaRcvd++; |
| 133 | 142 | }else{ |
| 134 | 143 | srcid = 0; |
| 135 | 144 | pXfer->nFileRcvd++; |
| 136 | 145 | } |
| 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); |
| 138 | 148 | remote_has(rid); |
| 139 | 149 | blob_reset(&content); |
| 140 | 150 | return; |
| 141 | 151 | } |
| 142 | 152 | if( pXfer->nToken==4 ){ |
| 143 | 153 | Blob src, next; |
| 144 | | - srcid = rid_from_uuid(&pXfer->aToken[2], 1); |
| 154 | + srcid = rid_from_uuid(&pXfer->aToken[2], 1, isPriv); |
| 145 | 155 | 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); |
| 147 | 158 | pXfer->nDanglingFile++; |
| 148 | 159 | db_multi_exec("DELETE FROM phantom WHERE rid=%d", rid); |
| 149 | | - content_make_public(rid); |
| 160 | + if( !isPriv ) content_make_public(rid); |
| 150 | 161 | return; |
| 151 | 162 | } |
| 152 | 163 | pXfer->nDeltaRcvd++; |
| 153 | 164 | blob_delta_apply(&src, &content, &next); |
| 154 | 165 | blob_reset(&src); |
| | @@ -159,17 +170,17 @@ |
| 159 | 170 | } |
| 160 | 171 | sha1sum_blob(&content, &hash); |
| 161 | 172 | if( !blob_eq_str(&pXfer->aToken[1], blob_str(&hash), -1) ){ |
| 162 | 173 | blob_appendf(&pXfer->err, "content does not match sha1 hash"); |
| 163 | 174 | } |
| 164 | | - rid = content_put_ex(&content, blob_str(&hash), 0, 0, 0); |
| 175 | + rid = content_put_ex(&content, blob_str(&hash), 0, 0, isPriv); |
| 165 | 176 | blob_reset(&hash); |
| 166 | 177 | if( rid==0 ){ |
| 167 | 178 | blob_appendf(&pXfer->err, "%s", g.zErrMsg); |
| 168 | 179 | blob_reset(&content); |
| 169 | 180 | }else{ |
| 170 | | - content_make_public(rid); |
| 181 | + if( !isPriv ) content_make_public(rid); |
| 171 | 182 | manifest_crosslink(rid, &content); |
| 172 | 183 | } |
| 173 | 184 | assert( blob_is_reset(&content) ); |
| 174 | 185 | remote_has(rid); |
| 175 | 186 | } |
| | @@ -201,11 +212,14 @@ |
| 201 | 212 | int szC; /* CSIZE */ |
| 202 | 213 | int szU; /* USIZE */ |
| 203 | 214 | int rid; |
| 204 | 215 | int srcid = 0; |
| 205 | 216 | Blob content; |
| 217 | + int isPriv; |
| 206 | 218 | |
| 219 | + isPriv = pXfer->nextIsPrivate; |
| 220 | + pXfer->nextIsPrivate = 0; |
| 207 | 221 | if( pXfer->nToken<4 |
| 208 | 222 | || pXfer->nToken>5 |
| 209 | 223 | || !blob_is_uuid(&pXfer->aToken[1]) |
| 210 | 224 | || !blob_is_int(&pXfer->aToken[pXfer->nToken-2], &szU) |
| 211 | 225 | || !blob_is_int(&pXfer->aToken[pXfer->nToken-1], &szC) |
| | @@ -212,25 +226,30 @@ |
| 212 | 226 | || szC<0 || szU<0 |
| 213 | 227 | || (pXfer->nToken==5 && !blob_is_uuid(&pXfer->aToken[2])) |
| 214 | 228 | ){ |
| 215 | 229 | blob_appendf(&pXfer->err, "malformed cfile line"); |
| 216 | 230 | return; |
| 231 | + } |
| 232 | + if( isPriv && !g.okPrivate ){ |
| 233 | + /* Do not accept private files if not authorized */ |
| 234 | + return; |
| 217 | 235 | } |
| 218 | 236 | blob_zero(&content); |
| 219 | 237 | blob_extract(pXfer->pIn, szC, &content); |
| 220 | 238 | if( uuid_is_shunned(blob_str(&pXfer->aToken[1])) ){ |
| 221 | 239 | /* Ignore files that have been shunned */ |
| 222 | 240 | return; |
| 223 | 241 | } |
| 224 | 242 | if( pXfer->nToken==5 ){ |
| 225 | | - srcid = rid_from_uuid(&pXfer->aToken[2], 1); |
| 243 | + srcid = rid_from_uuid(&pXfer->aToken[2], 1, isPriv); |
| 226 | 244 | pXfer->nDeltaRcvd++; |
| 227 | 245 | }else{ |
| 228 | 246 | srcid = 0; |
| 229 | 247 | pXfer->nFileRcvd++; |
| 230 | 248 | } |
| 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); |
| 232 | 251 | remote_has(rid); |
| 233 | 252 | blob_reset(&content); |
| 234 | 253 | } |
| 235 | 254 | |
| 236 | 255 | /* |
| | @@ -242,10 +261,11 @@ |
| 242 | 261 | ** Never send a delta against a private artifact. |
| 243 | 262 | */ |
| 244 | 263 | static int send_delta_parent( |
| 245 | 264 | Xfer *pXfer, /* The transfer context */ |
| 246 | 265 | int rid, /* record id of the file to send */ |
| 266 | + int isPrivate, /* True if rid is a private artifact */ |
| 247 | 267 | Blob *pContent, /* The content of the file to send */ |
| 248 | 268 | Blob *pUuid /* The UUID of the file to send */ |
| 249 | 269 | ){ |
| 250 | 270 | static const char *azQuery[] = { |
| 251 | 271 | "SELECT pid FROM plink x" |
| | @@ -266,22 +286,25 @@ |
| 266 | 286 | int srcId = 0; |
| 267 | 287 | |
| 268 | 288 | for(i=0; srcId==0 && i<count(azQuery); i++){ |
| 269 | 289 | srcId = db_int(0, azQuery[i], rid); |
| 270 | 290 | } |
| 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 | + ){ |
| 272 | 295 | char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", srcId); |
| 273 | 296 | blob_delta_create(&src, pContent, &delta); |
| 274 | 297 | size = blob_size(&delta); |
| 275 | 298 | if( size>=blob_size(pContent)-50 ){ |
| 276 | 299 | size = 0; |
| 277 | 300 | }else if( uuid_is_shunned(zUuid) ){ |
| 278 | 301 | size = 0; |
| 279 | 302 | }else{ |
| 303 | + if( isPrivate ) blob_append(pXfer->pOut, "private\n", -1); |
| 280 | 304 | blob_appendf(pXfer->pOut, "file %b %s %d\n", pUuid, zUuid, size); |
| 281 | 305 | blob_append(pXfer->pOut, blob_buffer(&delta), size); |
| 282 | | - /* blob_appendf(pXfer->pOut, "\n", 1); */ |
| 283 | 306 | } |
| 284 | 307 | blob_reset(&delta); |
| 285 | 308 | free(zUuid); |
| 286 | 309 | blob_reset(&src); |
| 287 | 310 | } |
| | @@ -297,27 +320,31 @@ |
| 297 | 320 | ** Never send a delta against a private artifact. |
| 298 | 321 | */ |
| 299 | 322 | static int send_delta_native( |
| 300 | 323 | Xfer *pXfer, /* The transfer context */ |
| 301 | 324 | int rid, /* record id of the file to send */ |
| 325 | + int isPrivate, /* True if rid is a private artifact */ |
| 302 | 326 | Blob *pUuid /* The UUID of the file to send */ |
| 303 | 327 | ){ |
| 304 | 328 | Blob src, delta; |
| 305 | 329 | int size = 0; |
| 306 | 330 | int srcId; |
| 307 | 331 | |
| 308 | 332 | 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 | + ){ |
| 310 | 336 | blob_zero(&src); |
| 311 | 337 | db_blob(&src, "SELECT uuid FROM blob WHERE rid=%d", srcId); |
| 312 | 338 | if( uuid_is_shunned(blob_str(&src)) ){ |
| 313 | 339 | blob_reset(&src); |
| 314 | 340 | return 0; |
| 315 | 341 | } |
| 316 | 342 | blob_zero(&delta); |
| 317 | 343 | db_blob(&delta, "SELECT content FROM blob WHERE rid=%d", rid); |
| 318 | 344 | blob_uncompress(&delta, &delta); |
| 345 | + if( isPrivate ) blob_append(pXfer->pOut, "private\n", -1); |
| 319 | 346 | blob_appendf(pXfer->pOut, "file %b %b %d\n", |
| 320 | 347 | pUuid, &src, blob_size(&delta)); |
| 321 | 348 | blob_append(pXfer->pOut, blob_buffer(&delta), blob_size(&delta)); |
| 322 | 349 | size = blob_size(&delta); |
| 323 | 350 | blob_reset(&delta); |
| | @@ -342,12 +369,13 @@ |
| 342 | 369 | ** this routine becomes a no-op. |
| 343 | 370 | */ |
| 344 | 371 | static void send_file(Xfer *pXfer, int rid, Blob *pUuid, int nativeDelta){ |
| 345 | 372 | Blob content, uuid; |
| 346 | 373 | int size = 0; |
| 374 | + int isPriv = content_is_private(rid); |
| 347 | 375 | |
| 348 | | - if( content_is_private(rid) ) return; |
| 376 | + if( pXfer->syncPrivate==0 && isPriv ) return; |
| 349 | 377 | if( db_exists("SELECT 1 FROM onremote WHERE rid=%d", rid) ){ |
| 350 | 378 | return; |
| 351 | 379 | } |
| 352 | 380 | blob_zero(&uuid); |
| 353 | 381 | db_blob(&uuid, "SELECT uuid FROM blob WHERE rid=%d AND size>=0", rid); |
| | @@ -365,38 +393,45 @@ |
| 365 | 393 | if( uuid_is_shunned(blob_str(pUuid)) ){ |
| 366 | 394 | blob_reset(&uuid); |
| 367 | 395 | return; |
| 368 | 396 | } |
| 369 | 397 | 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); |
| 371 | 400 | pXfer->nIGotSent++; |
| 372 | 401 | blob_reset(&uuid); |
| 373 | 402 | return; |
| 374 | 403 | } |
| 375 | 404 | if( nativeDelta ){ |
| 376 | | - size = send_delta_native(pXfer, rid, pUuid); |
| 405 | + size = send_delta_native(pXfer, rid, isPriv, pUuid); |
| 377 | 406 | if( size ){ |
| 378 | 407 | pXfer->nDeltaSent++; |
| 379 | 408 | } |
| 380 | 409 | } |
| 381 | 410 | if( size==0 ){ |
| 382 | 411 | content_get(rid, &content); |
| 383 | 412 | |
| 384 | 413 | 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); |
| 386 | 415 | } |
| 387 | 416 | if( size==0 ){ |
| 388 | 417 | int size = blob_size(&content); |
| 418 | + if( isPriv ) blob_append(pXfer->pOut, "private\n", -1); |
| 389 | 419 | blob_appendf(pXfer->pOut, "file %b %d\n", pUuid, size); |
| 390 | 420 | blob_append(pXfer->pOut, blob_buffer(&content), size); |
| 391 | 421 | pXfer->nFileSent++; |
| 392 | 422 | }else{ |
| 393 | 423 | pXfer->nDeltaSent++; |
| 394 | 424 | } |
| 395 | 425 | } |
| 396 | 426 | remote_has(rid); |
| 397 | 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 |
| 398 | 433 | } |
| 399 | 434 | |
| 400 | 435 | /* |
| 401 | 436 | ** Send the file identified by rid as a compressed artifact. Basically, |
| 402 | 437 | ** send the content exactly as it appears in the BLOB table using |
| | @@ -407,57 +442,62 @@ |
| 407 | 442 | const char *zUuid; |
| 408 | 443 | const char *zDelta; |
| 409 | 444 | int szU; |
| 410 | 445 | int szC; |
| 411 | 446 | int rc; |
| 447 | + int isPrivate; |
| 412 | 448 | static Stmt q1; |
| 413 | 449 | |
| 450 | + isPrivate = content_is_private(rid); |
| 451 | + if( isPrivate && pXfer->syncPrivate==0 ) return; |
| 414 | 452 | db_static_prepare(&q1, |
| 415 | 453 | "SELECT uuid, size, content," |
| 416 | 454 | " (SELECT uuid FROM delta, blob" |
| 417 | 455 | " WHERE delta.rid=:rid AND delta.srcid=blob.rid)" |
| 418 | 456 | " FROM blob" |
| 419 | 457 | " WHERE rid=:rid" |
| 420 | 458 | " AND size>=0" |
| 421 | 459 | " AND uuid NOT IN shun" |
| 422 | | - " AND rid NOT IN private", |
| 423 | | - rid |
| 424 | 460 | ); |
| 425 | 461 | db_bind_int(&q1, ":rid", rid); |
| 426 | 462 | rc = db_step(&q1); |
| 427 | 463 | if( rc==SQLITE_ROW ){ |
| 428 | 464 | zUuid = db_column_text(&q1, 0); |
| 429 | 465 | szU = db_column_int(&q1, 1); |
| 430 | 466 | szC = db_column_bytes(&q1, 2); |
| 431 | 467 | zContent = db_column_raw(&q1, 2); |
| 432 | 468 | zDelta = db_column_text(&q1, 3); |
| 469 | + if( isPrivate ) blob_append(pXfer->pOut, "private\n", -1); |
| 433 | 470 | blob_appendf(pXfer->pOut, "cfile %s ", zUuid); |
| 434 | | - if( zDelta ){ |
| 471 | + if( zDelta ){ |
| 435 | 472 | blob_appendf(pXfer->pOut, "%s ", zDelta); |
| 436 | 473 | pXfer->nDeltaSent++; |
| 437 | 474 | }else{ |
| 438 | 475 | pXfer->nFileSent++; |
| 439 | 476 | } |
| 440 | 477 | blob_appendf(pXfer->pOut, "%d %d\n", szU, szC); |
| 441 | 478 | 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 | + } |
| 443 | 482 | } |
| 444 | 483 | db_reset(&q1); |
| 445 | 484 | } |
| 446 | 485 | |
| 447 | 486 | /* |
| 448 | 487 | ** Send a gimme message for every phantom. |
| 449 | 488 | ** |
| 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. |
| 452 | 491 | */ |
| 453 | 492 | static void request_phantoms(Xfer *pXfer, int maxReq){ |
| 454 | 493 | Stmt q; |
| 455 | 494 | db_prepare(&q, |
| 456 | 495 | "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)") |
| 459 | 499 | ); |
| 460 | 500 | while( db_step(&q)==SQLITE_ROW && maxReq-- > 0 ){ |
| 461 | 501 | const char *zUuid = db_column_text(&q, 0); |
| 462 | 502 | blob_appendf(pXfer->pOut, "gimme %s\n", zUuid); |
| 463 | 503 | pXfer->nGimmeSent++; |
| | @@ -641,10 +681,27 @@ |
| 641 | 681 | content_put(&cluster); |
| 642 | 682 | blob_reset(&cluster); |
| 643 | 683 | } |
| 644 | 684 | } |
| 645 | 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 | +} |
| 646 | 703 | |
| 647 | 704 | /* |
| 648 | 705 | ** Send an igot message for every entry in unclustered table. |
| 649 | 706 | ** Return the number of cards sent. |
| 650 | 707 | */ |
| | @@ -652,12 +709,12 @@ |
| 652 | 709 | Stmt q; |
| 653 | 710 | int cnt = 0; |
| 654 | 711 | db_prepare(&q, |
| 655 | 712 | "SELECT uuid FROM unclustered JOIN blob USING(rid)" |
| 656 | 713 | " WHERE NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid)" |
| 657 | | - " AND NOT EXISTS(SELECT 1 FROM private WHERE rid=blob.rid)" |
| 658 | 714 | " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=blob.rid)" |
| 715 | + " AND NOT EXISTS(SELECT 1 FROM private WHERE rid=blob.rid)" |
| 659 | 716 | ); |
| 660 | 717 | while( db_step(&q)==SQLITE_ROW ){ |
| 661 | 718 | blob_appendf(pXfer->pOut, "igot %s\n", db_column_text(&q, 0)); |
| 662 | 719 | cnt++; |
| 663 | 720 | } |
| | @@ -704,10 +761,19 @@ |
| 704 | 761 | blob_size(&content), blob_str(&content)); |
| 705 | 762 | blob_reset(&content); |
| 706 | 763 | } |
| 707 | 764 | } |
| 708 | 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 | + |
| 709 | 775 | |
| 710 | 776 | /* |
| 711 | 777 | ** If this variable is set, disable login checks. Used for debugging |
| 712 | 778 | ** only. |
| 713 | 779 | */ |
| | @@ -757,10 +823,11 @@ |
| 757 | 823 | "CREATE TEMP TABLE onremote(rid INTEGER PRIMARY KEY);" |
| 758 | 824 | ); |
| 759 | 825 | manifest_crosslink_begin(); |
| 760 | 826 | while( blob_line(xfer.pIn, &xfer.line) ){ |
| 761 | 827 | if( blob_buffer(&xfer.line)[0]=='#' ) continue; |
| 828 | + if( blob_size(&xfer.line)==0 ) continue; |
| 762 | 829 | xfer.nToken = blob_tokenize(&xfer.line, xfer.aToken, count(xfer.aToken)); |
| 763 | 830 | |
| 764 | 831 | /* file UUID SIZE \n CONTENT |
| 765 | 832 | ** file UUID DELTASRC SIZE \n CONTENT |
| 766 | 833 | ** |
| | @@ -811,27 +878,34 @@ |
| 811 | 878 | && xfer.nToken==2 |
| 812 | 879 | && blob_is_uuid(&xfer.aToken[1]) |
| 813 | 880 | ){ |
| 814 | 881 | nGimme++; |
| 815 | 882 | if( isPull ){ |
| 816 | | - int rid = rid_from_uuid(&xfer.aToken[1], 0); |
| 883 | + int rid = rid_from_uuid(&xfer.aToken[1], 0, 0); |
| 817 | 884 | if( rid ){ |
| 818 | 885 | send_file(&xfer, rid, &xfer.aToken[1], deltaFlag); |
| 819 | 886 | } |
| 820 | 887 | } |
| 821 | 888 | }else |
| 822 | 889 | |
| 823 | | - /* igot UUID |
| 890 | + /* igot UUID ?ISPRIVATE? |
| 824 | 891 | ** |
| 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. |
| 826 | 894 | */ |
| 827 | | - if( xfer.nToken==2 |
| 895 | + if( xfer.nToken>=2 |
| 828 | 896 | && blob_eq(&xfer.aToken[0], "igot") |
| 829 | 897 | && blob_is_uuid(&xfer.aToken[1]) |
| 830 | 898 | ){ |
| 831 | 899 | 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 | + } |
| 833 | 907 | } |
| 834 | 908 | }else |
| 835 | 909 | |
| 836 | 910 | |
| 837 | 911 | /* pull SERVERCODE PROJECTCODE |
| | @@ -929,11 +1003,11 @@ |
| 929 | 1003 | */ |
| 930 | 1004 | if( blob_eq(&xfer.aToken[0], "login") |
| 931 | 1005 | && xfer.nToken==4 |
| 932 | 1006 | ){ |
| 933 | 1007 | if( disableLogin ){ |
| 934 | | - g.okRead = g.okWrite = 1; |
| 1008 | + g.okRead = g.okWrite = g.okPrivate = 1; |
| 935 | 1009 | }else{ |
| 936 | 1010 | if( check_tail_hash(&xfer.aToken[2], xfer.pIn) |
| 937 | 1011 | || check_login(&xfer.aToken[1], &xfer.aToken[2], &xfer.aToken[3]) |
| 938 | 1012 | ){ |
| 939 | 1013 | cgi_reset_content(); |
| | @@ -1029,10 +1103,48 @@ |
| 1029 | 1103 | */ |
| 1030 | 1104 | if( blob_eq(&xfer.aToken[0], "cookie") && xfer.nToken==2 ){ |
| 1031 | 1105 | /* Process the cookie */ |
| 1032 | 1106 | }else |
| 1033 | 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 | + |
| 1034 | 1146 | /* Unknown message |
| 1035 | 1147 | */ |
| 1036 | 1148 | { |
| 1037 | 1149 | cgi_reset_content(); |
| 1038 | 1150 | @ error bad\scommand:\s%F(blob_str(&xfer.line)) |
| | @@ -1049,13 +1161,15 @@ |
| 1049 | 1161 | ** cause the client to create phantoms for all artifacts, which will |
| 1050 | 1162 | ** in turn make sure that the entire repository is sent efficiently |
| 1051 | 1163 | ** and expeditiously. |
| 1052 | 1164 | */ |
| 1053 | 1165 | send_all(&xfer); |
| 1166 | + if( xfer.syncPrivate ) send_private(&xfer); |
| 1054 | 1167 | }else if( isPull ){ |
| 1055 | 1168 | create_cluster(); |
| 1056 | 1169 | send_unclustered(&xfer); |
| 1170 | + if( xfer.syncPrivate ) send_private(&xfer); |
| 1057 | 1171 | } |
| 1058 | 1172 | if( recvConfig ){ |
| 1059 | 1173 | configure_finalize_receive(); |
| 1060 | 1174 | } |
| 1061 | 1175 | manifest_crosslink_end(); |
| | @@ -1120,10 +1234,11 @@ |
| 1120 | 1234 | */ |
| 1121 | 1235 | int client_sync( |
| 1122 | 1236 | int pushFlag, /* True to do a push (or a sync) */ |
| 1123 | 1237 | int pullFlag, /* True to do a pull (or a sync) */ |
| 1124 | 1238 | int cloneFlag, /* True if this is a clone */ |
| 1239 | + int privateFlag, /* True to exchange private branches */ |
| 1125 | 1240 | int configRcvMask, /* Receive these configuration items */ |
| 1126 | 1241 | int configSendMask /* Send these configuration items */ |
| 1127 | 1242 | ){ |
| 1128 | 1243 | int go = 1; /* Loop until zero */ |
| 1129 | 1244 | int nCardSent = 0; /* Number of cards sent */ |
| | @@ -1155,10 +1270,14 @@ |
| 1155 | 1270 | socket_global_init(); |
| 1156 | 1271 | memset(&xfer, 0, sizeof(xfer)); |
| 1157 | 1272 | xfer.pIn = &recv; |
| 1158 | 1273 | xfer.pOut = &send; |
| 1159 | 1274 | xfer.mxSend = db_get_int("max-upload", 250000); |
| 1275 | + if( privateFlag ){ |
| 1276 | + g.okPrivate = 1; |
| 1277 | + xfer.syncPrivate = 1; |
| 1278 | + } |
| 1160 | 1279 | |
| 1161 | 1280 | assert( pushFlag | pullFlag | cloneFlag | configRcvMask | configSendMask ); |
| 1162 | 1281 | db_begin_transaction(); |
| 1163 | 1282 | db_record_repository_filename(0); |
| 1164 | 1283 | db_multi_exec( |
| | @@ -1169,10 +1288,14 @@ |
| 1169 | 1288 | blob_zero(&recv); |
| 1170 | 1289 | blob_zero(&xfer.err); |
| 1171 | 1290 | blob_zero(&xfer.line); |
| 1172 | 1291 | origConfigRcvMask = 0; |
| 1173 | 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 | + |
| 1174 | 1297 | /* |
| 1175 | 1298 | ** Always begin with a clone, pull, or push message |
| 1176 | 1299 | */ |
| 1177 | 1300 | if( cloneFlag ){ |
| 1178 | 1301 | blob_appendf(&send, "clone 3 %d\n", cloneSeqno); |
| | @@ -1212,10 +1335,11 @@ |
| 1212 | 1335 | request_phantoms(&xfer, mxPhantomReq); |
| 1213 | 1336 | } |
| 1214 | 1337 | if( pushFlag ){ |
| 1215 | 1338 | send_unsent(&xfer); |
| 1216 | 1339 | nCardSent += send_unclustered(&xfer); |
| 1340 | + if( privateFlag ) send_private(&xfer); |
| 1217 | 1341 | } |
| 1218 | 1342 | |
| 1219 | 1343 | /* Send configuration parameter requests. On a clone, delay sending |
| 1220 | 1344 | ** this until the second cycle since the login card might fail on |
| 1221 | 1345 | ** the first cycle. |
| | @@ -1275,10 +1399,13 @@ |
| 1275 | 1399 | break; |
| 1276 | 1400 | } |
| 1277 | 1401 | lastPctDone = -1; |
| 1278 | 1402 | blob_reset(&send); |
| 1279 | 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); |
| 1280 | 1407 | |
| 1281 | 1408 | /* Begin constructing the next message (which might never be |
| 1282 | 1409 | ** sent) by beginning with the pull or push cards |
| 1283 | 1410 | */ |
| 1284 | 1411 | if( pullFlag ){ |
| | @@ -1349,32 +1476,40 @@ |
| 1349 | 1476 | if( blob_eq(&xfer.aToken[0], "gimme") |
| 1350 | 1477 | && xfer.nToken==2 |
| 1351 | 1478 | && blob_is_uuid(&xfer.aToken[1]) |
| 1352 | 1479 | ){ |
| 1353 | 1480 | if( pushFlag ){ |
| 1354 | | - int rid = rid_from_uuid(&xfer.aToken[1], 0); |
| 1481 | + int rid = rid_from_uuid(&xfer.aToken[1], 0, 0); |
| 1355 | 1482 | if( rid ) send_file(&xfer, rid, &xfer.aToken[1], 0); |
| 1356 | 1483 | } |
| 1357 | 1484 | }else |
| 1358 | 1485 | |
| 1359 | | - /* igot UUID |
| 1486 | + /* igot UUID ?PRIVATEFLAG? |
| 1360 | 1487 | ** |
| 1361 | 1488 | ** Server announces that it has a particular file. If this is |
| 1362 | 1489 | ** not a file that we have and we are pulling, then create a |
| 1363 | 1490 | ** phantom to cause this file to be requested on the next cycle. |
| 1364 | 1491 | ** Always remember that the server has this file so that we do |
| 1365 | 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. |
| 1366 | 1497 | */ |
| 1367 | | - if( xfer.nToken==2 |
| 1498 | + if( xfer.nToken>=2 |
| 1368 | 1499 | && blob_eq(&xfer.aToken[0], "igot") |
| 1369 | 1500 | && blob_is_uuid(&xfer.aToken[1]) |
| 1370 | 1501 | ){ |
| 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); |
| 1372 | 1505 | 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 */ |
| 1374 | 1509 | }else if( pullFlag || cloneFlag ){ |
| 1375 | | - rid = content_new(blob_str(&xfer.aToken[1]), 0); |
| 1510 | + rid = content_new(blob_str(&xfer.aToken[1]), isPriv); |
| 1376 | 1511 | if( rid ) newPhantom = 1; |
| 1377 | 1512 | } |
| 1378 | 1513 | remote_has(rid); |
| 1379 | 1514 | }else |
| 1380 | 1515 | |
| | @@ -1454,10 +1589,21 @@ |
| 1454 | 1589 | ** same server. |
| 1455 | 1590 | */ |
| 1456 | 1591 | if( blob_eq(&xfer.aToken[0], "cookie") && xfer.nToken==2 ){ |
| 1457 | 1592 | db_set("cookie", blob_str(&xfer.aToken[1]), 0); |
| 1458 | 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 | + |
| 1459 | 1605 | |
| 1460 | 1606 | /* clone_seqno N |
| 1461 | 1607 | ** |
| 1462 | 1608 | ** When doing a clone, the server tries to send all of its artifacts |
| 1463 | 1609 | ** in sequence. This card indicates the sequence number of the next |
| 1464 | 1610 | |