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 */ 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 */ e06ae9f6d2 2008-05-22 drh: } aGroupName[] = { e06ae9f6d2 2008-05-22 drh: { "skin", CONFIGSET_SKIN }, e06ae9f6d2 2008-05-22 drh: { "ticket", CONFIGSET_TKT }, e06ae9f6d2 2008-05-22 drh: { "project", CONFIGSET_PROJ }, e06ae9f6d2 2008-05-22 drh: { "all", CONFIGSET_ALL }, 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. 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[] = { e06ae9f6d2 2008-05-22 drh: { "css", CONFIGSET_SKIN }, e06ae9f6d2 2008-05-22 drh: { "header", CONFIGSET_SKIN }, e06ae9f6d2 2008-05-22 drh: { "footer", CONFIGSET_SKIN }, e06ae9f6d2 2008-05-22 drh: { "project-name", CONFIGSET_PROJ }, e06ae9f6d2 2008-05-22 drh: { "project-description", CONFIGSET_PROJ }, e06ae9f6d2 2008-05-22 drh: { "index-page", CONFIGSET_SKIN }, e06ae9f6d2 2008-05-22 drh: { "timeline-block-markup", CONFIGSET_SKIN }, e06ae9f6d2 2008-05-22 drh: { "timeline-max-comment", CONFIGSET_SKIN }, 86db6fa150 2008-05-28 drh: { "ticket-table", CONFIGSET_TKT }, 86db6fa150 2008-05-28 drh: { "ticket-common", CONFIGSET_TKT }, 86db6fa150 2008-05-28 drh: { "ticket-newpage", CONFIGSET_TKT }, 86db6fa150 2008-05-28 drh: { "ticket-viewpage", CONFIGSET_TKT }, 86db6fa150 2008-05-28 drh: { "ticket-editpage", CONFIGSET_TKT }, 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: /* e06ae9f6d2 2008-05-22 drh: ** Return TRUE if a particular configuration parameter zName is e06ae9f6d2 2008-05-22 drh: ** safely exportable. 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 ){ 28e56282c9 2008-05-23 drh: return aConfig[i].groupMask; 28e56282c9 2008-05-23 drh: } e06ae9f6d2 2008-05-22 drh: } e06ae9f6d2 2008-05-22 drh: return 0; 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: } 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: 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: ** e06ae9f6d2 2008-05-22 drh: ** Where METHOD is one of: export import 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 e06ae9f6d2 2008-05-22 drh: ** configuration. Warning: Do not read a configuration from e06ae9f6d2 2008-05-22 drh: ** an untrusted source since the configuration is not checked e06ae9f6d2 2008-05-22 drh: ** for safety and can introduce security threats. e06ae9f6d2 2008-05-22 drh: ** 28e56282c9 2008-05-23 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 e06ae9f6d2 2008-05-22 drh: ** identified by URL. AREA is as in "export". 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. 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 ){ e06ae9f6d2 2008-05-22 drh: usage("METHOD ..."); 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 ){ e06ae9f6d2 2008-05-22 drh: int i; e06ae9f6d2 2008-05-22 drh: int mask; e06ae9f6d2 2008-05-22 drh: const char *zSep; e06ae9f6d2 2008-05-22 drh: Blob sql; e06ae9f6d2 2008-05-22 drh: Stmt q; e06ae9f6d2 2008-05-22 drh: Blob out; 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]); e06ae9f6d2 2008-05-22 drh: blob_zero(&sql); e06ae9f6d2 2008-05-22 drh: blob_zero(&out); e06ae9f6d2 2008-05-22 drh: blob_appendf(&sql, e06ae9f6d2 2008-05-22 drh: "SELECT 'REPLACE INTO config(name,value) VALUES('''" e06ae9f6d2 2008-05-22 drh: " || name || ''',' || quote(value) || ');'" e06ae9f6d2 2008-05-22 drh: " FROM config WHERE name IN " e06ae9f6d2 2008-05-22 drh: ); e06ae9f6d2 2008-05-22 drh: zSep = "("; 28e56282c9 2008-05-23 drh: for(i=0; i<count(aConfig); i++){ 28e56282c9 2008-05-23 drh: if( aConfig[i].groupMask & mask ){ 28e56282c9 2008-05-23 drh: blob_appendf(&sql, "%s'%s'", zSep, aConfig[i].zName); e06ae9f6d2 2008-05-22 drh: zSep = ","; e06ae9f6d2 2008-05-22 drh: } e06ae9f6d2 2008-05-22 drh: } e06ae9f6d2 2008-05-22 drh: blob_appendf(&sql, ") ORDER BY name"); e06ae9f6d2 2008-05-22 drh: db_prepare(&q, blob_str(&sql)); e06ae9f6d2 2008-05-22 drh: blob_reset(&sql); e06ae9f6d2 2008-05-22 drh: blob_appendf(&out, e06ae9f6d2 2008-05-22 drh: "-- The \"%s\" configuration exported from\n" e06ae9f6d2 2008-05-22 drh: "-- repository \"%s\"\n" 28e56282c9 2008-05-23 drh: "-- on %s\n", e06ae9f6d2 2008-05-22 drh: g.argv[3], g.zRepositoryName, e06ae9f6d2 2008-05-22 drh: db_text(0, "SELECT datetime('now')") e06ae9f6d2 2008-05-22 drh: ); e06ae9f6d2 2008-05-22 drh: while( db_step(&q)==SQLITE_ROW ){ e06ae9f6d2 2008-05-22 drh: blob_appendf(&out, "%s\n", db_column_text(&q, 0)); e06ae9f6d2 2008-05-22 drh: } e06ae9f6d2 2008-05-22 drh: db_finalize(&q); e06ae9f6d2 2008-05-22 drh: blob_write_to_file(&out, g.argv[4]); e06ae9f6d2 2008-05-22 drh: blob_reset(&out); e06ae9f6d2 2008-05-22 drh: }else e06ae9f6d2 2008-05-22 drh: if( strncmp(zMethod, "import", n)==0 ){ 28e56282c9 2008-05-23 drh: Blob in; 28e56282c9 2008-05-23 drh: if( g.argc!=4 ) usage("import FILENAME"); 28e56282c9 2008-05-23 drh: blob_read_from_file(&in, g.argv[3]); 28e56282c9 2008-05-23 drh: db_begin_transaction(); 28e56282c9 2008-05-23 drh: db_multi_exec("%s", blob_str(&in)); 28e56282c9 2008-05-23 drh: db_end_transaction(0); e06ae9f6d2 2008-05-22 drh: }else e06ae9f6d2 2008-05-22 drh: if( strncmp(zMethod, "pull", n)==0 ){ 28e56282c9 2008-05-23 drh: int mask; 28e56282c9 2008-05-23 drh: url_proxy_options(); 28e56282c9 2008-05-23 drh: if( g.argc!=5 ) usage("pull AREA URL"); 28e56282c9 2008-05-23 drh: mask = find_area(g.argv[3]); 28e56282c9 2008-05-23 drh: url_parse(g.argv[4]); 28e56282c9 2008-05-23 drh: if( g.urlIsFile ){ 28e56282c9 2008-05-23 drh: fossil_fatal("network sync only"); 28e56282c9 2008-05-23 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; 28e56282c9 2008-05-23 drh: if( g.argc!=4 ) usage("reset AREA"); 28e56282c9 2008-05-23 drh: mask = find_area(g.argv[3]); 28e56282c9 2008-05-23 drh: db_begin_transaction(); 28e56282c9 2008-05-23 drh: for(i=0; i<count(aConfig); i++){ 28e56282c9 2008-05-23 drh: if( (aConfig[i].groupMask & mask)==0 ) continue; 28e56282c9 2008-05-23 drh: db_multi_exec("DELETE FROM config WHERE name=%Q", aConfig[i].zName); 28e56282c9 2008-05-23 drh: } 28e56282c9 2008-05-23 drh: db_end_transaction(0); e06ae9f6d2 2008-05-22 drh: }else e06ae9f6d2 2008-05-22 drh: { e06ae9f6d2 2008-05-22 drh: fossil_fatal("METHOD should be one of: export import pull reset"); e06ae9f6d2 2008-05-22 drh: } e06ae9f6d2 2008-05-22 drh: }