e06ae9f6d2 2008-05-22 drh: /* e06ae9f6d2 2008-05-22 drh: ** Copyright (c) 2008 D. Richard Hipp e06ae9f6d2 2008-05-22 drh: ** e06ae9f6d2 2008-05-22 drh: ** This program is free software; you can redistribute it and/or e06ae9f6d2 2008-05-22 drh: ** modify it under the terms of the GNU General Public e06ae9f6d2 2008-05-22 drh: ** License version 2 as published by the Free Software Foundation. e06ae9f6d2 2008-05-22 drh: ** e06ae9f6d2 2008-05-22 drh: ** This program is distributed in the hope that it will be useful, e06ae9f6d2 2008-05-22 drh: ** but WITHOUT ANY WARRANTY; without even the implied warranty of e06ae9f6d2 2008-05-22 drh: ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU e06ae9f6d2 2008-05-22 drh: ** General Public License for more details. e06ae9f6d2 2008-05-22 drh: ** e06ae9f6d2 2008-05-22 drh: ** You should have received a copy of the GNU General Public e06ae9f6d2 2008-05-22 drh: ** License along with this library; if not, write to the e06ae9f6d2 2008-05-22 drh: ** Free Software Foundation, Inc., 59 Temple Place - Suite 330, e06ae9f6d2 2008-05-22 drh: ** Boston, MA 02111-1307, USA. e06ae9f6d2 2008-05-22 drh: ** e06ae9f6d2 2008-05-22 drh: ** Author contact information: e06ae9f6d2 2008-05-22 drh: ** drh@hwaci.com e06ae9f6d2 2008-05-22 drh: ** http://www.hwaci.com/drh/ e06ae9f6d2 2008-05-22 drh: ** e06ae9f6d2 2008-05-22 drh: ******************************************************************************* e06ae9f6d2 2008-05-22 drh: ** e06ae9f6d2 2008-05-22 drh: ** This file contains code used to manage repository configurations. e06ae9f6d2 2008-05-22 drh: ** By "responsitory configure" we mean the local state of a repository e06ae9f6d2 2008-05-22 drh: ** distinct from the versioned files. e06ae9f6d2 2008-05-22 drh: */ e06ae9f6d2 2008-05-22 drh: #include "config.h" e06ae9f6d2 2008-05-22 drh: #include "configure.h" e06ae9f6d2 2008-05-22 drh: #include <assert.h> e06ae9f6d2 2008-05-22 drh: e06ae9f6d2 2008-05-22 drh: #if INTERFACE e06ae9f6d2 2008-05-22 drh: /* e06ae9f6d2 2008-05-22 drh: ** Configuration transfers occur in groups. These are the allowed e06ae9f6d2 2008-05-22 drh: ** groupings: e06ae9f6d2 2008-05-22 drh: */ e06ae9f6d2 2008-05-22 drh: #define CONFIGSET_SKIN 0x000001 /* WWW interface appearance */ e06ae9f6d2 2008-05-22 drh: #define CONFIGSET_TKT 0x000002 /* Ticket configuration */ e06ae9f6d2 2008-05-22 drh: #define CONFIGSET_PROJ 0x000004 /* Project name */ bf75ea9852 2008-10-04 drh: #define CONFIGSET_SHUN 0x000008 /* Shun settings */ bf75ea9852 2008-10-04 drh: #define CONFIGSET_USER 0x000010 /* The USER table */ e06ae9f6d2 2008-05-22 drh: e06ae9f6d2 2008-05-22 drh: #define CONFIGSET_ALL 0xffffff /* Everything */ e06ae9f6d2 2008-05-22 drh: e06ae9f6d2 2008-05-22 drh: #endif /* INTERFACE */ e06ae9f6d2 2008-05-22 drh: e06ae9f6d2 2008-05-22 drh: /* e06ae9f6d2 2008-05-22 drh: ** Names of the configuration sets e06ae9f6d2 2008-05-22 drh: */ e06ae9f6d2 2008-05-22 drh: static struct { e06ae9f6d2 2008-05-22 drh: const char *zName; /* Name of the configuration set */ e06ae9f6d2 2008-05-22 drh: int groupMask; /* Mask for that configuration set */ bf75ea9852 2008-10-04 drh: const char *zHelp; /* What it does */ e06ae9f6d2 2008-05-22 drh: } aGroupName[] = { bf75ea9852 2008-10-04 drh: { "skin", CONFIGSET_SKIN, "Web interface apparance settings" }, bf75ea9852 2008-10-04 drh: { "ticket", CONFIGSET_TKT, "Ticket setup", }, bf75ea9852 2008-10-04 drh: { "project", CONFIGSET_PROJ, "Project name and description" }, bf75ea9852 2008-10-04 drh: { "shun", CONFIGSET_SHUN, "List of shunned artifacts" }, bf75ea9852 2008-10-04 drh: { "user", CONFIGSET_USER, "Users and privilege settings" }, bf75ea9852 2008-10-04 drh: { "all", CONFIGSET_ALL, "All of the above" }, e06ae9f6d2 2008-05-22 drh: }; e06ae9f6d2 2008-05-22 drh: e06ae9f6d2 2008-05-22 drh: e06ae9f6d2 2008-05-22 drh: /* e06ae9f6d2 2008-05-22 drh: ** The following is a list of settings that we are willing to e06ae9f6d2 2008-05-22 drh: ** transfer. bf75ea9852 2008-10-04 drh: ** bf75ea9852 2008-10-04 drh: ** Setting names that begin with an alphabetic characters refer to bf75ea9852 2008-10-04 drh: ** single entries in the CONFIG table. Setting names that begin with bf75ea9852 2008-10-04 drh: ** "@" are for special processing. e06ae9f6d2 2008-05-22 drh: */ e06ae9f6d2 2008-05-22 drh: static struct { e06ae9f6d2 2008-05-22 drh: const char *zName; /* Name of the configuration parameter */ e06ae9f6d2 2008-05-22 drh: int groupMask; /* Which config groups is it part of */ 28e56282c9 2008-05-23 drh: } aConfig[] = { bf75ea9852 2008-10-04 drh: { "css", CONFIGSET_SKIN }, bf75ea9852 2008-10-04 drh: { "header", CONFIGSET_SKIN }, bf75ea9852 2008-10-04 drh: { "footer", CONFIGSET_SKIN }, bf75ea9852 2008-10-04 drh: { "project-name", CONFIGSET_PROJ }, bf75ea9852 2008-10-04 drh: { "project-description", CONFIGSET_PROJ }, bf75ea9852 2008-10-04 drh: { "index-page", CONFIGSET_SKIN }, bf75ea9852 2008-10-04 drh: { "timeline-block-markup", CONFIGSET_SKIN }, bf75ea9852 2008-10-04 drh: { "timeline-max-comment", CONFIGSET_SKIN }, bf75ea9852 2008-10-04 drh: { "ticket-table", CONFIGSET_TKT }, bf75ea9852 2008-10-04 drh: { "ticket-common", CONFIGSET_TKT }, bf75ea9852 2008-10-04 drh: { "ticket-newpage", CONFIGSET_TKT }, bf75ea9852 2008-10-04 drh: { "ticket-viewpage", CONFIGSET_TKT }, bf75ea9852 2008-10-04 drh: { "ticket-editpage", CONFIGSET_TKT }, bf75ea9852 2008-10-04 drh: { "ticket-report-template", CONFIGSET_TKT }, bf75ea9852 2008-10-04 drh: { "ticket-key-template", CONFIGSET_TKT }, bf75ea9852 2008-10-04 drh: { "@reportfmt", CONFIGSET_TKT }, bf75ea9852 2008-10-04 drh: { "@user", CONFIGSET_USER }, bf75ea9852 2008-10-04 drh: { "@shun", CONFIGSET_SHUN }, e06ae9f6d2 2008-05-22 drh: }; 28e56282c9 2008-05-23 drh: static int iConfig = 0; 28e56282c9 2008-05-23 drh: 28e56282c9 2008-05-23 drh: /* 28e56282c9 2008-05-23 drh: ** Return name of first configuration property matching the given mask. 28e56282c9 2008-05-23 drh: */ 28e56282c9 2008-05-23 drh: const char *configure_first_name(int iMask){ 28e56282c9 2008-05-23 drh: iConfig = 0; 28e56282c9 2008-05-23 drh: return configure_next_name(iMask); 28e56282c9 2008-05-23 drh: } 28e56282c9 2008-05-23 drh: const char *configure_next_name(int iMask){ 28e56282c9 2008-05-23 drh: while( iConfig<count(aConfig) ){ 28e56282c9 2008-05-23 drh: if( aConfig[iConfig].groupMask & iMask ){ 28e56282c9 2008-05-23 drh: return aConfig[iConfig++].zName; 28e56282c9 2008-05-23 drh: }else{ 28e56282c9 2008-05-23 drh: iConfig++; 28e56282c9 2008-05-23 drh: } 28e56282c9 2008-05-23 drh: } 28e56282c9 2008-05-23 drh: return 0; 28e56282c9 2008-05-23 drh: } e06ae9f6d2 2008-05-22 drh: e06ae9f6d2 2008-05-22 drh: /* bf75ea9852 2008-10-04 drh: ** Return the mask for the named configuration parameter if it can be bf75ea9852 2008-10-04 drh: ** safely exported. Return 0 if the parameter is not safe to export. e06ae9f6d2 2008-05-22 drh: */ e06ae9f6d2 2008-05-22 drh: int configure_is_exportable(const char *zName){ e06ae9f6d2 2008-05-22 drh: int i; 28e56282c9 2008-05-23 drh: for(i=0; i<count(aConfig); i++){ 28e56282c9 2008-05-23 drh: if( strcmp(zName, aConfig[i].zName)==0 ){ bf75ea9852 2008-10-04 drh: int m = aConfig[i].groupMask; bf75ea9852 2008-10-04 drh: if( !g.okAdmin ){ bf75ea9852 2008-10-04 drh: m &= ~CONFIGSET_USER; bf75ea9852 2008-10-04 drh: } bf75ea9852 2008-10-04 drh: return m; 28e56282c9 2008-05-23 drh: } e06ae9f6d2 2008-05-22 drh: } e06ae9f6d2 2008-05-22 drh: return 0; bf75ea9852 2008-10-04 drh: } bf75ea9852 2008-10-04 drh: bf75ea9852 2008-10-04 drh: /* bf75ea9852 2008-10-04 drh: ** zName is one of the special configuration names that refers to an entire bf75ea9852 2008-10-04 drh: ** table rather than a single entry in CONFIG. Special names are "@reportfmt" bf75ea9852 2008-10-04 drh: ** and "@shun" and "@user". This routine writes SQL text into pOut that when bf75ea9852 2008-10-04 drh: ** evaluated will populate the corresponding table with data. bf75ea9852 2008-10-04 drh: */ bf75ea9852 2008-10-04 drh: void configure_render_special_name(const char *zName, Blob *pOut){ bf75ea9852 2008-10-04 drh: Stmt q; bf75ea9852 2008-10-04 drh: if( strcmp(zName, "@shun")==0 ){ bf75ea9852 2008-10-04 drh: db_prepare(&q, "SELECT uuid FROM shun"); bf75ea9852 2008-10-04 drh: while( db_step(&q)==SQLITE_ROW ){ bf75ea9852 2008-10-04 drh: blob_appendf(pOut, "INSERT OR IGNORE INTO shun VALUES('%s');\n", bf75ea9852 2008-10-04 drh: db_column_text(&q, 0) bf75ea9852 2008-10-04 drh: ); bf75ea9852 2008-10-04 drh: } bf75ea9852 2008-10-04 drh: db_finalize(&q); bf75ea9852 2008-10-04 drh: }else if( strcmp(zName, "@reportfmt")==0 ){ bf75ea9852 2008-10-04 drh: db_prepare(&q, "SELECT title, cols, sqlcode FROM reportfmt"); bf75ea9852 2008-10-04 drh: while( db_step(&q)==SQLITE_ROW ){ bf75ea9852 2008-10-04 drh: blob_appendf(pOut, "INSERT INTO _xfer_reportfmt(title,cols,sqlcode) " bf75ea9852 2008-10-04 drh: " VALUES(%Q,%Q,%Q);\n", bf75ea9852 2008-10-04 drh: db_column_text(&q, 0), bf75ea9852 2008-10-04 drh: db_column_text(&q, 1), bf75ea9852 2008-10-04 drh: db_column_text(&q, 2) bf75ea9852 2008-10-04 drh: ); bf75ea9852 2008-10-04 drh: } bf75ea9852 2008-10-04 drh: db_finalize(&q); bf75ea9852 2008-10-04 drh: }else if( strcmp(zName, "@user")==0 ){ bf75ea9852 2008-10-04 drh: db_prepare(&q, "SELECT login, cap, info, quote(photo) FROM user"); bf75ea9852 2008-10-04 drh: while( db_step(&q)==SQLITE_ROW ){ bf75ea9852 2008-10-04 drh: blob_appendf(pOut, "INSERT INTO _xfer_user(login,cap,info,photo) " bf75ea9852 2008-10-04 drh: " VALUES(%Q,%Q,%Q,%s);\n", bf75ea9852 2008-10-04 drh: db_column_text(&q, 0), bf75ea9852 2008-10-04 drh: db_column_text(&q, 1), bf75ea9852 2008-10-04 drh: db_column_text(&q, 2), bf75ea9852 2008-10-04 drh: db_column_text(&q, 3) bf75ea9852 2008-10-04 drh: ); bf75ea9852 2008-10-04 drh: } bf75ea9852 2008-10-04 drh: db_finalize(&q); bf75ea9852 2008-10-04 drh: } bf75ea9852 2008-10-04 drh: } bf75ea9852 2008-10-04 drh: bf75ea9852 2008-10-04 drh: /* bf75ea9852 2008-10-04 drh: ** Two SQL functions: bf75ea9852 2008-10-04 drh: ** bf75ea9852 2008-10-04 drh: ** flag_test(int) bf75ea9852 2008-10-04 drh: ** flag_clear(int) bf75ea9852 2008-10-04 drh: ** bf75ea9852 2008-10-04 drh: ** The flag_test() function takes the integer valued argument and bf75ea9852 2008-10-04 drh: ** ANDs it against the static variable "flag_value" below. The bf75ea9852 2008-10-04 drh: ** function returns TRUE or false depending on the result. The bf75ea9852 2008-10-04 drh: ** flag_clear() function masks off the bits from "flag_value" that bf75ea9852 2008-10-04 drh: ** are given in the argument. bf75ea9852 2008-10-04 drh: ** bf75ea9852 2008-10-04 drh: ** These functions are used below in the WHEN clause of a trigger to bf75ea9852 2008-10-04 drh: ** get the trigger to fire exactly once. bf75ea9852 2008-10-04 drh: */ bf75ea9852 2008-10-04 drh: static int flag_value = 0xffff; bf75ea9852 2008-10-04 drh: static void flag_test_function( bf75ea9852 2008-10-04 drh: sqlite3_context *context, bf75ea9852 2008-10-04 drh: int argc, bf75ea9852 2008-10-04 drh: sqlite3_value **argv bf75ea9852 2008-10-04 drh: ){ bf75ea9852 2008-10-04 drh: int m = sqlite3_value_int(argv[0]); bf75ea9852 2008-10-04 drh: sqlite3_result_int(context, (flag_value&m)!=0 ); bf75ea9852 2008-10-04 drh: } bf75ea9852 2008-10-04 drh: static void flag_clear_function( bf75ea9852 2008-10-04 drh: sqlite3_context *context, bf75ea9852 2008-10-04 drh: int argc, bf75ea9852 2008-10-04 drh: sqlite3_value **argv bf75ea9852 2008-10-04 drh: ){ bf75ea9852 2008-10-04 drh: int m = sqlite3_value_int(argv[0]); bf75ea9852 2008-10-04 drh: flag_value &= ~m; bf75ea9852 2008-10-04 drh: } bf75ea9852 2008-10-04 drh: bf75ea9852 2008-10-04 drh: /* bf75ea9852 2008-10-04 drh: ** Create the temporary _xfer_reportfmt and _xfer_user tables that are bf75ea9852 2008-10-04 drh: ** necessary in order to evalute the SQL text generated by the bf75ea9852 2008-10-04 drh: ** configure_render_special_name() routine. bf75ea9852 2008-10-04 drh: ** bf75ea9852 2008-10-04 drh: ** If replaceFlag is true, then the setup is such that the content in bf75ea9852 2008-10-04 drh: ** the SQL text will completely replace the current repository configuration. bf75ea9852 2008-10-04 drh: ** If replaceFlag is false, then the SQL text will be merged with the bf75ea9852 2008-10-04 drh: ** existing configuration. When merging, existing values take priority bf75ea9852 2008-10-04 drh: ** over SQL text values. bf75ea9852 2008-10-04 drh: */ bf75ea9852 2008-10-04 drh: void configure_prepare_to_receive(int replaceFlag){ bf75ea9852 2008-10-04 drh: static const char zSQL1[] = bf75ea9852 2008-10-04 drh: @ CREATE TEMP TABLE _xfer_reportfmt( bf75ea9852 2008-10-04 drh: @ rn integer primary key, -- Report number bf75ea9852 2008-10-04 drh: @ owner text, -- Owner of this report format (not used) bf75ea9852 2008-10-04 drh: @ title text UNIQUE ON CONFLICT IGNORE, -- Title of this report bf75ea9852 2008-10-04 drh: @ cols text, -- A color-key specification bf75ea9852 2008-10-04 drh: @ sqlcode text -- An SQL SELECT statement for this report bf75ea9852 2008-10-04 drh: @ ); bf75ea9852 2008-10-04 drh: @ CREATE TEMP TABLE _xfer_user( bf75ea9852 2008-10-04 drh: @ uid INTEGER PRIMARY KEY, -- User ID bf75ea9852 2008-10-04 drh: @ login TEXT UNIQUE ON CONFLICT IGNORE, -- login name of the user bf75ea9852 2008-10-04 drh: @ pw TEXT, -- password bf75ea9852 2008-10-04 drh: @ cap TEXT, -- Capabilities of this user bf75ea9852 2008-10-04 drh: @ cookie TEXT, -- WWW login cookie bf75ea9852 2008-10-04 drh: @ ipaddr TEXT, -- IP address for which cookie is valid bf75ea9852 2008-10-04 drh: @ cexpire DATETIME, -- Time when cookie expires bf75ea9852 2008-10-04 drh: @ info TEXT, -- contact information bf75ea9852 2008-10-04 drh: @ photo BLOB -- JPEG image of this user bf75ea9852 2008-10-04 drh: @ ); bf75ea9852 2008-10-04 drh: @ INSERT INTO _xfer_reportfmt SELECT * FROM reportfmt; bf75ea9852 2008-10-04 drh: @ INSERT INTO _xfer_user SELECT * FROM user; bf75ea9852 2008-10-04 drh: ; bf75ea9852 2008-10-04 drh: db_multi_exec(zSQL1); bf75ea9852 2008-10-04 drh: bf75ea9852 2008-10-04 drh: /* When the replace flag is set, add triggers that run the first time bf75ea9852 2008-10-04 drh: ** that new data is seen. The triggers run only once and delete all the bf75ea9852 2008-10-04 drh: ** existing data. bf75ea9852 2008-10-04 drh: */ bf75ea9852 2008-10-04 drh: if( replaceFlag ){ bf75ea9852 2008-10-04 drh: static const char zSQL2[] = bf75ea9852 2008-10-04 drh: @ CREATE TRIGGER _xfer_r1 BEFORE INSERT ON _xfer_reportfmt bf75ea9852 2008-10-04 drh: @ WHEN flag_test(1) BEGIN bf75ea9852 2008-10-04 drh: @ DELETE FROM _xfer_reportfmt; bf75ea9852 2008-10-04 drh: @ SELECT flag_clear(1); bf75ea9852 2008-10-04 drh: @ END; bf75ea9852 2008-10-04 drh: @ CREATE TRIGGER _xfer_r2 BEFORE INSERT ON _xfer_user bf75ea9852 2008-10-04 drh: @ WHEN flag_test(2) BEGIN bf75ea9852 2008-10-04 drh: @ DELETE FROM _xfer_user; bf75ea9852 2008-10-04 drh: @ SELECT flag_clear(2); bf75ea9852 2008-10-04 drh: @ END; bf75ea9852 2008-10-04 drh: @ CREATE TEMP TRIGGER _xfer_r3 BEFORE INSERT ON shun bf75ea9852 2008-10-04 drh: @ WHEN flag_test(4) BEGIN bf75ea9852 2008-10-04 drh: @ DELETE FROM shun; bf75ea9852 2008-10-04 drh: @ SELECT flag_clear(4); bf75ea9852 2008-10-04 drh: @ END; bf75ea9852 2008-10-04 drh: ; bf75ea9852 2008-10-04 drh: sqlite3_create_function(g.db, "flag_test", 1, SQLITE_UTF8, 0, bf75ea9852 2008-10-04 drh: flag_test_function, 0, 0); bf75ea9852 2008-10-04 drh: sqlite3_create_function(g.db, "flag_clear", 1, SQLITE_UTF8, 0, bf75ea9852 2008-10-04 drh: flag_clear_function, 0, 0); bf75ea9852 2008-10-04 drh: flag_value = 0xffff; bf75ea9852 2008-10-04 drh: db_multi_exec(zSQL2); bf75ea9852 2008-10-04 drh: } bf75ea9852 2008-10-04 drh: } bf75ea9852 2008-10-04 drh: bf75ea9852 2008-10-04 drh: /* bf75ea9852 2008-10-04 drh: ** After receiving configuration data, call this routine to transfer bf75ea9852 2008-10-04 drh: ** the results into the main database. bf75ea9852 2008-10-04 drh: */ bf75ea9852 2008-10-04 drh: void configure_finalize_receive(void){ bf75ea9852 2008-10-04 drh: static const char zSQL[] = bf75ea9852 2008-10-04 drh: @ DELETE FROM user; bf75ea9852 2008-10-04 drh: @ INSERT INTO user SELECT * FROM _xfer_user; bf75ea9852 2008-10-04 drh: @ DELETE FROM reportfmt; bf75ea9852 2008-10-04 drh: @ INSERT INTO reportfmt SELECT * FROM _xfer_reportfmt; bf75ea9852 2008-10-04 drh: @ DROP TABLE _xfer_user; bf75ea9852 2008-10-04 drh: @ DROP TABLE _xfer_reportfmt; bf75ea9852 2008-10-04 drh: ; bf75ea9852 2008-10-04 drh: db_multi_exec(zSQL); e06ae9f6d2 2008-05-22 drh: } e06ae9f6d2 2008-05-22 drh: e06ae9f6d2 2008-05-22 drh: /* e06ae9f6d2 2008-05-22 drh: ** Identify a configuration group by name. Return its mask. e06ae9f6d2 2008-05-22 drh: ** Throw an error if no match. e06ae9f6d2 2008-05-22 drh: */ e06ae9f6d2 2008-05-22 drh: static int find_area(const char *z){ e06ae9f6d2 2008-05-22 drh: int i; e06ae9f6d2 2008-05-22 drh: int n = strlen(z); e06ae9f6d2 2008-05-22 drh: for(i=0; i<count(aGroupName); i++){ e06ae9f6d2 2008-05-22 drh: if( strncmp(z, aGroupName[i].zName, n)==0 ){ e06ae9f6d2 2008-05-22 drh: return aGroupName[i].groupMask; e06ae9f6d2 2008-05-22 drh: } e06ae9f6d2 2008-05-22 drh: } bf75ea9852 2008-10-04 drh: printf("Available configuration areas:\n"); bf75ea9852 2008-10-04 drh: for(i=0; i<count(aGroupName); i++){ bf75ea9852 2008-10-04 drh: printf(" %-10s %s\n", aGroupName[i].zName, aGroupName[i].zHelp); bf75ea9852 2008-10-04 drh: } e06ae9f6d2 2008-05-22 drh: fossil_fatal("no such configuration area: \"%s\"", z); e06ae9f6d2 2008-05-22 drh: return 0; e06ae9f6d2 2008-05-22 drh: } e06ae9f6d2 2008-05-22 drh: bf75ea9852 2008-10-04 drh: /* bf75ea9852 2008-10-04 drh: ** Write SQL text into file zFilename that will restore the configuration bf75ea9852 2008-10-04 drh: ** area identified by mask to its current state from any other state. bf75ea9852 2008-10-04 drh: */ bf75ea9852 2008-10-04 drh: static void export_config( bf75ea9852 2008-10-04 drh: int mask, /* Mask indicating which configuration to export */ bf75ea9852 2008-10-04 drh: const char *zMask, /* Name of the configuration */ bf75ea9852 2008-10-04 drh: const char *zFilename /* Write into this file */ bf75ea9852 2008-10-04 drh: ){ bf75ea9852 2008-10-04 drh: int i; bf75ea9852 2008-10-04 drh: Blob out; bf75ea9852 2008-10-04 drh: blob_zero(&out); bf75ea9852 2008-10-04 drh: blob_appendf(&out, bf75ea9852 2008-10-04 drh: "-- The \"%s\" configuration exported from\n" bf75ea9852 2008-10-04 drh: "-- repository \"%s\"\n" bf75ea9852 2008-10-04 drh: "-- on %s\n", bf75ea9852 2008-10-04 drh: zMask, g.zRepositoryName, bf75ea9852 2008-10-04 drh: db_text(0, "SELECT datetime('now')") bf75ea9852 2008-10-04 drh: ); bf75ea9852 2008-10-04 drh: for(i=0; i<count(aConfig); i++){ bf75ea9852 2008-10-04 drh: if( (aConfig[i].groupMask & mask)!=0 ){ bf75ea9852 2008-10-04 drh: const char *zName = aConfig[i].zName; bf75ea9852 2008-10-04 drh: if( zName[0]!='@' ){ bf75ea9852 2008-10-04 drh: char *zValue = db_text(0, bf75ea9852 2008-10-04 drh: "SELECT value FROM config WHERE name=%Q", zName); bf75ea9852 2008-10-04 drh: if( zValue ){ bf75ea9852 2008-10-04 drh: blob_appendf(&out,"REPLACE INTO config VALUES(%Q,%Q);\n", bf75ea9852 2008-10-04 drh: zName, zValue); bf75ea9852 2008-10-04 drh: } bf75ea9852 2008-10-04 drh: free(zValue); bf75ea9852 2008-10-04 drh: }else{ bf75ea9852 2008-10-04 drh: configure_render_special_name(zName, &out); bf75ea9852 2008-10-04 drh: } bf75ea9852 2008-10-04 drh: } bf75ea9852 2008-10-04 drh: } bf75ea9852 2008-10-04 drh: blob_write_to_file(&out, zFilename); bf75ea9852 2008-10-04 drh: blob_reset(&out); 28e56282c9 2008-05-23 drh: } 28e56282c9 2008-05-23 drh: e06ae9f6d2 2008-05-22 drh: e06ae9f6d2 2008-05-22 drh: /* 28e56282c9 2008-05-23 drh: ** COMMAND: configuration e06ae9f6d2 2008-05-22 drh: ** e06ae9f6d2 2008-05-22 drh: ** Usage: %fossil configure METHOD ... e06ae9f6d2 2008-05-22 drh: ** bf75ea9852 2008-10-04 drh: ** Where METHOD is one of: export import merge pull reset. All methods e06ae9f6d2 2008-05-22 drh: ** accept the -R or --repository option to specific a repository. e06ae9f6d2 2008-05-22 drh: ** 28e56282c9 2008-05-23 drh: ** %fossil configuration export AREA FILENAME e06ae9f6d2 2008-05-22 drh: ** e06ae9f6d2 2008-05-22 drh: ** Write to FILENAME exported configuraton information for AREA. e06ae9f6d2 2008-05-22 drh: ** AREA can be one of: all ticket skin project e06ae9f6d2 2008-05-22 drh: ** 28e56282c9 2008-05-23 drh: ** %fossil configuration import FILENAME e06ae9f6d2 2008-05-22 drh: ** e06ae9f6d2 2008-05-22 drh: ** Read a configuration from FILENAME, overwriting the current bf75ea9852 2008-10-04 drh: ** configuration. bf75ea9852 2008-10-04 drh: ** bf75ea9852 2008-10-04 drh: ** %fossil configuration merge FILENAME bf75ea9852 2008-10-04 drh: ** bf75ea9852 2008-10-04 drh: ** Read a configuration from FILENAME and merge its values into bf75ea9852 2008-10-04 drh: ** the current configuration. Existing values take priority over bf75ea9852 2008-10-04 drh: ** values read from FILENAME. e06ae9f6d2 2008-05-22 drh: ** bf75ea9852 2008-10-04 drh: ** %fossil configuration pull AREA ?URL? e06ae9f6d2 2008-05-22 drh: ** e06ae9f6d2 2008-05-22 drh: ** Pull and install the configuration from a different server bf75ea9852 2008-10-04 drh: ** identified by URL. If no URL is specified, then the default bf75ea9852 2008-10-04 drh: ** server is used. e06ae9f6d2 2008-05-22 drh: ** 28e56282c9 2008-05-23 drh: ** %fossil configuration reset AREA e06ae9f6d2 2008-05-22 drh: ** e06ae9f6d2 2008-05-22 drh: ** Restore the configuration to the default. AREA as above. bf75ea9852 2008-10-04 drh: ** bf75ea9852 2008-10-04 drh: ** WARNING: Do not import, merge, or pull configurations from an untrusted bf75ea9852 2008-10-04 drh: ** source. The inbound configuration is not checked for safety and can bf75ea9852 2008-10-04 drh: ** introduce security vulnerabilities. e06ae9f6d2 2008-05-22 drh: */ 28e56282c9 2008-05-23 drh: void configuration_cmd(void){ e06ae9f6d2 2008-05-22 drh: int n; e06ae9f6d2 2008-05-22 drh: const char *zMethod; e06ae9f6d2 2008-05-22 drh: if( g.argc<3 ){ bf75ea9852 2008-10-04 drh: usage("export|import|merge|pull|reset ..."); e06ae9f6d2 2008-05-22 drh: } e06ae9f6d2 2008-05-22 drh: db_find_and_open_repository(1); e06ae9f6d2 2008-05-22 drh: zMethod = g.argv[2]; e06ae9f6d2 2008-05-22 drh: n = strlen(zMethod); e06ae9f6d2 2008-05-22 drh: if( strncmp(zMethod, "export", n)==0 ){ bf75ea9852 2008-10-04 drh: int mask; e06ae9f6d2 2008-05-22 drh: if( g.argc!=5 ){ e06ae9f6d2 2008-05-22 drh: usage("export AREA FILENAME"); e06ae9f6d2 2008-05-22 drh: } e06ae9f6d2 2008-05-22 drh: mask = find_area(g.argv[3]); bf75ea9852 2008-10-04 drh: export_config(mask, g.argv[3], g.argv[4]); 28e56282c9 2008-05-23 drh: }else bf75ea9852 2008-10-04 drh: if( strncmp(zMethod, "import", n)==0 bf75ea9852 2008-10-04 drh: || strncmp(zMethod, "merge", n)==0 ){ 28e56282c9 2008-05-23 drh: Blob in; bf75ea9852 2008-10-04 drh: if( g.argc!=4 ) usage(mprintf("%s FILENAME",zMethod)); 28e56282c9 2008-05-23 drh: blob_read_from_file(&in, g.argv[3]); 28e56282c9 2008-05-23 drh: db_begin_transaction(); bf75ea9852 2008-10-04 drh: configure_prepare_to_receive(zMethod[0]=='i'); 28e56282c9 2008-05-23 drh: db_multi_exec("%s", blob_str(&in)); bf75ea9852 2008-10-04 drh: configure_finalize_receive(); 28e56282c9 2008-05-23 drh: db_end_transaction(0); 28e56282c9 2008-05-23 drh: }else 28e56282c9 2008-05-23 drh: if( strncmp(zMethod, "pull", n)==0 ){ 28e56282c9 2008-05-23 drh: int mask; bf75ea9852 2008-10-04 drh: const char *zServer; 28e56282c9 2008-05-23 drh: url_proxy_options(); bf75ea9852 2008-10-04 drh: if( g.argc!=4 && g.argc!=5 ){ bf75ea9852 2008-10-04 drh: usage("pull AREA ?URL?"); bf75ea9852 2008-10-04 drh: } 28e56282c9 2008-05-23 drh: mask = find_area(g.argv[3]); bf75ea9852 2008-10-04 drh: if( g.argc==5 ){ bf75ea9852 2008-10-04 drh: zServer = g.argv[4]; bf75ea9852 2008-10-04 drh: }else{ bf75ea9852 2008-10-04 drh: zServer = db_get("last-sync-url", 0); bf75ea9852 2008-10-04 drh: if( zServer==0 ){ bf75ea9852 2008-10-04 drh: fossil_fatal("no server specified"); e06ae9f6d2 2008-05-22 drh: } e06ae9f6d2 2008-05-22 drh: } bf75ea9852 2008-10-04 drh: url_parse(zServer); 28e56282c9 2008-05-23 drh: if( g.urlIsFile ){ 28e56282c9 2008-05-23 drh: fossil_fatal("network sync only"); e06ae9f6d2 2008-05-22 drh: } 28e56282c9 2008-05-23 drh: user_select(); 28e56282c9 2008-05-23 drh: client_sync(0,0,0,mask); e06ae9f6d2 2008-05-22 drh: }else e06ae9f6d2 2008-05-22 drh: if( strncmp(zMethod, "reset", n)==0 ){ 28e56282c9 2008-05-23 drh: int mask, i; bf75ea9852 2008-10-04 drh: char *zBackup; 28e56282c9 2008-05-23 drh: if( g.argc!=4 ) usage("reset AREA"); 28e56282c9 2008-05-23 drh: mask = find_area(g.argv[3]); bf75ea9852 2008-10-04 drh: zBackup = db_text(0, bf75ea9852 2008-10-04 drh: "SELECT strftime('config-backup-%%Y%%m%%d%%H%%M%%f','now')"); 28e56282c9 2008-05-23 drh: db_begin_transaction(); bf75ea9852 2008-10-04 drh: export_config(mask, g.argv[3], zBackup); 28e56282c9 2008-05-23 drh: for(i=0; i<count(aConfig); i++){ bf75ea9852 2008-10-04 drh: const char *zName = aConfig[i].zName; 28e56282c9 2008-05-23 drh: if( (aConfig[i].groupMask & mask)==0 ) continue; bf75ea9852 2008-10-04 drh: if( zName[0]!='@' ){ bf75ea9852 2008-10-04 drh: db_multi_exec("DELETE FROM config WHERE name=%Q", zName); bf75ea9852 2008-10-04 drh: }else if( strcmp(zName,"@user")==0 ){ bf75ea9852 2008-10-04 drh: db_multi_exec("DELETE FROM user"); bf75ea9852 2008-10-04 drh: db_create_default_users(); bf75ea9852 2008-10-04 drh: }else if( strcmp(zName,"@reportfmt")==0 ){ bf75ea9852 2008-10-04 drh: db_multi_exec("DELETE FROM reportfmt"); bf75ea9852 2008-10-04 drh: } 28e56282c9 2008-05-23 drh: } 28e56282c9 2008-05-23 drh: db_end_transaction(0); bf75ea9852 2008-10-04 drh: printf("Configuration reset to factory defaults.\n"); bf75ea9852 2008-10-04 drh: printf("To recover, use: %s %s import %s\n", bf75ea9852 2008-10-04 drh: g.argv[0], g.argv[1], zBackup); e06ae9f6d2 2008-05-22 drh: }else e06ae9f6d2 2008-05-22 drh: { bf75ea9852 2008-10-04 drh: fossil_fatal("METHOD should be one of: export import merge pull reset"); e06ae9f6d2 2008-05-22 drh: } e06ae9f6d2 2008-05-22 drh: }