Fossil SCM
Hook up the new configuration transfer logic to "push" and "pull". Compiles cleanly but otherwise untested.
Commit
9d35e1dbf0a3ee60ed4d8094af5ec9e5e9000ff2
Parent
42911838825ae9c…
2 files changed
+91
-68
+33
-14
+91
-68
| --- src/configure.c | ||
| +++ src/configure.c | ||
| @@ -2,11 +2,11 @@ | ||
| 2 | 2 | ** Copyright (c) 2008 D. Richard Hipp |
| 3 | 3 | ** |
| 4 | 4 | ** This program is free software; you can redistribute it and/or |
| 5 | 5 | ** modify it under the terms of the Simplified BSD License (also |
| 6 | 6 | ** known as the "2-Clause License" or "FreeBSD License".) |
| 7 | - | |
| 7 | +** | |
| 8 | 8 | ** This program is distributed in the hope that it will be useful, |
| 9 | 9 | ** but without any warranty; without even the implied warranty of |
| 10 | 10 | ** merchantability or fitness for a particular purpose. |
| 11 | 11 | ** |
| 12 | 12 | ** Author contact information: |
| @@ -37,10 +37,11 @@ | ||
| 37 | 37 | #define CONFIGSET_ADDR 0x000020 /* The CONCEALED table */ |
| 38 | 38 | |
| 39 | 39 | #define CONFIGSET_ALL 0x0000ff /* Everything */ |
| 40 | 40 | |
| 41 | 41 | #define CONFIGSET_OVERWRITE 0x100000 /* Causes overwrite instead of merge */ |
| 42 | +#define CONFIGSET_OLDFORMAT 0x200000 /* Use the legacy format */ | |
| 42 | 43 | |
| 43 | 44 | #endif /* INTERFACE */ |
| 44 | 45 | |
| 45 | 46 | /* |
| 46 | 47 | ** Names of the configuration sets |
| @@ -48,17 +49,17 @@ | ||
| 48 | 49 | static struct { |
| 49 | 50 | const char *zName; /* Name of the configuration set */ |
| 50 | 51 | int groupMask; /* Mask for that configuration set */ |
| 51 | 52 | const char *zHelp; /* What it does */ |
| 52 | 53 | } aGroupName[] = { |
| 53 | - { "email", CONFIGSET_ADDR, "Concealed email addresses in tickets" }, | |
| 54 | - { "project", CONFIGSET_PROJ, "Project name and description" }, | |
| 55 | - { "skin", CONFIGSET_SKIN, "Web interface apparance settings" }, | |
| 56 | - { "shun", CONFIGSET_SHUN, "List of shunned artifacts" }, | |
| 57 | - { "ticket", CONFIGSET_TKT, "Ticket setup", }, | |
| 58 | - { "user", CONFIGSET_USER, "Users and privilege settings" }, | |
| 59 | - { "all", CONFIGSET_ALL, "All of the above" }, | |
| 54 | + { "/email", CONFIGSET_ADDR, "Concealed email addresses in tickets" }, | |
| 55 | + { "/project", CONFIGSET_PROJ, "Project name and description" }, | |
| 56 | + { "/skin", CONFIGSET_SKIN, "Web interface apparance settings" }, | |
| 57 | + { "/shun", CONFIGSET_SHUN, "List of shunned artifacts" }, | |
| 58 | + { "/ticket", CONFIGSET_TKT, "Ticket setup", }, | |
| 59 | + { "/user", CONFIGSET_USER, "Users and privilege settings" }, | |
| 60 | + { "/all", CONFIGSET_ALL, "All of the above" }, | |
| 60 | 61 | }; |
| 61 | 62 | |
| 62 | 63 | |
| 63 | 64 | /* |
| 64 | 65 | ** The following is a list of settings that we are willing to |
| @@ -108,15 +109,29 @@ | ||
| 108 | 109 | const char *configure_first_name(int iMask){ |
| 109 | 110 | iConfig = 0; |
| 110 | 111 | return configure_next_name(iMask); |
| 111 | 112 | } |
| 112 | 113 | const char *configure_next_name(int iMask){ |
| 113 | - while( iConfig<count(aConfig) ){ | |
| 114 | - if( aConfig[iConfig].groupMask & iMask ){ | |
| 115 | - return aConfig[iConfig++].zName; | |
| 116 | - }else{ | |
| 117 | - iConfig++; | |
| 114 | + if( iMask & CONFIGSET_OLDFORMAT ){ | |
| 115 | + while( iConfig<count(aConfig) ){ | |
| 116 | + if( aConfig[iConfig].groupMask & iMask ){ | |
| 117 | + return aConfig[iConfig++].zName; | |
| 118 | + }else{ | |
| 119 | + iConfig++; | |
| 120 | + } | |
| 121 | + } | |
| 122 | + }else{ | |
| 123 | + if( iConfig==0 && (iMask & CONFIGSET_ALL)==CONFIGSET_ALL ){ | |
| 124 | + iConfig = count(aGroupName); | |
| 125 | + return "/all"; | |
| 126 | + } | |
| 127 | + while( iConfig<count(aGroupName)-1 ){ | |
| 128 | + if( aGroupName[iConfig].groupMask & iMask ){ | |
| 129 | + return aGroupName[iConfig++].zName; | |
| 130 | + }else{ | |
| 131 | + iConfig++; | |
| 132 | + } | |
| 118 | 133 | } |
| 119 | 134 | } |
| 120 | 135 | return 0; |
| 121 | 136 | } |
| 122 | 137 | |
| @@ -304,10 +319,26 @@ | ||
| 304 | 319 | config_reset_function, 0, 0); |
| 305 | 320 | configHasBeenReset = 0; |
| 306 | 321 | db_multi_exec(zSQL2); |
| 307 | 322 | } |
| 308 | 323 | } |
| 324 | + | |
| 325 | +/* | |
| 326 | +** After receiving configuration data, call this routine to transfer | |
| 327 | +** the results into the main database. | |
| 328 | +*/ | |
| 329 | +void configure_finalize_receive(void){ | |
| 330 | + static const char zSQL[] = | |
| 331 | + @ DELETE FROM user; | |
| 332 | + @ INSERT INTO user SELECT * FROM _xfer_user; | |
| 333 | + @ DELETE FROM reportfmt; | |
| 334 | + @ INSERT INTO reportfmt SELECT * FROM _xfer_reportfmt; | |
| 335 | + @ DROP TABLE _xfer_user; | |
| 336 | + @ DROP TABLE _xfer_reportfmt; | |
| 337 | + ; | |
| 338 | + db_multi_exec(zSQL); | |
| 339 | +} | |
| 309 | 340 | |
| 310 | 341 | /* |
| 311 | 342 | ** Return true if z[] is not a "safe" SQL token. A safe token is one of: |
| 312 | 343 | ** |
| 313 | 344 | ** * A string literal |
| @@ -534,19 +565,22 @@ | ||
| 534 | 565 | ** Send "config" cards using the new format for all elements of a group |
| 535 | 566 | ** that have recently changed. |
| 536 | 567 | ** |
| 537 | 568 | ** Output goes into pOut. The groupMask identifies the group(s) to be sent. |
| 538 | 569 | ** Send only entries whose timestamp is later than or equal to iStart. |
| 570 | +** | |
| 571 | +** Return the number of cards sent. | |
| 539 | 572 | */ |
| 540 | -void configure_send_group( | |
| 573 | +int configure_send_group( | |
| 541 | 574 | Blob *pOut, /* Write output here */ |
| 542 | 575 | int groupMask, /* Mask of groups to be send */ |
| 543 | 576 | sqlite3_int64 iStart /* Only write values changed since this time */ |
| 544 | 577 | ){ |
| 545 | 578 | Stmt q; |
| 546 | 579 | Blob rec; |
| 547 | 580 | int ii; |
| 581 | + int nCard = 0; | |
| 548 | 582 | |
| 549 | 583 | blob_zero(&rec); |
| 550 | 584 | if( groupMask & CONFIGSET_SHUN ){ |
| 551 | 585 | db_prepare(&q, "SELECT mtime, quote(uuid), quote(scom) FROM shun" |
| 552 | 586 | " WHERE mtime>=%lld", iStart); |
| @@ -556,10 +590,11 @@ | ||
| 556 | 590 | db_column_text(&q, 1), |
| 557 | 591 | db_column_text(&q, 2) |
| 558 | 592 | ); |
| 559 | 593 | blob_appendf(pOut, "config /shun %d\n%s", |
| 560 | 594 | blob_size(&rec), blob_str(&rec)); |
| 595 | + nCard++; | |
| 561 | 596 | blob_reset(&rec); |
| 562 | 597 | } |
| 563 | 598 | db_finalize(&q); |
| 564 | 599 | } |
| 565 | 600 | if( groupMask & CONFIGSET_USER ){ |
| @@ -575,10 +610,11 @@ | ||
| 575 | 610 | db_column_text(&q, 4), |
| 576 | 611 | db_column_text(&q, 5) |
| 577 | 612 | ); |
| 578 | 613 | blob_appendf(pOut, "config /user %d\n%s", |
| 579 | 614 | blob_size(&rec), blob_str(&rec)); |
| 615 | + nCard++; | |
| 580 | 616 | blob_reset(&rec); |
| 581 | 617 | } |
| 582 | 618 | db_finalize(&q); |
| 583 | 619 | } |
| 584 | 620 | if( groupMask & CONFIGSET_TKT ){ |
| @@ -593,10 +629,11 @@ | ||
| 593 | 629 | db_column_text(&q, 3), |
| 594 | 630 | db_column_text(&q, 4) |
| 595 | 631 | ); |
| 596 | 632 | blob_appendf(pOut, "config /reportfmt %d\n%s", |
| 597 | 633 | blob_size(&rec), blob_str(&rec)); |
| 634 | + nCard++; | |
| 598 | 635 | blob_reset(&rec); |
| 599 | 636 | } |
| 600 | 637 | db_finalize(&q); |
| 601 | 638 | } |
| 602 | 639 | if( groupMask & CONFIGSET_ADDR ){ |
| @@ -608,10 +645,11 @@ | ||
| 608 | 645 | db_column_text(&q, 1), |
| 609 | 646 | db_column_text(&q, 2) |
| 610 | 647 | ); |
| 611 | 648 | blob_appendf(pOut, "config /concealed %d\n%s", |
| 612 | 649 | blob_size(&rec), blob_str(&rec)); |
| 650 | + nCard++; | |
| 613 | 651 | blob_reset(&rec); |
| 614 | 652 | } |
| 615 | 653 | db_finalize(&q); |
| 616 | 654 | } |
| 617 | 655 | db_prepare(&q, "SELECT mtime, quote(name), quote(value) FROM config" |
| @@ -625,73 +663,39 @@ | ||
| 625 | 663 | db_column_text(&q, 1), |
| 626 | 664 | db_column_text(&q, 2) |
| 627 | 665 | ); |
| 628 | 666 | blob_appendf(pOut, "config /config %d\n%s", |
| 629 | 667 | blob_size(&rec), blob_str(&rec)); |
| 668 | + nCard++; | |
| 630 | 669 | blob_reset(&rec); |
| 631 | 670 | } |
| 632 | 671 | db_reset(&q); |
| 633 | 672 | } |
| 634 | 673 | } |
| 635 | 674 | db_finalize(&q); |
| 636 | -} | |
| 637 | - | |
| 638 | -/* | |
| 639 | -** COMMAND: test-config-send | |
| 640 | -** | |
| 641 | -** Usage: %fossil test-config-send GROUP START | |
| 642 | -*/ | |
| 643 | -void test_config_send(void){ | |
| 644 | - Blob out; | |
| 645 | - sqlite3_int64 iStart; | |
| 646 | - int i; | |
| 647 | - blob_zero(&out); | |
| 648 | - db_find_and_open_repository(0,0); | |
| 649 | - if( g.argc!=4 ) usage("GROUP START"); | |
| 650 | - iStart = db_int64(0, "SELECT strftime('%%s',%Q)", g.argv[3]); | |
| 651 | - for(i=0; i<count(aGroupName); i++){ | |
| 652 | - if( fossil_strcmp(g.argv[2], aGroupName[i].zName)==0 ){ | |
| 653 | - configure_send_group(&out, aGroupName[i].groupMask, iStart); | |
| 654 | - printf("%s", blob_str(&out)); | |
| 655 | - blob_reset(&out); | |
| 656 | - } | |
| 657 | - } | |
| 658 | -} | |
| 659 | - | |
| 660 | -/* | |
| 661 | -** After receiving configuration data, call this routine to transfer | |
| 662 | -** the results into the main database. | |
| 663 | -*/ | |
| 664 | -void configure_finalize_receive(void){ | |
| 665 | - static const char zSQL[] = | |
| 666 | - @ DELETE FROM user; | |
| 667 | - @ INSERT INTO user SELECT * FROM _xfer_user; | |
| 668 | - @ DELETE FROM reportfmt; | |
| 669 | - @ INSERT INTO reportfmt SELECT * FROM _xfer_reportfmt; | |
| 670 | - @ DROP TABLE _xfer_user; | |
| 671 | - @ DROP TABLE _xfer_reportfmt; | |
| 672 | - ; | |
| 673 | - db_multi_exec(zSQL); | |
| 675 | + return nCard; | |
| 674 | 676 | } |
| 675 | 677 | |
| 676 | 678 | /* |
| 677 | 679 | ** Identify a configuration group by name. Return its mask. |
| 678 | 680 | ** Throw an error if no match. |
| 679 | 681 | */ |
| 680 | -static int find_area(const char *z){ | |
| 682 | +int configure_name_to_mask(const char *z, int notFoundIsFatal){ | |
| 681 | 683 | int i; |
| 682 | 684 | int n = strlen(z); |
| 683 | 685 | for(i=0; i<count(aGroupName); i++){ |
| 684 | - if( strncmp(z, aGroupName[i].zName, n)==0 ){ | |
| 686 | + if( strncmp(z, &aGroupName[i].zName[1], n)==0 ){ | |
| 685 | 687 | return aGroupName[i].groupMask; |
| 686 | 688 | } |
| 687 | 689 | } |
| 688 | - printf("Available configuration areas:\n"); | |
| 689 | - for(i=0; i<count(aGroupName); i++){ | |
| 690 | - printf(" %-10s %s\n", aGroupName[i].zName, aGroupName[i].zHelp); | |
| 690 | + if( notFoundIsFatal ){ | |
| 691 | + printf("Available configuration areas:\n"); | |
| 692 | + for(i=0; i<count(aGroupName); i++){ | |
| 693 | + printf(" %-10s %s\n", &aGroupName[i].zName[1], aGroupName[i].zHelp); | |
| 694 | + } | |
| 695 | + fossil_fatal("no such configuration area: \"%s\"", z); | |
| 691 | 696 | } |
| 692 | - fossil_fatal("no such configuration area: \"%s\"", z); | |
| 693 | 697 | return 0; |
| 694 | 698 | } |
| 695 | 699 | |
| 696 | 700 | /* |
| 697 | 701 | ** Write SQL text into file zFilename that will restore the configuration |
| @@ -744,25 +748,31 @@ | ||
| 744 | 748 | ** |
| 745 | 749 | ** %fossil configuration pull AREA ?URL? |
| 746 | 750 | ** |
| 747 | 751 | ** Pull and install the configuration from a different server |
| 748 | 752 | ** identified by URL. If no URL is specified, then the default |
| 749 | -** server is used. | |
| 753 | +** server is used. Use the --legacy option for the older protocol | |
| 754 | +** (when talking to servers compiled prior to 2011-04-27.) Use | |
| 755 | +** the --overwrite flag to completely replace local settings with | |
| 756 | +** content received from URL. | |
| 750 | 757 | ** |
| 751 | 758 | ** %fossil configuration push AREA ?URL? |
| 752 | 759 | ** |
| 753 | 760 | ** Push the local configuration into the remote server identified |
| 754 | 761 | ** by URL. Admin privilege is required on the remote server for |
| 755 | -** this to work. | |
| 762 | +** this to work. When the same record exists both locally and on | |
| 763 | +** the remote end, the one that was most recently changed wins. | |
| 764 | +** Use the --legacy flag when talking to holder servers. | |
| 756 | 765 | ** |
| 757 | 766 | ** %fossil configuration reset AREA |
| 758 | 767 | ** |
| 759 | 768 | ** Restore the configuration to the default. AREA as above. |
| 760 | 769 | ** |
| 761 | -** WARNING: Do not import, merge, or pull configurations from an untrusted | |
| 762 | -** source. The inbound configuration is not checked for safety and can | |
| 763 | -** introduce security vulnerabilities. | |
| 770 | +** %fossil configuration sync AREA ?URL? | |
| 771 | +** | |
| 772 | +** Synchronize configuration changes in the local repository with | |
| 773 | +** the remote repository at URL. | |
| 764 | 774 | */ |
| 765 | 775 | void configuration_cmd(void){ |
| 766 | 776 | int n; |
| 767 | 777 | const char *zMethod; |
| 768 | 778 | if( g.argc<3 ){ |
| @@ -776,11 +786,11 @@ | ||
| 776 | 786 | const char *zSince = find_option("since",0,1); |
| 777 | 787 | sqlite3_int64 iStart; |
| 778 | 788 | if( g.argc!=5 ){ |
| 779 | 789 | usage("export AREA FILENAME"); |
| 780 | 790 | } |
| 781 | - mask = find_area(g.argv[3]); | |
| 791 | + mask = configure_name_to_mask(g.argv[3], 1); | |
| 782 | 792 | if( zSince ){ |
| 783 | 793 | iStart = db_multi_exec( |
| 784 | 794 | "SELECT coalesce(strftime('%%s',%Q),strftime('%%s','now',%Q))+0", |
| 785 | 795 | zSince, zSince |
| 786 | 796 | ); |
| @@ -802,19 +812,28 @@ | ||
| 802 | 812 | groupMask = CONFIGSET_ALL; |
| 803 | 813 | } |
| 804 | 814 | configure_receive_all(&in, groupMask); |
| 805 | 815 | db_end_transaction(0); |
| 806 | 816 | }else |
| 807 | - if( strncmp(zMethod, "pull", n)==0 || strncmp(zMethod, "push", n)==0 ){ | |
| 817 | + if( strncmp(zMethod, "pull", n)==0 | |
| 818 | + || strncmp(zMethod, "push", n)==0 | |
| 819 | + || strncmp(zMethod, "sync", n)==0 | |
| 820 | + ){ | |
| 808 | 821 | int mask; |
| 809 | 822 | const char *zServer; |
| 810 | 823 | const char *zPw; |
| 824 | + int legacyFlag = 0; | |
| 825 | + int overwriteFlag = 0; | |
| 826 | + if( zMethod[0]!='s' ) legacyFlag = find_option("legacy",0,0)!=0; | |
| 827 | + if( strncmp(zMethod,"pull",n)==0 ){ | |
| 828 | + overwriteFlag = find_option("overwrite",0,0)!=0; | |
| 829 | + } | |
| 811 | 830 | url_proxy_options(); |
| 812 | 831 | if( g.argc!=4 && g.argc!=5 ){ |
| 813 | 832 | usage("pull AREA ?URL?"); |
| 814 | 833 | } |
| 815 | - mask = find_area(g.argv[3]); | |
| 834 | + mask = configure_name_to_mask(g.argv[3], 1); | |
| 816 | 835 | if( g.argc==5 ){ |
| 817 | 836 | zServer = g.argv[4]; |
| 818 | 837 | zPw = 0; |
| 819 | 838 | g.dontKeepUrl = 1; |
| 820 | 839 | }else{ |
| @@ -826,21 +845,25 @@ | ||
| 826 | 845 | } |
| 827 | 846 | url_parse(zServer); |
| 828 | 847 | if( g.urlPasswd==0 && zPw ) g.urlPasswd = mprintf("%s", zPw); |
| 829 | 848 | user_select(); |
| 830 | 849 | url_enable_proxy("via proxy: "); |
| 850 | + if( legacyFlag ) mask |= CONFIGSET_OLDFORMAT; | |
| 851 | + if( overwriteFlag ) mask |= CONFIGSET_OVERWRITE; | |
| 831 | 852 | if( strncmp(zMethod, "push", n)==0 ){ |
| 832 | 853 | client_sync(0,0,0,0,0,mask); |
| 833 | - }else{ | |
| 854 | + }else if( strncmp(zMethod, "pull", n)==0 ){ | |
| 834 | 855 | client_sync(0,0,0,0,mask,0); |
| 856 | + }else{ | |
| 857 | + client_sync(0,0,0,0,mask,mask); | |
| 835 | 858 | } |
| 836 | 859 | }else |
| 837 | 860 | if( strncmp(zMethod, "reset", n)==0 ){ |
| 838 | 861 | int mask, i; |
| 839 | 862 | char *zBackup; |
| 840 | 863 | if( g.argc!=4 ) usage("reset AREA"); |
| 841 | - mask = find_area(g.argv[3]); | |
| 864 | + mask = configure_name_to_mask(g.argv[3], 1); | |
| 842 | 865 | zBackup = db_text(0, |
| 843 | 866 | "SELECT strftime('config-backup-%%Y%%m%%d%%H%%M%%f','now')"); |
| 844 | 867 | db_begin_transaction(); |
| 845 | 868 | export_config(mask, g.argv[3], 0, zBackup); |
| 846 | 869 | for(i=0; i<count(aConfig); i++){ |
| 847 | 870 |
| --- src/configure.c | |
| +++ src/configure.c | |
| @@ -2,11 +2,11 @@ | |
| 2 | ** Copyright (c) 2008 D. Richard Hipp |
| 3 | ** |
| 4 | ** This program is free software; you can redistribute it and/or |
| 5 | ** modify it under the terms of the Simplified BSD License (also |
| 6 | ** known as the "2-Clause License" or "FreeBSD License".) |
| 7 | |
| 8 | ** This program is distributed in the hope that it will be useful, |
| 9 | ** but without any warranty; without even the implied warranty of |
| 10 | ** merchantability or fitness for a particular purpose. |
| 11 | ** |
| 12 | ** Author contact information: |
| @@ -37,10 +37,11 @@ | |
| 37 | #define CONFIGSET_ADDR 0x000020 /* The CONCEALED table */ |
| 38 | |
| 39 | #define CONFIGSET_ALL 0x0000ff /* Everything */ |
| 40 | |
| 41 | #define CONFIGSET_OVERWRITE 0x100000 /* Causes overwrite instead of merge */ |
| 42 | |
| 43 | #endif /* INTERFACE */ |
| 44 | |
| 45 | /* |
| 46 | ** Names of the configuration sets |
| @@ -48,17 +49,17 @@ | |
| 48 | static struct { |
| 49 | const char *zName; /* Name of the configuration set */ |
| 50 | int groupMask; /* Mask for that configuration set */ |
| 51 | const char *zHelp; /* What it does */ |
| 52 | } aGroupName[] = { |
| 53 | { "email", CONFIGSET_ADDR, "Concealed email addresses in tickets" }, |
| 54 | { "project", CONFIGSET_PROJ, "Project name and description" }, |
| 55 | { "skin", CONFIGSET_SKIN, "Web interface apparance settings" }, |
| 56 | { "shun", CONFIGSET_SHUN, "List of shunned artifacts" }, |
| 57 | { "ticket", CONFIGSET_TKT, "Ticket setup", }, |
| 58 | { "user", CONFIGSET_USER, "Users and privilege settings" }, |
| 59 | { "all", CONFIGSET_ALL, "All of the above" }, |
| 60 | }; |
| 61 | |
| 62 | |
| 63 | /* |
| 64 | ** The following is a list of settings that we are willing to |
| @@ -108,15 +109,29 @@ | |
| 108 | const char *configure_first_name(int iMask){ |
| 109 | iConfig = 0; |
| 110 | return configure_next_name(iMask); |
| 111 | } |
| 112 | const char *configure_next_name(int iMask){ |
| 113 | while( iConfig<count(aConfig) ){ |
| 114 | if( aConfig[iConfig].groupMask & iMask ){ |
| 115 | return aConfig[iConfig++].zName; |
| 116 | }else{ |
| 117 | iConfig++; |
| 118 | } |
| 119 | } |
| 120 | return 0; |
| 121 | } |
| 122 | |
| @@ -304,10 +319,26 @@ | |
| 304 | config_reset_function, 0, 0); |
| 305 | configHasBeenReset = 0; |
| 306 | db_multi_exec(zSQL2); |
| 307 | } |
| 308 | } |
| 309 | |
| 310 | /* |
| 311 | ** Return true if z[] is not a "safe" SQL token. A safe token is one of: |
| 312 | ** |
| 313 | ** * A string literal |
| @@ -534,19 +565,22 @@ | |
| 534 | ** Send "config" cards using the new format for all elements of a group |
| 535 | ** that have recently changed. |
| 536 | ** |
| 537 | ** Output goes into pOut. The groupMask identifies the group(s) to be sent. |
| 538 | ** Send only entries whose timestamp is later than or equal to iStart. |
| 539 | */ |
| 540 | void configure_send_group( |
| 541 | Blob *pOut, /* Write output here */ |
| 542 | int groupMask, /* Mask of groups to be send */ |
| 543 | sqlite3_int64 iStart /* Only write values changed since this time */ |
| 544 | ){ |
| 545 | Stmt q; |
| 546 | Blob rec; |
| 547 | int ii; |
| 548 | |
| 549 | blob_zero(&rec); |
| 550 | if( groupMask & CONFIGSET_SHUN ){ |
| 551 | db_prepare(&q, "SELECT mtime, quote(uuid), quote(scom) FROM shun" |
| 552 | " WHERE mtime>=%lld", iStart); |
| @@ -556,10 +590,11 @@ | |
| 556 | db_column_text(&q, 1), |
| 557 | db_column_text(&q, 2) |
| 558 | ); |
| 559 | blob_appendf(pOut, "config /shun %d\n%s", |
| 560 | blob_size(&rec), blob_str(&rec)); |
| 561 | blob_reset(&rec); |
| 562 | } |
| 563 | db_finalize(&q); |
| 564 | } |
| 565 | if( groupMask & CONFIGSET_USER ){ |
| @@ -575,10 +610,11 @@ | |
| 575 | db_column_text(&q, 4), |
| 576 | db_column_text(&q, 5) |
| 577 | ); |
| 578 | blob_appendf(pOut, "config /user %d\n%s", |
| 579 | blob_size(&rec), blob_str(&rec)); |
| 580 | blob_reset(&rec); |
| 581 | } |
| 582 | db_finalize(&q); |
| 583 | } |
| 584 | if( groupMask & CONFIGSET_TKT ){ |
| @@ -593,10 +629,11 @@ | |
| 593 | db_column_text(&q, 3), |
| 594 | db_column_text(&q, 4) |
| 595 | ); |
| 596 | blob_appendf(pOut, "config /reportfmt %d\n%s", |
| 597 | blob_size(&rec), blob_str(&rec)); |
| 598 | blob_reset(&rec); |
| 599 | } |
| 600 | db_finalize(&q); |
| 601 | } |
| 602 | if( groupMask & CONFIGSET_ADDR ){ |
| @@ -608,10 +645,11 @@ | |
| 608 | db_column_text(&q, 1), |
| 609 | db_column_text(&q, 2) |
| 610 | ); |
| 611 | blob_appendf(pOut, "config /concealed %d\n%s", |
| 612 | blob_size(&rec), blob_str(&rec)); |
| 613 | blob_reset(&rec); |
| 614 | } |
| 615 | db_finalize(&q); |
| 616 | } |
| 617 | db_prepare(&q, "SELECT mtime, quote(name), quote(value) FROM config" |
| @@ -625,73 +663,39 @@ | |
| 625 | db_column_text(&q, 1), |
| 626 | db_column_text(&q, 2) |
| 627 | ); |
| 628 | blob_appendf(pOut, "config /config %d\n%s", |
| 629 | blob_size(&rec), blob_str(&rec)); |
| 630 | blob_reset(&rec); |
| 631 | } |
| 632 | db_reset(&q); |
| 633 | } |
| 634 | } |
| 635 | db_finalize(&q); |
| 636 | } |
| 637 | |
| 638 | /* |
| 639 | ** COMMAND: test-config-send |
| 640 | ** |
| 641 | ** Usage: %fossil test-config-send GROUP START |
| 642 | */ |
| 643 | void test_config_send(void){ |
| 644 | Blob out; |
| 645 | sqlite3_int64 iStart; |
| 646 | int i; |
| 647 | blob_zero(&out); |
| 648 | db_find_and_open_repository(0,0); |
| 649 | if( g.argc!=4 ) usage("GROUP START"); |
| 650 | iStart = db_int64(0, "SELECT strftime('%%s',%Q)", g.argv[3]); |
| 651 | for(i=0; i<count(aGroupName); i++){ |
| 652 | if( fossil_strcmp(g.argv[2], aGroupName[i].zName)==0 ){ |
| 653 | configure_send_group(&out, aGroupName[i].groupMask, iStart); |
| 654 | printf("%s", blob_str(&out)); |
| 655 | blob_reset(&out); |
| 656 | } |
| 657 | } |
| 658 | } |
| 659 | |
| 660 | /* |
| 661 | ** After receiving configuration data, call this routine to transfer |
| 662 | ** the results into the main database. |
| 663 | */ |
| 664 | void configure_finalize_receive(void){ |
| 665 | static const char zSQL[] = |
| 666 | @ DELETE FROM user; |
| 667 | @ INSERT INTO user SELECT * FROM _xfer_user; |
| 668 | @ DELETE FROM reportfmt; |
| 669 | @ INSERT INTO reportfmt SELECT * FROM _xfer_reportfmt; |
| 670 | @ DROP TABLE _xfer_user; |
| 671 | @ DROP TABLE _xfer_reportfmt; |
| 672 | ; |
| 673 | db_multi_exec(zSQL); |
| 674 | } |
| 675 | |
| 676 | /* |
| 677 | ** Identify a configuration group by name. Return its mask. |
| 678 | ** Throw an error if no match. |
| 679 | */ |
| 680 | static int find_area(const char *z){ |
| 681 | int i; |
| 682 | int n = strlen(z); |
| 683 | for(i=0; i<count(aGroupName); i++){ |
| 684 | if( strncmp(z, aGroupName[i].zName, n)==0 ){ |
| 685 | return aGroupName[i].groupMask; |
| 686 | } |
| 687 | } |
| 688 | printf("Available configuration areas:\n"); |
| 689 | for(i=0; i<count(aGroupName); i++){ |
| 690 | printf(" %-10s %s\n", aGroupName[i].zName, aGroupName[i].zHelp); |
| 691 | } |
| 692 | fossil_fatal("no such configuration area: \"%s\"", z); |
| 693 | return 0; |
| 694 | } |
| 695 | |
| 696 | /* |
| 697 | ** Write SQL text into file zFilename that will restore the configuration |
| @@ -744,25 +748,31 @@ | |
| 744 | ** |
| 745 | ** %fossil configuration pull AREA ?URL? |
| 746 | ** |
| 747 | ** Pull and install the configuration from a different server |
| 748 | ** identified by URL. If no URL is specified, then the default |
| 749 | ** server is used. |
| 750 | ** |
| 751 | ** %fossil configuration push AREA ?URL? |
| 752 | ** |
| 753 | ** Push the local configuration into the remote server identified |
| 754 | ** by URL. Admin privilege is required on the remote server for |
| 755 | ** this to work. |
| 756 | ** |
| 757 | ** %fossil configuration reset AREA |
| 758 | ** |
| 759 | ** Restore the configuration to the default. AREA as above. |
| 760 | ** |
| 761 | ** WARNING: Do not import, merge, or pull configurations from an untrusted |
| 762 | ** source. The inbound configuration is not checked for safety and can |
| 763 | ** introduce security vulnerabilities. |
| 764 | */ |
| 765 | void configuration_cmd(void){ |
| 766 | int n; |
| 767 | const char *zMethod; |
| 768 | if( g.argc<3 ){ |
| @@ -776,11 +786,11 @@ | |
| 776 | const char *zSince = find_option("since",0,1); |
| 777 | sqlite3_int64 iStart; |
| 778 | if( g.argc!=5 ){ |
| 779 | usage("export AREA FILENAME"); |
| 780 | } |
| 781 | mask = find_area(g.argv[3]); |
| 782 | if( zSince ){ |
| 783 | iStart = db_multi_exec( |
| 784 | "SELECT coalesce(strftime('%%s',%Q),strftime('%%s','now',%Q))+0", |
| 785 | zSince, zSince |
| 786 | ); |
| @@ -802,19 +812,28 @@ | |
| 802 | groupMask = CONFIGSET_ALL; |
| 803 | } |
| 804 | configure_receive_all(&in, groupMask); |
| 805 | db_end_transaction(0); |
| 806 | }else |
| 807 | if( strncmp(zMethod, "pull", n)==0 || strncmp(zMethod, "push", n)==0 ){ |
| 808 | int mask; |
| 809 | const char *zServer; |
| 810 | const char *zPw; |
| 811 | url_proxy_options(); |
| 812 | if( g.argc!=4 && g.argc!=5 ){ |
| 813 | usage("pull AREA ?URL?"); |
| 814 | } |
| 815 | mask = find_area(g.argv[3]); |
| 816 | if( g.argc==5 ){ |
| 817 | zServer = g.argv[4]; |
| 818 | zPw = 0; |
| 819 | g.dontKeepUrl = 1; |
| 820 | }else{ |
| @@ -826,21 +845,25 @@ | |
| 826 | } |
| 827 | url_parse(zServer); |
| 828 | if( g.urlPasswd==0 && zPw ) g.urlPasswd = mprintf("%s", zPw); |
| 829 | user_select(); |
| 830 | url_enable_proxy("via proxy: "); |
| 831 | if( strncmp(zMethod, "push", n)==0 ){ |
| 832 | client_sync(0,0,0,0,0,mask); |
| 833 | }else{ |
| 834 | client_sync(0,0,0,0,mask,0); |
| 835 | } |
| 836 | }else |
| 837 | if( strncmp(zMethod, "reset", n)==0 ){ |
| 838 | int mask, i; |
| 839 | char *zBackup; |
| 840 | if( g.argc!=4 ) usage("reset AREA"); |
| 841 | mask = find_area(g.argv[3]); |
| 842 | zBackup = db_text(0, |
| 843 | "SELECT strftime('config-backup-%%Y%%m%%d%%H%%M%%f','now')"); |
| 844 | db_begin_transaction(); |
| 845 | export_config(mask, g.argv[3], 0, zBackup); |
| 846 | for(i=0; i<count(aConfig); i++){ |
| 847 |
| --- src/configure.c | |
| +++ src/configure.c | |
| @@ -2,11 +2,11 @@ | |
| 2 | ** Copyright (c) 2008 D. Richard Hipp |
| 3 | ** |
| 4 | ** This program is free software; you can redistribute it and/or |
| 5 | ** modify it under the terms of the Simplified BSD License (also |
| 6 | ** known as the "2-Clause License" or "FreeBSD License".) |
| 7 | ** |
| 8 | ** This program is distributed in the hope that it will be useful, |
| 9 | ** but without any warranty; without even the implied warranty of |
| 10 | ** merchantability or fitness for a particular purpose. |
| 11 | ** |
| 12 | ** Author contact information: |
| @@ -37,10 +37,11 @@ | |
| 37 | #define CONFIGSET_ADDR 0x000020 /* The CONCEALED table */ |
| 38 | |
| 39 | #define CONFIGSET_ALL 0x0000ff /* Everything */ |
| 40 | |
| 41 | #define CONFIGSET_OVERWRITE 0x100000 /* Causes overwrite instead of merge */ |
| 42 | #define CONFIGSET_OLDFORMAT 0x200000 /* Use the legacy format */ |
| 43 | |
| 44 | #endif /* INTERFACE */ |
| 45 | |
| 46 | /* |
| 47 | ** Names of the configuration sets |
| @@ -48,17 +49,17 @@ | |
| 49 | static struct { |
| 50 | const char *zName; /* Name of the configuration set */ |
| 51 | int groupMask; /* Mask for that configuration set */ |
| 52 | const char *zHelp; /* What it does */ |
| 53 | } aGroupName[] = { |
| 54 | { "/email", CONFIGSET_ADDR, "Concealed email addresses in tickets" }, |
| 55 | { "/project", CONFIGSET_PROJ, "Project name and description" }, |
| 56 | { "/skin", CONFIGSET_SKIN, "Web interface apparance settings" }, |
| 57 | { "/shun", CONFIGSET_SHUN, "List of shunned artifacts" }, |
| 58 | { "/ticket", CONFIGSET_TKT, "Ticket setup", }, |
| 59 | { "/user", CONFIGSET_USER, "Users and privilege settings" }, |
| 60 | { "/all", CONFIGSET_ALL, "All of the above" }, |
| 61 | }; |
| 62 | |
| 63 | |
| 64 | /* |
| 65 | ** The following is a list of settings that we are willing to |
| @@ -108,15 +109,29 @@ | |
| 109 | const char *configure_first_name(int iMask){ |
| 110 | iConfig = 0; |
| 111 | return configure_next_name(iMask); |
| 112 | } |
| 113 | const char *configure_next_name(int iMask){ |
| 114 | if( iMask & CONFIGSET_OLDFORMAT ){ |
| 115 | while( iConfig<count(aConfig) ){ |
| 116 | if( aConfig[iConfig].groupMask & iMask ){ |
| 117 | return aConfig[iConfig++].zName; |
| 118 | }else{ |
| 119 | iConfig++; |
| 120 | } |
| 121 | } |
| 122 | }else{ |
| 123 | if( iConfig==0 && (iMask & CONFIGSET_ALL)==CONFIGSET_ALL ){ |
| 124 | iConfig = count(aGroupName); |
| 125 | return "/all"; |
| 126 | } |
| 127 | while( iConfig<count(aGroupName)-1 ){ |
| 128 | if( aGroupName[iConfig].groupMask & iMask ){ |
| 129 | return aGroupName[iConfig++].zName; |
| 130 | }else{ |
| 131 | iConfig++; |
| 132 | } |
| 133 | } |
| 134 | } |
| 135 | return 0; |
| 136 | } |
| 137 | |
| @@ -304,10 +319,26 @@ | |
| 319 | config_reset_function, 0, 0); |
| 320 | configHasBeenReset = 0; |
| 321 | db_multi_exec(zSQL2); |
| 322 | } |
| 323 | } |
| 324 | |
| 325 | /* |
| 326 | ** After receiving configuration data, call this routine to transfer |
| 327 | ** the results into the main database. |
| 328 | */ |
| 329 | void configure_finalize_receive(void){ |
| 330 | static const char zSQL[] = |
| 331 | @ DELETE FROM user; |
| 332 | @ INSERT INTO user SELECT * FROM _xfer_user; |
| 333 | @ DELETE FROM reportfmt; |
| 334 | @ INSERT INTO reportfmt SELECT * FROM _xfer_reportfmt; |
| 335 | @ DROP TABLE _xfer_user; |
| 336 | @ DROP TABLE _xfer_reportfmt; |
| 337 | ; |
| 338 | db_multi_exec(zSQL); |
| 339 | } |
| 340 | |
| 341 | /* |
| 342 | ** Return true if z[] is not a "safe" SQL token. A safe token is one of: |
| 343 | ** |
| 344 | ** * A string literal |
| @@ -534,19 +565,22 @@ | |
| 565 | ** Send "config" cards using the new format for all elements of a group |
| 566 | ** that have recently changed. |
| 567 | ** |
| 568 | ** Output goes into pOut. The groupMask identifies the group(s) to be sent. |
| 569 | ** Send only entries whose timestamp is later than or equal to iStart. |
| 570 | ** |
| 571 | ** Return the number of cards sent. |
| 572 | */ |
| 573 | int configure_send_group( |
| 574 | Blob *pOut, /* Write output here */ |
| 575 | int groupMask, /* Mask of groups to be send */ |
| 576 | sqlite3_int64 iStart /* Only write values changed since this time */ |
| 577 | ){ |
| 578 | Stmt q; |
| 579 | Blob rec; |
| 580 | int ii; |
| 581 | int nCard = 0; |
| 582 | |
| 583 | blob_zero(&rec); |
| 584 | if( groupMask & CONFIGSET_SHUN ){ |
| 585 | db_prepare(&q, "SELECT mtime, quote(uuid), quote(scom) FROM shun" |
| 586 | " WHERE mtime>=%lld", iStart); |
| @@ -556,10 +590,11 @@ | |
| 590 | db_column_text(&q, 1), |
| 591 | db_column_text(&q, 2) |
| 592 | ); |
| 593 | blob_appendf(pOut, "config /shun %d\n%s", |
| 594 | blob_size(&rec), blob_str(&rec)); |
| 595 | nCard++; |
| 596 | blob_reset(&rec); |
| 597 | } |
| 598 | db_finalize(&q); |
| 599 | } |
| 600 | if( groupMask & CONFIGSET_USER ){ |
| @@ -575,10 +610,11 @@ | |
| 610 | db_column_text(&q, 4), |
| 611 | db_column_text(&q, 5) |
| 612 | ); |
| 613 | blob_appendf(pOut, "config /user %d\n%s", |
| 614 | blob_size(&rec), blob_str(&rec)); |
| 615 | nCard++; |
| 616 | blob_reset(&rec); |
| 617 | } |
| 618 | db_finalize(&q); |
| 619 | } |
| 620 | if( groupMask & CONFIGSET_TKT ){ |
| @@ -593,10 +629,11 @@ | |
| 629 | db_column_text(&q, 3), |
| 630 | db_column_text(&q, 4) |
| 631 | ); |
| 632 | blob_appendf(pOut, "config /reportfmt %d\n%s", |
| 633 | blob_size(&rec), blob_str(&rec)); |
| 634 | nCard++; |
| 635 | blob_reset(&rec); |
| 636 | } |
| 637 | db_finalize(&q); |
| 638 | } |
| 639 | if( groupMask & CONFIGSET_ADDR ){ |
| @@ -608,10 +645,11 @@ | |
| 645 | db_column_text(&q, 1), |
| 646 | db_column_text(&q, 2) |
| 647 | ); |
| 648 | blob_appendf(pOut, "config /concealed %d\n%s", |
| 649 | blob_size(&rec), blob_str(&rec)); |
| 650 | nCard++; |
| 651 | blob_reset(&rec); |
| 652 | } |
| 653 | db_finalize(&q); |
| 654 | } |
| 655 | db_prepare(&q, "SELECT mtime, quote(name), quote(value) FROM config" |
| @@ -625,73 +663,39 @@ | |
| 663 | db_column_text(&q, 1), |
| 664 | db_column_text(&q, 2) |
| 665 | ); |
| 666 | blob_appendf(pOut, "config /config %d\n%s", |
| 667 | blob_size(&rec), blob_str(&rec)); |
| 668 | nCard++; |
| 669 | blob_reset(&rec); |
| 670 | } |
| 671 | db_reset(&q); |
| 672 | } |
| 673 | } |
| 674 | db_finalize(&q); |
| 675 | return nCard; |
| 676 | } |
| 677 | |
| 678 | /* |
| 679 | ** Identify a configuration group by name. Return its mask. |
| 680 | ** Throw an error if no match. |
| 681 | */ |
| 682 | int configure_name_to_mask(const char *z, int notFoundIsFatal){ |
| 683 | int i; |
| 684 | int n = strlen(z); |
| 685 | for(i=0; i<count(aGroupName); i++){ |
| 686 | if( strncmp(z, &aGroupName[i].zName[1], n)==0 ){ |
| 687 | return aGroupName[i].groupMask; |
| 688 | } |
| 689 | } |
| 690 | if( notFoundIsFatal ){ |
| 691 | printf("Available configuration areas:\n"); |
| 692 | for(i=0; i<count(aGroupName); i++){ |
| 693 | printf(" %-10s %s\n", &aGroupName[i].zName[1], aGroupName[i].zHelp); |
| 694 | } |
| 695 | fossil_fatal("no such configuration area: \"%s\"", z); |
| 696 | } |
| 697 | return 0; |
| 698 | } |
| 699 | |
| 700 | /* |
| 701 | ** Write SQL text into file zFilename that will restore the configuration |
| @@ -744,25 +748,31 @@ | |
| 748 | ** |
| 749 | ** %fossil configuration pull AREA ?URL? |
| 750 | ** |
| 751 | ** Pull and install the configuration from a different server |
| 752 | ** identified by URL. If no URL is specified, then the default |
| 753 | ** server is used. Use the --legacy option for the older protocol |
| 754 | ** (when talking to servers compiled prior to 2011-04-27.) Use |
| 755 | ** the --overwrite flag to completely replace local settings with |
| 756 | ** content received from URL. |
| 757 | ** |
| 758 | ** %fossil configuration push AREA ?URL? |
| 759 | ** |
| 760 | ** Push the local configuration into the remote server identified |
| 761 | ** by URL. Admin privilege is required on the remote server for |
| 762 | ** this to work. When the same record exists both locally and on |
| 763 | ** the remote end, the one that was most recently changed wins. |
| 764 | ** Use the --legacy flag when talking to holder servers. |
| 765 | ** |
| 766 | ** %fossil configuration reset AREA |
| 767 | ** |
| 768 | ** Restore the configuration to the default. AREA as above. |
| 769 | ** |
| 770 | ** %fossil configuration sync AREA ?URL? |
| 771 | ** |
| 772 | ** Synchronize configuration changes in the local repository with |
| 773 | ** the remote repository at URL. |
| 774 | */ |
| 775 | void configuration_cmd(void){ |
| 776 | int n; |
| 777 | const char *zMethod; |
| 778 | if( g.argc<3 ){ |
| @@ -776,11 +786,11 @@ | |
| 786 | const char *zSince = find_option("since",0,1); |
| 787 | sqlite3_int64 iStart; |
| 788 | if( g.argc!=5 ){ |
| 789 | usage("export AREA FILENAME"); |
| 790 | } |
| 791 | mask = configure_name_to_mask(g.argv[3], 1); |
| 792 | if( zSince ){ |
| 793 | iStart = db_multi_exec( |
| 794 | "SELECT coalesce(strftime('%%s',%Q),strftime('%%s','now',%Q))+0", |
| 795 | zSince, zSince |
| 796 | ); |
| @@ -802,19 +812,28 @@ | |
| 812 | groupMask = CONFIGSET_ALL; |
| 813 | } |
| 814 | configure_receive_all(&in, groupMask); |
| 815 | db_end_transaction(0); |
| 816 | }else |
| 817 | if( strncmp(zMethod, "pull", n)==0 |
| 818 | || strncmp(zMethod, "push", n)==0 |
| 819 | || strncmp(zMethod, "sync", n)==0 |
| 820 | ){ |
| 821 | int mask; |
| 822 | const char *zServer; |
| 823 | const char *zPw; |
| 824 | int legacyFlag = 0; |
| 825 | int overwriteFlag = 0; |
| 826 | if( zMethod[0]!='s' ) legacyFlag = find_option("legacy",0,0)!=0; |
| 827 | if( strncmp(zMethod,"pull",n)==0 ){ |
| 828 | overwriteFlag = find_option("overwrite",0,0)!=0; |
| 829 | } |
| 830 | url_proxy_options(); |
| 831 | if( g.argc!=4 && g.argc!=5 ){ |
| 832 | usage("pull AREA ?URL?"); |
| 833 | } |
| 834 | mask = configure_name_to_mask(g.argv[3], 1); |
| 835 | if( g.argc==5 ){ |
| 836 | zServer = g.argv[4]; |
| 837 | zPw = 0; |
| 838 | g.dontKeepUrl = 1; |
| 839 | }else{ |
| @@ -826,21 +845,25 @@ | |
| 845 | } |
| 846 | url_parse(zServer); |
| 847 | if( g.urlPasswd==0 && zPw ) g.urlPasswd = mprintf("%s", zPw); |
| 848 | user_select(); |
| 849 | url_enable_proxy("via proxy: "); |
| 850 | if( legacyFlag ) mask |= CONFIGSET_OLDFORMAT; |
| 851 | if( overwriteFlag ) mask |= CONFIGSET_OVERWRITE; |
| 852 | if( strncmp(zMethod, "push", n)==0 ){ |
| 853 | client_sync(0,0,0,0,0,mask); |
| 854 | }else if( strncmp(zMethod, "pull", n)==0 ){ |
| 855 | client_sync(0,0,0,0,mask,0); |
| 856 | }else{ |
| 857 | client_sync(0,0,0,0,mask,mask); |
| 858 | } |
| 859 | }else |
| 860 | if( strncmp(zMethod, "reset", n)==0 ){ |
| 861 | int mask, i; |
| 862 | char *zBackup; |
| 863 | if( g.argc!=4 ) usage("reset AREA"); |
| 864 | mask = configure_name_to_mask(g.argv[3], 1); |
| 865 | zBackup = db_text(0, |
| 866 | "SELECT strftime('config-backup-%%Y%%m%%d%%H%%M%%f','now')"); |
| 867 | db_begin_transaction(); |
| 868 | export_config(mask, g.argv[3], 0, zBackup); |
| 869 | for(i=0; i<count(aConfig); i++){ |
| 870 |
+33
-14
| --- src/xfer.c | ||
| +++ src/xfer.c | ||
| @@ -738,13 +738,16 @@ | ||
| 738 | 738 | } |
| 739 | 739 | db_finalize(&q); |
| 740 | 740 | } |
| 741 | 741 | |
| 742 | 742 | /* |
| 743 | -** Send a single old-style config card for configuration item zName | |
| 743 | +** Send a single old-style config card for configuration item zName. | |
| 744 | +** | |
| 745 | +** This routine and the functionality it implements is scheduled for | |
| 746 | +** removal on 2012-05-01. | |
| 744 | 747 | */ |
| 745 | -static void send_config_card(Xfer *pXfer, const char *zName){ | |
| 748 | +static void send_legacy_config_card(Xfer *pXfer, const char *zName){ | |
| 746 | 749 | if( zName[0]!='@' ){ |
| 747 | 750 | Blob val; |
| 748 | 751 | blob_zero(&val); |
| 749 | 752 | db_blob(&val, "SELECT value FROM config WHERE name=%Q", zName); |
| 750 | 753 | if( blob_size(&val)>0 ){ |
| @@ -1028,12 +1031,19 @@ | ||
| 1028 | 1031 | if( blob_eq(&xfer.aToken[0], "reqconfig") |
| 1029 | 1032 | && xfer.nToken==2 |
| 1030 | 1033 | ){ |
| 1031 | 1034 | if( g.okRead ){ |
| 1032 | 1035 | char *zName = blob_str(&xfer.aToken[1]); |
| 1033 | - if( configure_is_exportable(zName) ){ | |
| 1034 | - send_config_card(&xfer, zName); | |
| 1036 | + if( zName[0]=='/' ){ | |
| 1037 | + /* New style configuration transfer */ | |
| 1038 | + int groupMask = configure_name_to_mask(&zName[0], 0); | |
| 1039 | + if( !g.okAdmin ) groupMask &= ~CONFIGSET_USER; | |
| 1040 | + if( !g.okRdAddr ) groupMask &= ~CONFIGSET_ADDR; | |
| 1041 | + configure_send_group(xfer.pOut, groupMask, 0); | |
| 1042 | + }else if( configure_is_exportable(zName) ){ | |
| 1043 | + /* Old style configuration transfer */ | |
| 1044 | + send_legacy_config_card(&xfer, zName); | |
| 1035 | 1045 | } |
| 1036 | 1046 | } |
| 1037 | 1047 | }else |
| 1038 | 1048 | |
| 1039 | 1049 | /* config NAME SIZE \n CONTENT |
| @@ -1051,11 +1061,11 @@ | ||
| 1051 | 1061 | cgi_reset_content(); |
| 1052 | 1062 | @ error not\sauthorized\sto\spush\sconfiguration |
| 1053 | 1063 | nErr++; |
| 1054 | 1064 | break; |
| 1055 | 1065 | } |
| 1056 | - if( !recvConfig ){ | |
| 1066 | + if( !recvConfig && zName[0]=='@' ){ | |
| 1057 | 1067 | configure_prepare_to_receive(0); |
| 1058 | 1068 | recvConfig = 1; |
| 1059 | 1069 | } |
| 1060 | 1070 | configure_receive(zName, &content, CONFIGSET_ALL); |
| 1061 | 1071 | blob_reset(&content); |
| @@ -1330,25 +1340,32 @@ | ||
| 1330 | 1340 | while( zName ){ |
| 1331 | 1341 | blob_appendf(&send, "reqconfig %s\n", zName); |
| 1332 | 1342 | zName = configure_next_name(configRcvMask); |
| 1333 | 1343 | nCardSent++; |
| 1334 | 1344 | } |
| 1335 | - if( configRcvMask & (CONFIGSET_USER|CONFIGSET_TKT) ){ | |
| 1336 | - configure_prepare_to_receive(0); | |
| 1345 | + if( (configRcvMask & (CONFIGSET_USER|CONFIGSET_TKT))!=0 | |
| 1346 | + && (configRcvMask & CONFIGSET_OLDFORMAT)!=0 | |
| 1347 | + ){ | |
| 1348 | + int overwrite = (configRcvMask & CONFIGSET_OVERWRITE)!=0; | |
| 1349 | + configure_prepare_to_receive(overwrite); | |
| 1337 | 1350 | } |
| 1338 | 1351 | origConfigRcvMask = configRcvMask; |
| 1339 | 1352 | configRcvMask = 0; |
| 1340 | 1353 | } |
| 1341 | 1354 | |
| 1342 | 1355 | /* Send configuration parameters being pushed */ |
| 1343 | 1356 | if( configSendMask ){ |
| 1344 | - const char *zName; | |
| 1345 | - zName = configure_first_name(configSendMask); | |
| 1346 | - while( zName ){ | |
| 1347 | - send_config_card(&xfer, zName); | |
| 1348 | - zName = configure_next_name(configSendMask); | |
| 1349 | - nCardSent++; | |
| 1357 | + if( configSendMask & CONFIGSET_OLDFORMAT ){ | |
| 1358 | + const char *zName; | |
| 1359 | + zName = configure_first_name(configSendMask); | |
| 1360 | + while( zName ){ | |
| 1361 | + send_legacy_config_card(&xfer, zName); | |
| 1362 | + zName = configure_next_name(configSendMask); | |
| 1363 | + nCardSent++; | |
| 1364 | + } | |
| 1365 | + }else{ | |
| 1366 | + nCardSent += configure_send_group(xfer.pOut, configSendMask, 0); | |
| 1350 | 1367 | } |
| 1351 | 1368 | configSendMask = 0; |
| 1352 | 1369 | } |
| 1353 | 1370 | |
| 1354 | 1371 | /* Append randomness to the end of the message. This makes all |
| @@ -1646,11 +1663,13 @@ | ||
| 1646 | 1663 | break; |
| 1647 | 1664 | } |
| 1648 | 1665 | blobarray_reset(xfer.aToken, xfer.nToken); |
| 1649 | 1666 | blob_reset(&xfer.line); |
| 1650 | 1667 | } |
| 1651 | - if( origConfigRcvMask & (CONFIGSET_TKT|CONFIGSET_USER) ){ | |
| 1668 | + if( (configRcvMask & (CONFIGSET_USER|CONFIGSET_TKT))!=0 | |
| 1669 | + && (configRcvMask & CONFIGSET_OLDFORMAT)!=0 | |
| 1670 | + ){ | |
| 1652 | 1671 | configure_finalize_receive(); |
| 1653 | 1672 | } |
| 1654 | 1673 | origConfigRcvMask = 0; |
| 1655 | 1674 | if( nCardRcvd>0 ){ |
| 1656 | 1675 | fossil_print(zValueFormat, "Received:", |
| 1657 | 1676 |
| --- src/xfer.c | |
| +++ src/xfer.c | |
| @@ -738,13 +738,16 @@ | |
| 738 | } |
| 739 | db_finalize(&q); |
| 740 | } |
| 741 | |
| 742 | /* |
| 743 | ** Send a single old-style config card for configuration item zName |
| 744 | */ |
| 745 | static void send_config_card(Xfer *pXfer, const char *zName){ |
| 746 | if( zName[0]!='@' ){ |
| 747 | Blob val; |
| 748 | blob_zero(&val); |
| 749 | db_blob(&val, "SELECT value FROM config WHERE name=%Q", zName); |
| 750 | if( blob_size(&val)>0 ){ |
| @@ -1028,12 +1031,19 @@ | |
| 1028 | if( blob_eq(&xfer.aToken[0], "reqconfig") |
| 1029 | && xfer.nToken==2 |
| 1030 | ){ |
| 1031 | if( g.okRead ){ |
| 1032 | char *zName = blob_str(&xfer.aToken[1]); |
| 1033 | if( configure_is_exportable(zName) ){ |
| 1034 | send_config_card(&xfer, zName); |
| 1035 | } |
| 1036 | } |
| 1037 | }else |
| 1038 | |
| 1039 | /* config NAME SIZE \n CONTENT |
| @@ -1051,11 +1061,11 @@ | |
| 1051 | cgi_reset_content(); |
| 1052 | @ error not\sauthorized\sto\spush\sconfiguration |
| 1053 | nErr++; |
| 1054 | break; |
| 1055 | } |
| 1056 | if( !recvConfig ){ |
| 1057 | configure_prepare_to_receive(0); |
| 1058 | recvConfig = 1; |
| 1059 | } |
| 1060 | configure_receive(zName, &content, CONFIGSET_ALL); |
| 1061 | blob_reset(&content); |
| @@ -1330,25 +1340,32 @@ | |
| 1330 | while( zName ){ |
| 1331 | blob_appendf(&send, "reqconfig %s\n", zName); |
| 1332 | zName = configure_next_name(configRcvMask); |
| 1333 | nCardSent++; |
| 1334 | } |
| 1335 | if( configRcvMask & (CONFIGSET_USER|CONFIGSET_TKT) ){ |
| 1336 | configure_prepare_to_receive(0); |
| 1337 | } |
| 1338 | origConfigRcvMask = configRcvMask; |
| 1339 | configRcvMask = 0; |
| 1340 | } |
| 1341 | |
| 1342 | /* Send configuration parameters being pushed */ |
| 1343 | if( configSendMask ){ |
| 1344 | const char *zName; |
| 1345 | zName = configure_first_name(configSendMask); |
| 1346 | while( zName ){ |
| 1347 | send_config_card(&xfer, zName); |
| 1348 | zName = configure_next_name(configSendMask); |
| 1349 | nCardSent++; |
| 1350 | } |
| 1351 | configSendMask = 0; |
| 1352 | } |
| 1353 | |
| 1354 | /* Append randomness to the end of the message. This makes all |
| @@ -1646,11 +1663,13 @@ | |
| 1646 | break; |
| 1647 | } |
| 1648 | blobarray_reset(xfer.aToken, xfer.nToken); |
| 1649 | blob_reset(&xfer.line); |
| 1650 | } |
| 1651 | if( origConfigRcvMask & (CONFIGSET_TKT|CONFIGSET_USER) ){ |
| 1652 | configure_finalize_receive(); |
| 1653 | } |
| 1654 | origConfigRcvMask = 0; |
| 1655 | if( nCardRcvd>0 ){ |
| 1656 | fossil_print(zValueFormat, "Received:", |
| 1657 |
| --- src/xfer.c | |
| +++ src/xfer.c | |
| @@ -738,13 +738,16 @@ | |
| 738 | } |
| 739 | db_finalize(&q); |
| 740 | } |
| 741 | |
| 742 | /* |
| 743 | ** Send a single old-style config card for configuration item zName. |
| 744 | ** |
| 745 | ** This routine and the functionality it implements is scheduled for |
| 746 | ** removal on 2012-05-01. |
| 747 | */ |
| 748 | static void send_legacy_config_card(Xfer *pXfer, const char *zName){ |
| 749 | if( zName[0]!='@' ){ |
| 750 | Blob val; |
| 751 | blob_zero(&val); |
| 752 | db_blob(&val, "SELECT value FROM config WHERE name=%Q", zName); |
| 753 | if( blob_size(&val)>0 ){ |
| @@ -1028,12 +1031,19 @@ | |
| 1031 | if( blob_eq(&xfer.aToken[0], "reqconfig") |
| 1032 | && xfer.nToken==2 |
| 1033 | ){ |
| 1034 | if( g.okRead ){ |
| 1035 | char *zName = blob_str(&xfer.aToken[1]); |
| 1036 | if( zName[0]=='/' ){ |
| 1037 | /* New style configuration transfer */ |
| 1038 | int groupMask = configure_name_to_mask(&zName[0], 0); |
| 1039 | if( !g.okAdmin ) groupMask &= ~CONFIGSET_USER; |
| 1040 | if( !g.okRdAddr ) groupMask &= ~CONFIGSET_ADDR; |
| 1041 | configure_send_group(xfer.pOut, groupMask, 0); |
| 1042 | }else if( configure_is_exportable(zName) ){ |
| 1043 | /* Old style configuration transfer */ |
| 1044 | send_legacy_config_card(&xfer, zName); |
| 1045 | } |
| 1046 | } |
| 1047 | }else |
| 1048 | |
| 1049 | /* config NAME SIZE \n CONTENT |
| @@ -1051,11 +1061,11 @@ | |
| 1061 | cgi_reset_content(); |
| 1062 | @ error not\sauthorized\sto\spush\sconfiguration |
| 1063 | nErr++; |
| 1064 | break; |
| 1065 | } |
| 1066 | if( !recvConfig && zName[0]=='@' ){ |
| 1067 | configure_prepare_to_receive(0); |
| 1068 | recvConfig = 1; |
| 1069 | } |
| 1070 | configure_receive(zName, &content, CONFIGSET_ALL); |
| 1071 | blob_reset(&content); |
| @@ -1330,25 +1340,32 @@ | |
| 1340 | while( zName ){ |
| 1341 | blob_appendf(&send, "reqconfig %s\n", zName); |
| 1342 | zName = configure_next_name(configRcvMask); |
| 1343 | nCardSent++; |
| 1344 | } |
| 1345 | if( (configRcvMask & (CONFIGSET_USER|CONFIGSET_TKT))!=0 |
| 1346 | && (configRcvMask & CONFIGSET_OLDFORMAT)!=0 |
| 1347 | ){ |
| 1348 | int overwrite = (configRcvMask & CONFIGSET_OVERWRITE)!=0; |
| 1349 | configure_prepare_to_receive(overwrite); |
| 1350 | } |
| 1351 | origConfigRcvMask = configRcvMask; |
| 1352 | configRcvMask = 0; |
| 1353 | } |
| 1354 | |
| 1355 | /* Send configuration parameters being pushed */ |
| 1356 | if( configSendMask ){ |
| 1357 | if( configSendMask & CONFIGSET_OLDFORMAT ){ |
| 1358 | const char *zName; |
| 1359 | zName = configure_first_name(configSendMask); |
| 1360 | while( zName ){ |
| 1361 | send_legacy_config_card(&xfer, zName); |
| 1362 | zName = configure_next_name(configSendMask); |
| 1363 | nCardSent++; |
| 1364 | } |
| 1365 | }else{ |
| 1366 | nCardSent += configure_send_group(xfer.pOut, configSendMask, 0); |
| 1367 | } |
| 1368 | configSendMask = 0; |
| 1369 | } |
| 1370 | |
| 1371 | /* Append randomness to the end of the message. This makes all |
| @@ -1646,11 +1663,13 @@ | |
| 1663 | break; |
| 1664 | } |
| 1665 | blobarray_reset(xfer.aToken, xfer.nToken); |
| 1666 | blob_reset(&xfer.line); |
| 1667 | } |
| 1668 | if( (configRcvMask & (CONFIGSET_USER|CONFIGSET_TKT))!=0 |
| 1669 | && (configRcvMask & CONFIGSET_OLDFORMAT)!=0 |
| 1670 | ){ |
| 1671 | configure_finalize_receive(); |
| 1672 | } |
| 1673 | origConfigRcvMask = 0; |
| 1674 | if( nCardRcvd>0 ){ |
| 1675 | fossil_print(zValueFormat, "Received:", |
| 1676 |