| | @@ -63,10 +63,17 @@ |
| 63 | 63 | rid = content_put(0, blob_str(pUuid), 0); |
| 64 | 64 | } |
| 65 | 65 | return rid; |
| 66 | 66 | } |
| 67 | 67 | |
| 68 | +/* |
| 69 | +** Remember that the other side of the connection already has a copy |
| 70 | +** of the file rid. |
| 71 | +*/ |
| 72 | +static void remote_has(int rid){ |
| 73 | + db_multi_exec("INSERT OR IGNORE INTO onremote VALUES(%d)", rid); |
| 74 | +} |
| 68 | 75 | |
| 69 | 76 | /* |
| 70 | 77 | ** The aToken[0..nToken-1] blob array is a parse of a "file" line |
| 71 | 78 | ** message. This routine finishes parsing that message and does |
| 72 | 79 | ** a record insert of the file. |
| | @@ -126,10 +133,11 @@ |
| 126 | 133 | if( rid==0 ){ |
| 127 | 134 | blob_appendf(&pXfer->err, "%s", g.zErrMsg); |
| 128 | 135 | }else{ |
| 129 | 136 | manifest_crosslink(rid, &content); |
| 130 | 137 | } |
| 138 | + remote_has(rid); |
| 131 | 139 | } |
| 132 | 140 | |
| 133 | 141 | /* |
| 134 | 142 | ** Try to send a file as a delta. If successful, return the number |
| 135 | 143 | ** of bytes in the delta. If not, return zero. |
| | @@ -221,21 +229,46 @@ |
| 221 | 229 | blob_append(pXfer->pOut, blob_buffer(&content), size); |
| 222 | 230 | pXfer->nFileSent++; |
| 223 | 231 | }else{ |
| 224 | 232 | pXfer->nDeltaSent++; |
| 225 | 233 | } |
| 226 | | - db_multi_exec("INSERT INTO onremote VALUES(%d)", rid); |
| 234 | + remote_has(rid); |
| 227 | 235 | blob_reset(&uuid); |
| 228 | 236 | } |
| 237 | + |
| 238 | +/* |
| 239 | +** Send the file identified by mid and pUuid. If that file happens |
| 240 | +** to be a manifest, then also send all of the associated content |
| 241 | +** files for that manifest. If the file is not a manifest, then this |
| 242 | +** routine is the equivalent of send_file(). |
| 243 | +*/ |
| 244 | +static void send_manifest(Xfer *pXfer, int mid, Blob *pUuid, int srcId){ |
| 245 | + Stmt q2; |
| 246 | + send_file(pXfer, mid, pUuid, srcId); |
| 247 | + db_prepare(&q2, |
| 248 | + "SELECT pid, uuid, fid FROM mlink, blob" |
| 249 | + " WHERE rid=fid AND mid=%d", |
| 250 | + mid |
| 251 | + ); |
| 252 | + while( db_step(&q2)==SQLITE_ROW ){ |
| 253 | + int pid, fid; |
| 254 | + Blob uuid; |
| 255 | + pid = db_column_int(&q2, 0); |
| 256 | + db_ephemeral_blob(&q2, 1, &uuid); |
| 257 | + fid = db_column_int(&q2, 2); |
| 258 | + send_file(pXfer, fid, &uuid, pid); |
| 259 | + } |
| 260 | + db_finalize(&q2); |
| 261 | +} |
| 229 | 262 | |
| 230 | 263 | /* |
| 231 | 264 | ** This routine runs when either client or server is notified that |
| 232 | | -** the other side things rid is a leaf manifest. If we hold |
| 265 | +** the other side thinks rid is a leaf manifest. If we hold |
| 233 | 266 | ** children of rid, then send them over to the other side. |
| 234 | 267 | */ |
| 235 | 268 | static void leaf_response(Xfer *pXfer, int rid){ |
| 236 | | - Stmt q1, q2; |
| 269 | + Stmt q1; |
| 237 | 270 | db_prepare(&q1, |
| 238 | 271 | "SELECT cid, uuid FROM plink, blob" |
| 239 | 272 | " WHERE blob.rid=plink.cid" |
| 240 | 273 | " AND plink.pid=%d", |
| 241 | 274 | rid |
| | @@ -244,24 +277,11 @@ |
| 244 | 277 | Blob uuid; |
| 245 | 278 | int cid; |
| 246 | 279 | |
| 247 | 280 | cid = db_column_int(&q1, 0); |
| 248 | 281 | db_ephemeral_blob(&q1, 1, &uuid); |
| 249 | | - send_file(pXfer, cid, &uuid, rid); |
| 250 | | - db_prepare(&q2, |
| 251 | | - "SELECT pid, uuid, fid FROM mlink, blob" |
| 252 | | - " WHERE rid=fid AND mid=%d", |
| 253 | | - cid |
| 254 | | - ); |
| 255 | | - while( db_step(&q2)==SQLITE_ROW ){ |
| 256 | | - int pid, fid; |
| 257 | | - pid = db_column_int(&q2, 0); |
| 258 | | - db_ephemeral_blob(&q2, 1, &uuid); |
| 259 | | - fid = db_column_int(&q2, 2); |
| 260 | | - send_file(pXfer, fid, &uuid, pid); |
| 261 | | - } |
| 262 | | - db_finalize(&q2); |
| 282 | + send_manifest(pXfer, cid, &uuid, rid); |
| 263 | 283 | if( blob_size(pXfer->pOut)<pXfer->mxSend ){ |
| 264 | 284 | leaf_response(pXfer, cid); |
| 265 | 285 | } |
| 266 | 286 | } |
| 267 | 287 | } |
| | @@ -279,10 +299,37 @@ |
| 279 | 299 | const char *zUuid = db_column_text(&q, 0); |
| 280 | 300 | blob_appendf(pXfer->pOut, "leaf %s\n", zUuid); |
| 281 | 301 | } |
| 282 | 302 | db_finalize(&q); |
| 283 | 303 | } |
| 304 | + |
| 305 | +/* |
| 306 | +** Sent leaf content for every leaf that is not found in the |
| 307 | +** onremote table. This is intended to send leaf content for |
| 308 | +** every leaf that is unknown on the remote end. |
| 309 | +** |
| 310 | +** In addition, we might send "igot" messages for a few generations of |
| 311 | +** parents of the unknown leaves. This will speed the transmission |
| 312 | +** of new branches. |
| 313 | +*/ |
| 314 | +static void send_unknown_leaf_content(Xfer *pXfer){ |
| 315 | + Stmt q1; |
| 316 | + db_prepare(&q1, |
| 317 | + "SELECT rid, uuid FROM blob WHERE rid IN" |
| 318 | + " (SELECT cid FROM plink EXCEPT SELECT pid FROM plink)" |
| 319 | + " AND NOT EXISTS(SELECT 1 FROM onremote WHERE rid=blob.rid)" |
| 320 | + ); |
| 321 | + while( db_step(&q1)==SQLITE_ROW ){ |
| 322 | + Blob uuid; |
| 323 | + int cid; |
| 324 | + |
| 325 | + cid = db_column_int(&q1, 0); |
| 326 | + db_ephemeral_blob(&q1, 1, &uuid); |
| 327 | + send_manifest(pXfer, cid, &uuid, 0); |
| 328 | + } |
| 329 | + db_finalize(&q1); |
| 330 | +} |
| 284 | 331 | |
| 285 | 332 | /* |
| 286 | 333 | ** Sen a gimme message for every phantom. |
| 287 | 334 | */ |
| 288 | 335 | static void request_phantoms(Xfer *pXfer){ |
| | @@ -407,27 +454,29 @@ |
| 407 | 454 | } |
| 408 | 455 | }else |
| 409 | 456 | |
| 410 | 457 | /* gimme UUID |
| 411 | 458 | ** |
| 412 | | - ** Client is requesting a file |
| 459 | + ** Client is requesting a file. If the file is a manifest, |
| 460 | + ** the server can assume that the client also needs all content |
| 461 | + ** files associated with that manifest. |
| 413 | 462 | */ |
| 414 | 463 | if( blob_eq(&xfer.aToken[0], "gimme") |
| 415 | 464 | && xfer.nToken==2 |
| 416 | 465 | && blob_is_uuid(&xfer.aToken[1]) |
| 417 | 466 | ){ |
| 418 | 467 | if( isPull ){ |
| 419 | 468 | int rid = rid_from_uuid(&xfer.aToken[1], 0); |
| 420 | 469 | if( rid ){ |
| 421 | | - send_file(&xfer, rid, &xfer.aToken[1], 0); |
| 470 | + send_manifest(&xfer, rid, &xfer.aToken[1], 0); |
| 422 | 471 | } |
| 423 | 472 | } |
| 424 | 473 | }else |
| 425 | 474 | |
| 426 | 475 | /* igot UUID |
| 427 | 476 | ** |
| 428 | | - ** Client announces that it has a particular file |
| 477 | + ** Client announces that it has a particular file. |
| 429 | 478 | */ |
| 430 | 479 | if( xfer.nToken==2 |
| 431 | 480 | && blob_eq(&xfer.aToken[0], "igot") |
| 432 | 481 | && blob_is_uuid(&xfer.aToken[1]) |
| 433 | 482 | ){ |
| | @@ -447,22 +496,26 @@ |
| 447 | 496 | if( xfer.nToken==2 |
| 448 | 497 | && blob_eq(&xfer.aToken[0], "leaf") |
| 449 | 498 | && blob_is_uuid(&xfer.aToken[1]) |
| 450 | 499 | ){ |
| 451 | 500 | int rid = rid_from_uuid(&xfer.aToken[1], 0); |
| 452 | | - if( isPull && rid ){ |
| 453 | | - leaf_response(&xfer, rid); |
| 454 | | - } |
| 455 | | - if( isPush && !rid ){ |
| 501 | + if( rid ){ |
| 502 | + remote_has(rid); |
| 503 | + if( isPull ){ |
| 504 | + leaf_response(&xfer, rid); |
| 505 | + } |
| 506 | + }else if( isPush ){ |
| 456 | 507 | content_put(0, blob_str(&xfer.aToken[1]), 0); |
| 457 | 508 | } |
| 458 | 509 | }else |
| 459 | 510 | |
| 460 | 511 | /* pull SERVERCODE PROJECTCODE |
| 461 | 512 | ** push SERVERCODE PROJECTCODE |
| 462 | 513 | ** |
| 463 | | - ** The client wants either send or receive |
| 514 | + ** The client wants either send or receive. The server should |
| 515 | + ** verify that the project code matches and that the server code |
| 516 | + ** does not match. |
| 464 | 517 | */ |
| 465 | 518 | if( xfer.nToken==3 |
| 466 | 519 | && (blob_eq(&xfer.aToken[0], "pull") || blob_eq(&xfer.aToken[0], "push")) |
| 467 | 520 | && blob_is_uuid(&xfer.aToken[1]) |
| 468 | 521 | && blob_is_uuid(&xfer.aToken[2]) |
| | @@ -537,10 +590,11 @@ |
| 537 | 590 | }else |
| 538 | 591 | |
| 539 | 592 | /* login USER NONCE SIGNATURE |
| 540 | 593 | ** |
| 541 | 594 | ** Check for a valid login. This has to happen before anything else. |
| 595 | + ** The client can send multiple logins. Permissions are cumulative. |
| 542 | 596 | */ |
| 543 | 597 | if( blob_eq(&xfer.aToken[0], "login") |
| 544 | 598 | && xfer.nToken==4 |
| 545 | 599 | ){ |
| 546 | 600 | if( disableLogin ){ |
| | @@ -559,10 +613,13 @@ |
| 559 | 613 | blobarray_reset(xfer.aToken, xfer.nToken); |
| 560 | 614 | } |
| 561 | 615 | if( isPush ){ |
| 562 | 616 | request_phantoms(&xfer); |
| 563 | 617 | } |
| 618 | + if( isPull ){ |
| 619 | + send_unknown_leaf_content(&xfer); |
| 620 | + } |
| 564 | 621 | db_end_transaction(0); |
| 565 | 622 | } |
| 566 | 623 | |
| 567 | 624 | /* |
| 568 | 625 | ** COMMAND: test-xfer |
| | @@ -697,53 +754,67 @@ |
| 697 | 754 | xfer.nToken = blob_tokenize(&xfer.line, xfer.aToken, count(xfer.aToken)); |
| 698 | 755 | |
| 699 | 756 | /* file UUID SIZE \n CONTENT |
| 700 | 757 | ** file UUID DELTASRC SIZE \n CONTENT |
| 701 | 758 | ** |
| 702 | | - ** Receive a file transmitted from the other side |
| 759 | + ** Receive a file transmitted from the server. |
| 703 | 760 | */ |
| 704 | 761 | if( blob_eq(&xfer.aToken[0],"file") ){ |
| 705 | 762 | xfer_accept_file(&xfer); |
| 706 | 763 | }else |
| 707 | 764 | |
| 708 | 765 | /* gimme UUID |
| 709 | 766 | ** |
| 710 | | - ** Server is requesting a file |
| 767 | + ** Server is requesting a file. If the file is a manifest, assume |
| 768 | + ** that the server will also want to know all of the content files |
| 769 | + ** associated with the manifest and send those too. |
| 711 | 770 | */ |
| 712 | 771 | if( blob_eq(&xfer.aToken[0], "gimme") |
| 713 | 772 | && xfer.nToken==2 |
| 714 | 773 | && blob_is_uuid(&xfer.aToken[1]) |
| 715 | 774 | ){ |
| 716 | 775 | nMsg++; |
| 717 | 776 | if( pushFlag ){ |
| 718 | 777 | int rid = rid_from_uuid(&xfer.aToken[1], 0); |
| 719 | | - send_file(&xfer, rid, &xfer.aToken[1], 0); |
| 778 | + send_manifest(&xfer, rid, &xfer.aToken[1], 0); |
| 720 | 779 | } |
| 721 | 780 | }else |
| 722 | 781 | |
| 723 | 782 | /* igot UUID |
| 724 | 783 | ** |
| 725 | | - ** Server announces that it has a particular file |
| 784 | + ** Server announces that it has a particular file. If this is |
| 785 | + ** not a file that we have and we are pulling, then create a |
| 786 | + ** phantom to cause this file to be requested on the next cycle. |
| 787 | + ** Always remember that the server has this file so that we do |
| 788 | + ** not transmit it by accident. |
| 726 | 789 | */ |
| 727 | 790 | if( xfer.nToken==2 |
| 728 | 791 | && blob_eq(&xfer.aToken[0], "igot") |
| 729 | 792 | && blob_is_uuid(&xfer.aToken[1]) |
| 730 | 793 | ){ |
| 794 | + int rid = 0; |
| 731 | 795 | nMsg++; |
| 732 | 796 | if( pullFlag ){ |
| 733 | 797 | if( !db_exists("SELECT 1 FROM blob WHERE uuid='%b' AND size>=0", |
| 734 | 798 | &xfer.aToken[1]) ){ |
| 735 | | - content_put(0, blob_str(&xfer.aToken[1]), 0); |
| 799 | + rid = content_put(0, blob_str(&xfer.aToken[1]), 0); |
| 736 | 800 | newPhantom = 1; |
| 737 | 801 | } |
| 738 | 802 | } |
| 803 | + if( rid==0 ){ |
| 804 | + rid = rid_from_uuid(&xfer.aToken[1], 0); |
| 805 | + } |
| 806 | + remote_has(rid); |
| 739 | 807 | }else |
| 740 | 808 | |
| 741 | 809 | |
| 742 | 810 | /* leaf UUID |
| 743 | 811 | ** |
| 744 | | - ** Server announces that it has a particular manifest |
| 812 | + ** Server announces that it has a particular manifest. Send |
| 813 | + ** any children of this leaf that we have if we are pushing. |
| 814 | + ** Make the leaf a phantom if we are pulling. Remember that the |
| 815 | + ** remote end has the specified UUID. |
| 745 | 816 | */ |
| 746 | 817 | if( xfer.nToken==2 |
| 747 | 818 | && blob_eq(&xfer.aToken[0], "leaf") |
| 748 | 819 | && blob_is_uuid(&xfer.aToken[1]) |
| 749 | 820 | ){ |
| | @@ -751,19 +822,21 @@ |
| 751 | 822 | nMsg++; |
| 752 | 823 | if( pushFlag && rid ){ |
| 753 | 824 | leaf_response(&xfer, rid); |
| 754 | 825 | } |
| 755 | 826 | if( pullFlag && rid==0 ){ |
| 756 | | - content_put(0, blob_str(&xfer.aToken[1]), 0); |
| 827 | + rid = content_put(0, blob_str(&xfer.aToken[1]), 0); |
| 757 | 828 | newPhantom = 1; |
| 758 | 829 | } |
| 830 | + remote_has(rid); |
| 759 | 831 | }else |
| 760 | 832 | |
| 761 | 833 | |
| 762 | 834 | /* push SERVERCODE PRODUCTCODE |
| 763 | 835 | ** |
| 764 | | - ** Should only happen in response to a clone. |
| 836 | + ** Should only happen in response to a clone. This message tells |
| 837 | + ** the client what product to use for the new database. |
| 765 | 838 | */ |
| 766 | 839 | if( blob_eq(&xfer.aToken[0],"push") |
| 767 | 840 | && xfer.nToken==3 |
| 768 | 841 | && cloneFlag |
| 769 | 842 | && blob_is_uuid(&xfer.aToken[1]) |
| 770 | 843 | |