Fossil SCM

The "configuration" command will now sync ticket report formats, shunned UUIDs, and user information (but not user passwords). Added the "config merge" method. Fix an initialization bug that was given Admin privilege to anonymous by default.

drh 2008-10-04 20:40 trunk
Commit bf75ea9852c061e3cbe1372025711682e0e00a5f
3 files changed +292 -71 +33 -26 +29 -9
+292 -71
--- src/configure.c
+++ src/configure.c
@@ -35,10 +35,12 @@
3535
** groupings:
3636
*/
3737
#define CONFIGSET_SKIN 0x000001 /* WWW interface appearance */
3838
#define CONFIGSET_TKT 0x000002 /* Ticket configuration */
3939
#define CONFIGSET_PROJ 0x000004 /* Project name */
40
+#define CONFIGSET_SHUN 0x000008 /* Shun settings */
41
+#define CONFIGSET_USER 0x000010 /* The USER table */
4042
4143
#define CONFIGSET_ALL 0xffffff /* Everything */
4244
4345
#endif /* INTERFACE */
4446
@@ -46,41 +48,51 @@
4648
** Names of the configuration sets
4749
*/
4850
static struct {
4951
const char *zName; /* Name of the configuration set */
5052
int groupMask; /* Mask for that configuration set */
53
+ const char *zHelp; /* What it does */
5154
} aGroupName[] = {
52
- { "skin", CONFIGSET_SKIN },
53
- { "ticket", CONFIGSET_TKT },
54
- { "project", CONFIGSET_PROJ },
55
- { "all", CONFIGSET_ALL },
55
+ { "skin", CONFIGSET_SKIN, "Web interface apparance settings" },
56
+ { "ticket", CONFIGSET_TKT, "Ticket setup", },
57
+ { "project", CONFIGSET_PROJ, "Project name and description" },
58
+ { "shun", CONFIGSET_SHUN, "List of shunned artifacts" },
59
+ { "user", CONFIGSET_USER, "Users and privilege settings" },
60
+ { "all", CONFIGSET_ALL, "All of the above" },
5661
};
5762
5863
5964
/*
6065
** The following is a list of settings that we are willing to
61
-** transfer.
66
+** transfer.
67
+**
68
+** Setting names that begin with an alphabetic characters refer to
69
+** single entries in the CONFIG table. Setting names that begin with
70
+** "@" are for special processing.
6271
*/
6372
static struct {
6473
const char *zName; /* Name of the configuration parameter */
6574
int groupMask; /* Which config groups is it part of */
6675
} aConfig[] = {
67
- { "css", CONFIGSET_SKIN },
68
- { "header", CONFIGSET_SKIN },
69
- { "footer", CONFIGSET_SKIN },
70
- { "project-name", CONFIGSET_PROJ },
71
- { "project-description", CONFIGSET_PROJ },
72
- { "index-page", CONFIGSET_SKIN },
73
- { "timeline-block-markup", CONFIGSET_SKIN },
74
- { "timeline-max-comment", CONFIGSET_SKIN },
75
- { "ticket-table", CONFIGSET_TKT },
76
- { "ticket-common", CONFIGSET_TKT },
77
- { "ticket-newpage", CONFIGSET_TKT },
78
- { "ticket-viewpage", CONFIGSET_TKT },
79
- { "ticket-editpage", CONFIGSET_TKT },
80
- { "ticket-report-template", CONFIGSET_TKT },
81
- { "ticket-key-template", CONFIGSET_TKT },
76
+ { "css", CONFIGSET_SKIN },
77
+ { "header", CONFIGSET_SKIN },
78
+ { "footer", CONFIGSET_SKIN },
79
+ { "project-name", CONFIGSET_PROJ },
80
+ { "project-description", CONFIGSET_PROJ },
81
+ { "index-page", CONFIGSET_SKIN },
82
+ { "timeline-block-markup", CONFIGSET_SKIN },
83
+ { "timeline-max-comment", CONFIGSET_SKIN },
84
+ { "ticket-table", CONFIGSET_TKT },
85
+ { "ticket-common", CONFIGSET_TKT },
86
+ { "ticket-newpage", CONFIGSET_TKT },
87
+ { "ticket-viewpage", CONFIGSET_TKT },
88
+ { "ticket-editpage", CONFIGSET_TKT },
89
+ { "ticket-report-template", CONFIGSET_TKT },
90
+ { "ticket-key-template", CONFIGSET_TKT },
91
+ { "@reportfmt", CONFIGSET_TKT },
92
+ { "@user", CONFIGSET_USER },
93
+ { "@shun", CONFIGSET_SHUN },
8294
};
8395
static int iConfig = 0;
8496
8597
/*
8698
** Return name of first configuration property matching the given mask.
@@ -99,22 +111,184 @@
99111
}
100112
return 0;
101113
}
102114
103115
/*
104
-** Return TRUE if a particular configuration parameter zName is
105
-** safely exportable.
116
+** Return the mask for the named configuration parameter if it can be
117
+** safely exported. Return 0 if the parameter is not safe to export.
106118
*/
107119
int configure_is_exportable(const char *zName){
108120
int i;
109121
for(i=0; i<count(aConfig); i++){
110122
if( strcmp(zName, aConfig[i].zName)==0 ){
111
- return aConfig[i].groupMask;
123
+ int m = aConfig[i].groupMask;
124
+ if( !g.okAdmin ){
125
+ m &= ~CONFIGSET_USER;
126
+ }
127
+ return m;
112128
}
113129
}
114130
return 0;
115131
}
132
+
133
+/*
134
+** zName is one of the special configuration names that refers to an entire
135
+** table rather than a single entry in CONFIG. Special names are "@reportfmt"
136
+** and "@shun" and "@user". This routine writes SQL text into pOut that when
137
+** evaluated will populate the corresponding table with data.
138
+*/
139
+void configure_render_special_name(const char *zName, Blob *pOut){
140
+ Stmt q;
141
+ if( strcmp(zName, "@shun")==0 ){
142
+ db_prepare(&q, "SELECT uuid FROM shun");
143
+ while( db_step(&q)==SQLITE_ROW ){
144
+ blob_appendf(pOut, "INSERT OR IGNORE INTO shun VALUES('%s');\n",
145
+ db_column_text(&q, 0)
146
+ );
147
+ }
148
+ db_finalize(&q);
149
+ }else if( strcmp(zName, "@reportfmt")==0 ){
150
+ db_prepare(&q, "SELECT title, cols, sqlcode FROM reportfmt");
151
+ while( db_step(&q)==SQLITE_ROW ){
152
+ blob_appendf(pOut, "INSERT INTO _xfer_reportfmt(title,cols,sqlcode) "
153
+ " VALUES(%Q,%Q,%Q);\n",
154
+ db_column_text(&q, 0),
155
+ db_column_text(&q, 1),
156
+ db_column_text(&q, 2)
157
+ );
158
+ }
159
+ db_finalize(&q);
160
+ }else if( strcmp(zName, "@user")==0 ){
161
+ db_prepare(&q, "SELECT login, cap, info, quote(photo) FROM user");
162
+ while( db_step(&q)==SQLITE_ROW ){
163
+ blob_appendf(pOut, "INSERT INTO _xfer_user(login,cap,info,photo) "
164
+ " VALUES(%Q,%Q,%Q,%s);\n",
165
+ db_column_text(&q, 0),
166
+ db_column_text(&q, 1),
167
+ db_column_text(&q, 2),
168
+ db_column_text(&q, 3)
169
+ );
170
+ }
171
+ db_finalize(&q);
172
+ }
173
+}
174
+
175
+/*
176
+** Two SQL functions:
177
+**
178
+** flag_test(int)
179
+** flag_clear(int)
180
+**
181
+** The flag_test() function takes the integer valued argument and
182
+** ANDs it against the static variable "flag_value" below. The
183
+** function returns TRUE or false depending on the result. The
184
+** flag_clear() function masks off the bits from "flag_value" that
185
+** are given in the argument.
186
+**
187
+** These functions are used below in the WHEN clause of a trigger to
188
+** get the trigger to fire exactly once.
189
+*/
190
+static int flag_value = 0xffff;
191
+static void flag_test_function(
192
+ sqlite3_context *context,
193
+ int argc,
194
+ sqlite3_value **argv
195
+){
196
+ int m = sqlite3_value_int(argv[0]);
197
+ sqlite3_result_int(context, (flag_value&m)!=0 );
198
+}
199
+static void flag_clear_function(
200
+ sqlite3_context *context,
201
+ int argc,
202
+ sqlite3_value **argv
203
+){
204
+ int m = sqlite3_value_int(argv[0]);
205
+ flag_value &= ~m;
206
+}
207
+
208
+/*
209
+** Create the temporary _xfer_reportfmt and _xfer_user tables that are
210
+** necessary in order to evalute the SQL text generated by the
211
+** configure_render_special_name() routine.
212
+**
213
+** If replaceFlag is true, then the setup is such that the content in
214
+** the SQL text will completely replace the current repository configuration.
215
+** If replaceFlag is false, then the SQL text will be merged with the
216
+** existing configuration. When merging, existing values take priority
217
+** over SQL text values.
218
+*/
219
+void configure_prepare_to_receive(int replaceFlag){
220
+ static const char zSQL1[] =
221
+ @ CREATE TEMP TABLE _xfer_reportfmt(
222
+ @ rn integer primary key, -- Report number
223
+ @ owner text, -- Owner of this report format (not used)
224
+ @ title text UNIQUE ON CONFLICT IGNORE, -- Title of this report
225
+ @ cols text, -- A color-key specification
226
+ @ sqlcode text -- An SQL SELECT statement for this report
227
+ @ );
228
+ @ CREATE TEMP TABLE _xfer_user(
229
+ @ uid INTEGER PRIMARY KEY, -- User ID
230
+ @ login TEXT UNIQUE ON CONFLICT IGNORE, -- login name of the user
231
+ @ pw TEXT, -- password
232
+ @ cap TEXT, -- Capabilities of this user
233
+ @ cookie TEXT, -- WWW login cookie
234
+ @ ipaddr TEXT, -- IP address for which cookie is valid
235
+ @ cexpire DATETIME, -- Time when cookie expires
236
+ @ info TEXT, -- contact information
237
+ @ photo BLOB -- JPEG image of this user
238
+ @ );
239
+ @ INSERT INTO _xfer_reportfmt SELECT * FROM reportfmt;
240
+ @ INSERT INTO _xfer_user SELECT * FROM user;
241
+ ;
242
+ db_multi_exec(zSQL1);
243
+
244
+ /* When the replace flag is set, add triggers that run the first time
245
+ ** that new data is seen. The triggers run only once and delete all the
246
+ ** existing data.
247
+ */
248
+ if( replaceFlag ){
249
+ static const char zSQL2[] =
250
+ @ CREATE TRIGGER _xfer_r1 BEFORE INSERT ON _xfer_reportfmt
251
+ @ WHEN flag_test(1) BEGIN
252
+ @ DELETE FROM _xfer_reportfmt;
253
+ @ SELECT flag_clear(1);
254
+ @ END;
255
+ @ CREATE TRIGGER _xfer_r2 BEFORE INSERT ON _xfer_user
256
+ @ WHEN flag_test(2) BEGIN
257
+ @ DELETE FROM _xfer_user;
258
+ @ SELECT flag_clear(2);
259
+ @ END;
260
+ @ CREATE TEMP TRIGGER _xfer_r3 BEFORE INSERT ON shun
261
+ @ WHEN flag_test(4) BEGIN
262
+ @ DELETE FROM shun;
263
+ @ SELECT flag_clear(4);
264
+ @ END;
265
+ ;
266
+ sqlite3_create_function(g.db, "flag_test", 1, SQLITE_UTF8, 0,
267
+ flag_test_function, 0, 0);
268
+ sqlite3_create_function(g.db, "flag_clear", 1, SQLITE_UTF8, 0,
269
+ flag_clear_function, 0, 0);
270
+ flag_value = 0xffff;
271
+ db_multi_exec(zSQL2);
272
+ }
273
+}
274
+
275
+/*
276
+** After receiving configuration data, call this routine to transfer
277
+** the results into the main database.
278
+*/
279
+void configure_finalize_receive(void){
280
+ static const char zSQL[] =
281
+ @ DELETE FROM user;
282
+ @ INSERT INTO user SELECT * FROM _xfer_user;
283
+ @ DELETE FROM reportfmt;
284
+ @ INSERT INTO reportfmt SELECT * FROM _xfer_reportfmt;
285
+ @ DROP TABLE _xfer_user;
286
+ @ DROP TABLE _xfer_reportfmt;
287
+ ;
288
+ db_multi_exec(zSQL);
289
+}
116290
117291
/*
118292
** Identify a configuration group by name. Return its mask.
119293
** Throw an error if no match.
120294
*/
@@ -124,21 +298,64 @@
124298
for(i=0; i<count(aGroupName); i++){
125299
if( strncmp(z, aGroupName[i].zName, n)==0 ){
126300
return aGroupName[i].groupMask;
127301
}
128302
}
303
+ printf("Available configuration areas:\n");
304
+ for(i=0; i<count(aGroupName); i++){
305
+ printf(" %-10s %s\n", aGroupName[i].zName, aGroupName[i].zHelp);
306
+ }
129307
fossil_fatal("no such configuration area: \"%s\"", z);
130308
return 0;
131309
}
310
+
311
+/*
312
+** Write SQL text into file zFilename that will restore the configuration
313
+** area identified by mask to its current state from any other state.
314
+*/
315
+static void export_config(
316
+ int mask, /* Mask indicating which configuration to export */
317
+ const char *zMask, /* Name of the configuration */
318
+ const char *zFilename /* Write into this file */
319
+){
320
+ int i;
321
+ Blob out;
322
+ blob_zero(&out);
323
+ blob_appendf(&out,
324
+ "-- The \"%s\" configuration exported from\n"
325
+ "-- repository \"%s\"\n"
326
+ "-- on %s\n",
327
+ zMask, g.zRepositoryName,
328
+ db_text(0, "SELECT datetime('now')")
329
+ );
330
+ for(i=0; i<count(aConfig); i++){
331
+ if( (aConfig[i].groupMask & mask)!=0 ){
332
+ const char *zName = aConfig[i].zName;
333
+ if( zName[0]!='@' ){
334
+ char *zValue = db_text(0,
335
+ "SELECT value FROM config WHERE name=%Q", zName);
336
+ if( zValue ){
337
+ blob_appendf(&out,"REPLACE INTO config VALUES(%Q,%Q);\n",
338
+ zName, zValue);
339
+ }
340
+ free(zValue);
341
+ }else{
342
+ configure_render_special_name(zName, &out);
343
+ }
344
+ }
345
+ }
346
+ blob_write_to_file(&out, zFilename);
347
+ blob_reset(&out);
348
+}
132349
133350
134351
/*
135352
** COMMAND: configuration
136353
**
137354
** Usage: %fossil configure METHOD ...
138355
**
139
-** Where METHOD is one of: export import pull reset. All methods
356
+** Where METHOD is one of: export import merge pull reset. All methods
140357
** accept the -R or --repository option to specific a repository.
141358
**
142359
** %fossil configuration export AREA FILENAME
143360
**
144361
** Write to FILENAME exported configuraton information for AREA.
@@ -145,104 +362,108 @@
145362
** AREA can be one of: all ticket skin project
146363
**
147364
** %fossil configuration import FILENAME
148365
**
149366
** Read a configuration from FILENAME, overwriting the current
150
-** configuration. Warning: Do not read a configuration from
151
-** an untrusted source since the configuration is not checked
152
-** for safety and can introduce security threats.
367
+** configuration.
368
+**
369
+** %fossil configuration merge FILENAME
370
+**
371
+** Read a configuration from FILENAME and merge its values into
372
+** the current configuration. Existing values take priority over
373
+** values read from FILENAME.
153374
**
154
-** %fossil configuration pull AREA URL
375
+** %fossil configuration pull AREA ?URL?
155376
**
156377
** Pull and install the configuration from a different server
157
-** identified by URL. AREA is as in "export".
378
+** identified by URL. If no URL is specified, then the default
379
+** server is used.
158380
**
159381
** %fossil configuration reset AREA
160382
**
161383
** Restore the configuration to the default. AREA as above.
384
+**
385
+** WARNING: Do not import, merge, or pull configurations from an untrusted
386
+** source. The inbound configuration is not checked for safety and can
387
+** introduce security vulnerabilities.
162388
*/
163389
void configuration_cmd(void){
164390
int n;
165391
const char *zMethod;
166392
if( g.argc<3 ){
167
- usage("METHOD ...");
393
+ usage("export|import|merge|pull|reset ...");
168394
}
169395
db_find_and_open_repository(1);
170396
zMethod = g.argv[2];
171397
n = strlen(zMethod);
172398
if( strncmp(zMethod, "export", n)==0 ){
173
- int i;
174399
int mask;
175
- const char *zSep;
176
- Blob sql;
177
- Stmt q;
178
- Blob out;
179400
if( g.argc!=5 ){
180401
usage("export AREA FILENAME");
181402
}
182403
mask = find_area(g.argv[3]);
183
- blob_zero(&sql);
184
- blob_zero(&out);
185
- blob_appendf(&sql,
186
- "SELECT 'REPLACE INTO config(name,value) VALUES('''"
187
- " || name || ''',' || quote(value) || ');'"
188
- " FROM config WHERE name IN "
189
- );
190
- zSep = "(";
191
- for(i=0; i<count(aConfig); i++){
192
- if( aConfig[i].groupMask & mask ){
193
- blob_appendf(&sql, "%s'%s'", zSep, aConfig[i].zName);
194
- zSep = ",";
195
- }
196
- }
197
- blob_appendf(&sql, ") ORDER BY name");
198
- db_prepare(&q, blob_str(&sql));
199
- blob_reset(&sql);
200
- blob_appendf(&out,
201
- "-- The \"%s\" configuration exported from\n"
202
- "-- repository \"%s\"\n"
203
- "-- on %s\n",
204
- g.argv[3], g.zRepositoryName,
205
- db_text(0, "SELECT datetime('now')")
206
- );
207
- while( db_step(&q)==SQLITE_ROW ){
208
- blob_appendf(&out, "%s\n", db_column_text(&q, 0));
209
- }
210
- db_finalize(&q);
211
- blob_write_to_file(&out, g.argv[4]);
212
- blob_reset(&out);
404
+ export_config(mask, g.argv[3], g.argv[4]);
213405
}else
214
- if( strncmp(zMethod, "import", n)==0 ){
406
+ if( strncmp(zMethod, "import", n)==0
407
+ || strncmp(zMethod, "merge", n)==0 ){
215408
Blob in;
216
- if( g.argc!=4 ) usage("import FILENAME");
409
+ if( g.argc!=4 ) usage(mprintf("%s FILENAME",zMethod));
217410
blob_read_from_file(&in, g.argv[3]);
218411
db_begin_transaction();
412
+ configure_prepare_to_receive(zMethod[0]=='i');
219413
db_multi_exec("%s", blob_str(&in));
414
+ configure_finalize_receive();
220415
db_end_transaction(0);
221416
}else
222417
if( strncmp(zMethod, "pull", n)==0 ){
223418
int mask;
419
+ const char *zServer;
224420
url_proxy_options();
225
- if( g.argc!=5 ) usage("pull AREA URL");
421
+ if( g.argc!=4 && g.argc!=5 ){
422
+ usage("pull AREA ?URL?");
423
+ }
226424
mask = find_area(g.argv[3]);
227
- url_parse(g.argv[4]);
425
+ if( g.argc==5 ){
426
+ zServer = g.argv[4];
427
+ }else{
428
+ zServer = db_get("last-sync-url", 0);
429
+ if( zServer==0 ){
430
+ fossil_fatal("no server specified");
431
+ }
432
+ }
433
+ url_parse(zServer);
228434
if( g.urlIsFile ){
229435
fossil_fatal("network sync only");
230436
}
231437
user_select();
232438
client_sync(0,0,0,mask);
233439
}else
234440
if( strncmp(zMethod, "reset", n)==0 ){
235441
int mask, i;
442
+ char *zBackup;
236443
if( g.argc!=4 ) usage("reset AREA");
237444
mask = find_area(g.argv[3]);
445
+ zBackup = db_text(0,
446
+ "SELECT strftime('config-backup-%%Y%%m%%d%%H%%M%%f','now')");
238447
db_begin_transaction();
448
+ export_config(mask, g.argv[3], zBackup);
239449
for(i=0; i<count(aConfig); i++){
450
+ const char *zName = aConfig[i].zName;
240451
if( (aConfig[i].groupMask & mask)==0 ) continue;
241
- db_multi_exec("DELETE FROM config WHERE name=%Q", aConfig[i].zName);
452
+ if( zName[0]!='@' ){
453
+ db_multi_exec("DELETE FROM config WHERE name=%Q", zName);
454
+ }else if( strcmp(zName,"@user")==0 ){
455
+ db_multi_exec("DELETE FROM user");
456
+ db_create_default_users();
457
+ }else if( strcmp(zName,"@reportfmt")==0 ){
458
+ db_multi_exec("DELETE FROM reportfmt");
459
+ }
242460
}
243461
db_end_transaction(0);
462
+ printf("Configuration reset to factory defaults.\n");
463
+ printf("To recover, use: %s %s import %s\n",
464
+ g.argv[0], g.argv[1], zBackup);
244465
}else
245466
{
246
- fossil_fatal("METHOD should be one of: export import pull reset");
467
+ fossil_fatal("METHOD should be one of: export import merge pull reset");
247468
}
248469
}
249470
--- src/configure.c
+++ src/configure.c
@@ -35,10 +35,12 @@
35 ** groupings:
36 */
37 #define CONFIGSET_SKIN 0x000001 /* WWW interface appearance */
38 #define CONFIGSET_TKT 0x000002 /* Ticket configuration */
39 #define CONFIGSET_PROJ 0x000004 /* Project name */
 
 
40
41 #define CONFIGSET_ALL 0xffffff /* Everything */
42
43 #endif /* INTERFACE */
44
@@ -46,41 +48,51 @@
46 ** Names of the configuration sets
47 */
48 static struct {
49 const char *zName; /* Name of the configuration set */
50 int groupMask; /* Mask for that configuration set */
 
51 } aGroupName[] = {
52 { "skin", CONFIGSET_SKIN },
53 { "ticket", CONFIGSET_TKT },
54 { "project", CONFIGSET_PROJ },
55 { "all", CONFIGSET_ALL },
 
 
56 };
57
58
59 /*
60 ** The following is a list of settings that we are willing to
61 ** transfer.
 
 
 
 
62 */
63 static struct {
64 const char *zName; /* Name of the configuration parameter */
65 int groupMask; /* Which config groups is it part of */
66 } aConfig[] = {
67 { "css", CONFIGSET_SKIN },
68 { "header", CONFIGSET_SKIN },
69 { "footer", CONFIGSET_SKIN },
70 { "project-name", CONFIGSET_PROJ },
71 { "project-description", CONFIGSET_PROJ },
72 { "index-page", CONFIGSET_SKIN },
73 { "timeline-block-markup", CONFIGSET_SKIN },
74 { "timeline-max-comment", CONFIGSET_SKIN },
75 { "ticket-table", CONFIGSET_TKT },
76 { "ticket-common", CONFIGSET_TKT },
77 { "ticket-newpage", CONFIGSET_TKT },
78 { "ticket-viewpage", CONFIGSET_TKT },
79 { "ticket-editpage", CONFIGSET_TKT },
80 { "ticket-report-template", CONFIGSET_TKT },
81 { "ticket-key-template", CONFIGSET_TKT },
 
 
 
82 };
83 static int iConfig = 0;
84
85 /*
86 ** Return name of first configuration property matching the given mask.
@@ -99,22 +111,184 @@
99 }
100 return 0;
101 }
102
103 /*
104 ** Return TRUE if a particular configuration parameter zName is
105 ** safely exportable.
106 */
107 int configure_is_exportable(const char *zName){
108 int i;
109 for(i=0; i<count(aConfig); i++){
110 if( strcmp(zName, aConfig[i].zName)==0 ){
111 return aConfig[i].groupMask;
 
 
 
 
112 }
113 }
114 return 0;
115 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
116
117 /*
118 ** Identify a configuration group by name. Return its mask.
119 ** Throw an error if no match.
120 */
@@ -124,21 +298,64 @@
124 for(i=0; i<count(aGroupName); i++){
125 if( strncmp(z, aGroupName[i].zName, n)==0 ){
126 return aGroupName[i].groupMask;
127 }
128 }
 
 
 
 
129 fossil_fatal("no such configuration area: \"%s\"", z);
130 return 0;
131 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
132
133
134 /*
135 ** COMMAND: configuration
136 **
137 ** Usage: %fossil configure METHOD ...
138 **
139 ** Where METHOD is one of: export import pull reset. All methods
140 ** accept the -R or --repository option to specific a repository.
141 **
142 ** %fossil configuration export AREA FILENAME
143 **
144 ** Write to FILENAME exported configuraton information for AREA.
@@ -145,104 +362,108 @@
145 ** AREA can be one of: all ticket skin project
146 **
147 ** %fossil configuration import FILENAME
148 **
149 ** Read a configuration from FILENAME, overwriting the current
150 ** configuration. Warning: Do not read a configuration from
151 ** an untrusted source since the configuration is not checked
152 ** for safety and can introduce security threats.
 
 
 
 
153 **
154 ** %fossil configuration pull AREA URL
155 **
156 ** Pull and install the configuration from a different server
157 ** identified by URL. AREA is as in "export".
 
158 **
159 ** %fossil configuration reset AREA
160 **
161 ** Restore the configuration to the default. AREA as above.
 
 
 
 
162 */
163 void configuration_cmd(void){
164 int n;
165 const char *zMethod;
166 if( g.argc<3 ){
167 usage("METHOD ...");
168 }
169 db_find_and_open_repository(1);
170 zMethod = g.argv[2];
171 n = strlen(zMethod);
172 if( strncmp(zMethod, "export", n)==0 ){
173 int i;
174 int mask;
175 const char *zSep;
176 Blob sql;
177 Stmt q;
178 Blob out;
179 if( g.argc!=5 ){
180 usage("export AREA FILENAME");
181 }
182 mask = find_area(g.argv[3]);
183 blob_zero(&sql);
184 blob_zero(&out);
185 blob_appendf(&sql,
186 "SELECT 'REPLACE INTO config(name,value) VALUES('''"
187 " || name || ''',' || quote(value) || ');'"
188 " FROM config WHERE name IN "
189 );
190 zSep = "(";
191 for(i=0; i<count(aConfig); i++){
192 if( aConfig[i].groupMask & mask ){
193 blob_appendf(&sql, "%s'%s'", zSep, aConfig[i].zName);
194 zSep = ",";
195 }
196 }
197 blob_appendf(&sql, ") ORDER BY name");
198 db_prepare(&q, blob_str(&sql));
199 blob_reset(&sql);
200 blob_appendf(&out,
201 "-- The \"%s\" configuration exported from\n"
202 "-- repository \"%s\"\n"
203 "-- on %s\n",
204 g.argv[3], g.zRepositoryName,
205 db_text(0, "SELECT datetime('now')")
206 );
207 while( db_step(&q)==SQLITE_ROW ){
208 blob_appendf(&out, "%s\n", db_column_text(&q, 0));
209 }
210 db_finalize(&q);
211 blob_write_to_file(&out, g.argv[4]);
212 blob_reset(&out);
213 }else
214 if( strncmp(zMethod, "import", n)==0 ){
 
215 Blob in;
216 if( g.argc!=4 ) usage("import FILENAME");
217 blob_read_from_file(&in, g.argv[3]);
218 db_begin_transaction();
 
219 db_multi_exec("%s", blob_str(&in));
 
220 db_end_transaction(0);
221 }else
222 if( strncmp(zMethod, "pull", n)==0 ){
223 int mask;
 
224 url_proxy_options();
225 if( g.argc!=5 ) usage("pull AREA URL");
 
 
226 mask = find_area(g.argv[3]);
227 url_parse(g.argv[4]);
 
 
 
 
 
 
 
 
228 if( g.urlIsFile ){
229 fossil_fatal("network sync only");
230 }
231 user_select();
232 client_sync(0,0,0,mask);
233 }else
234 if( strncmp(zMethod, "reset", n)==0 ){
235 int mask, i;
 
236 if( g.argc!=4 ) usage("reset AREA");
237 mask = find_area(g.argv[3]);
 
 
238 db_begin_transaction();
 
239 for(i=0; i<count(aConfig); i++){
 
240 if( (aConfig[i].groupMask & mask)==0 ) continue;
241 db_multi_exec("DELETE FROM config WHERE name=%Q", aConfig[i].zName);
 
 
 
 
 
 
 
242 }
243 db_end_transaction(0);
 
 
 
244 }else
245 {
246 fossil_fatal("METHOD should be one of: export import pull reset");
247 }
248 }
249
--- src/configure.c
+++ src/configure.c
@@ -35,10 +35,12 @@
35 ** groupings:
36 */
37 #define CONFIGSET_SKIN 0x000001 /* WWW interface appearance */
38 #define CONFIGSET_TKT 0x000002 /* Ticket configuration */
39 #define CONFIGSET_PROJ 0x000004 /* Project name */
40 #define CONFIGSET_SHUN 0x000008 /* Shun settings */
41 #define CONFIGSET_USER 0x000010 /* The USER table */
42
43 #define CONFIGSET_ALL 0xffffff /* Everything */
44
45 #endif /* INTERFACE */
46
@@ -46,41 +48,51 @@
48 ** Names of the configuration sets
49 */
50 static struct {
51 const char *zName; /* Name of the configuration set */
52 int groupMask; /* Mask for that configuration set */
53 const char *zHelp; /* What it does */
54 } aGroupName[] = {
55 { "skin", CONFIGSET_SKIN, "Web interface apparance settings" },
56 { "ticket", CONFIGSET_TKT, "Ticket setup", },
57 { "project", CONFIGSET_PROJ, "Project name and description" },
58 { "shun", CONFIGSET_SHUN, "List of shunned artifacts" },
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
66 ** transfer.
67 **
68 ** Setting names that begin with an alphabetic characters refer to
69 ** single entries in the CONFIG table. Setting names that begin with
70 ** "@" are for special processing.
71 */
72 static struct {
73 const char *zName; /* Name of the configuration parameter */
74 int groupMask; /* Which config groups is it part of */
75 } aConfig[] = {
76 { "css", CONFIGSET_SKIN },
77 { "header", CONFIGSET_SKIN },
78 { "footer", CONFIGSET_SKIN },
79 { "project-name", CONFIGSET_PROJ },
80 { "project-description", CONFIGSET_PROJ },
81 { "index-page", CONFIGSET_SKIN },
82 { "timeline-block-markup", CONFIGSET_SKIN },
83 { "timeline-max-comment", CONFIGSET_SKIN },
84 { "ticket-table", CONFIGSET_TKT },
85 { "ticket-common", CONFIGSET_TKT },
86 { "ticket-newpage", CONFIGSET_TKT },
87 { "ticket-viewpage", CONFIGSET_TKT },
88 { "ticket-editpage", CONFIGSET_TKT },
89 { "ticket-report-template", CONFIGSET_TKT },
90 { "ticket-key-template", CONFIGSET_TKT },
91 { "@reportfmt", CONFIGSET_TKT },
92 { "@user", CONFIGSET_USER },
93 { "@shun", CONFIGSET_SHUN },
94 };
95 static int iConfig = 0;
96
97 /*
98 ** Return name of first configuration property matching the given mask.
@@ -99,22 +111,184 @@
111 }
112 return 0;
113 }
114
115 /*
116 ** Return the mask for the named configuration parameter if it can be
117 ** safely exported. Return 0 if the parameter is not safe to export.
118 */
119 int configure_is_exportable(const char *zName){
120 int i;
121 for(i=0; i<count(aConfig); i++){
122 if( strcmp(zName, aConfig[i].zName)==0 ){
123 int m = aConfig[i].groupMask;
124 if( !g.okAdmin ){
125 m &= ~CONFIGSET_USER;
126 }
127 return m;
128 }
129 }
130 return 0;
131 }
132
133 /*
134 ** zName is one of the special configuration names that refers to an entire
135 ** table rather than a single entry in CONFIG. Special names are "@reportfmt"
136 ** and "@shun" and "@user". This routine writes SQL text into pOut that when
137 ** evaluated will populate the corresponding table with data.
138 */
139 void configure_render_special_name(const char *zName, Blob *pOut){
140 Stmt q;
141 if( strcmp(zName, "@shun")==0 ){
142 db_prepare(&q, "SELECT uuid FROM shun");
143 while( db_step(&q)==SQLITE_ROW ){
144 blob_appendf(pOut, "INSERT OR IGNORE INTO shun VALUES('%s');\n",
145 db_column_text(&q, 0)
146 );
147 }
148 db_finalize(&q);
149 }else if( strcmp(zName, "@reportfmt")==0 ){
150 db_prepare(&q, "SELECT title, cols, sqlcode FROM reportfmt");
151 while( db_step(&q)==SQLITE_ROW ){
152 blob_appendf(pOut, "INSERT INTO _xfer_reportfmt(title,cols,sqlcode) "
153 " VALUES(%Q,%Q,%Q);\n",
154 db_column_text(&q, 0),
155 db_column_text(&q, 1),
156 db_column_text(&q, 2)
157 );
158 }
159 db_finalize(&q);
160 }else if( strcmp(zName, "@user")==0 ){
161 db_prepare(&q, "SELECT login, cap, info, quote(photo) FROM user");
162 while( db_step(&q)==SQLITE_ROW ){
163 blob_appendf(pOut, "INSERT INTO _xfer_user(login,cap,info,photo) "
164 " VALUES(%Q,%Q,%Q,%s);\n",
165 db_column_text(&q, 0),
166 db_column_text(&q, 1),
167 db_column_text(&q, 2),
168 db_column_text(&q, 3)
169 );
170 }
171 db_finalize(&q);
172 }
173 }
174
175 /*
176 ** Two SQL functions:
177 **
178 ** flag_test(int)
179 ** flag_clear(int)
180 **
181 ** The flag_test() function takes the integer valued argument and
182 ** ANDs it against the static variable "flag_value" below. The
183 ** function returns TRUE or false depending on the result. The
184 ** flag_clear() function masks off the bits from "flag_value" that
185 ** are given in the argument.
186 **
187 ** These functions are used below in the WHEN clause of a trigger to
188 ** get the trigger to fire exactly once.
189 */
190 static int flag_value = 0xffff;
191 static void flag_test_function(
192 sqlite3_context *context,
193 int argc,
194 sqlite3_value **argv
195 ){
196 int m = sqlite3_value_int(argv[0]);
197 sqlite3_result_int(context, (flag_value&m)!=0 );
198 }
199 static void flag_clear_function(
200 sqlite3_context *context,
201 int argc,
202 sqlite3_value **argv
203 ){
204 int m = sqlite3_value_int(argv[0]);
205 flag_value &= ~m;
206 }
207
208 /*
209 ** Create the temporary _xfer_reportfmt and _xfer_user tables that are
210 ** necessary in order to evalute the SQL text generated by the
211 ** configure_render_special_name() routine.
212 **
213 ** If replaceFlag is true, then the setup is such that the content in
214 ** the SQL text will completely replace the current repository configuration.
215 ** If replaceFlag is false, then the SQL text will be merged with the
216 ** existing configuration. When merging, existing values take priority
217 ** over SQL text values.
218 */
219 void configure_prepare_to_receive(int replaceFlag){
220 static const char zSQL1[] =
221 @ CREATE TEMP TABLE _xfer_reportfmt(
222 @ rn integer primary key, -- Report number
223 @ owner text, -- Owner of this report format (not used)
224 @ title text UNIQUE ON CONFLICT IGNORE, -- Title of this report
225 @ cols text, -- A color-key specification
226 @ sqlcode text -- An SQL SELECT statement for this report
227 @ );
228 @ CREATE TEMP TABLE _xfer_user(
229 @ uid INTEGER PRIMARY KEY, -- User ID
230 @ login TEXT UNIQUE ON CONFLICT IGNORE, -- login name of the user
231 @ pw TEXT, -- password
232 @ cap TEXT, -- Capabilities of this user
233 @ cookie TEXT, -- WWW login cookie
234 @ ipaddr TEXT, -- IP address for which cookie is valid
235 @ cexpire DATETIME, -- Time when cookie expires
236 @ info TEXT, -- contact information
237 @ photo BLOB -- JPEG image of this user
238 @ );
239 @ INSERT INTO _xfer_reportfmt SELECT * FROM reportfmt;
240 @ INSERT INTO _xfer_user SELECT * FROM user;
241 ;
242 db_multi_exec(zSQL1);
243
244 /* When the replace flag is set, add triggers that run the first time
245 ** that new data is seen. The triggers run only once and delete all the
246 ** existing data.
247 */
248 if( replaceFlag ){
249 static const char zSQL2[] =
250 @ CREATE TRIGGER _xfer_r1 BEFORE INSERT ON _xfer_reportfmt
251 @ WHEN flag_test(1) BEGIN
252 @ DELETE FROM _xfer_reportfmt;
253 @ SELECT flag_clear(1);
254 @ END;
255 @ CREATE TRIGGER _xfer_r2 BEFORE INSERT ON _xfer_user
256 @ WHEN flag_test(2) BEGIN
257 @ DELETE FROM _xfer_user;
258 @ SELECT flag_clear(2);
259 @ END;
260 @ CREATE TEMP TRIGGER _xfer_r3 BEFORE INSERT ON shun
261 @ WHEN flag_test(4) BEGIN
262 @ DELETE FROM shun;
263 @ SELECT flag_clear(4);
264 @ END;
265 ;
266 sqlite3_create_function(g.db, "flag_test", 1, SQLITE_UTF8, 0,
267 flag_test_function, 0, 0);
268 sqlite3_create_function(g.db, "flag_clear", 1, SQLITE_UTF8, 0,
269 flag_clear_function, 0, 0);
270 flag_value = 0xffff;
271 db_multi_exec(zSQL2);
272 }
273 }
274
275 /*
276 ** After receiving configuration data, call this routine to transfer
277 ** the results into the main database.
278 */
279 void configure_finalize_receive(void){
280 static const char zSQL[] =
281 @ DELETE FROM user;
282 @ INSERT INTO user SELECT * FROM _xfer_user;
283 @ DELETE FROM reportfmt;
284 @ INSERT INTO reportfmt SELECT * FROM _xfer_reportfmt;
285 @ DROP TABLE _xfer_user;
286 @ DROP TABLE _xfer_reportfmt;
287 ;
288 db_multi_exec(zSQL);
289 }
290
291 /*
292 ** Identify a configuration group by name. Return its mask.
293 ** Throw an error if no match.
294 */
@@ -124,21 +298,64 @@
298 for(i=0; i<count(aGroupName); i++){
299 if( strncmp(z, aGroupName[i].zName, n)==0 ){
300 return aGroupName[i].groupMask;
301 }
302 }
303 printf("Available configuration areas:\n");
304 for(i=0; i<count(aGroupName); i++){
305 printf(" %-10s %s\n", aGroupName[i].zName, aGroupName[i].zHelp);
306 }
307 fossil_fatal("no such configuration area: \"%s\"", z);
308 return 0;
309 }
310
311 /*
312 ** Write SQL text into file zFilename that will restore the configuration
313 ** area identified by mask to its current state from any other state.
314 */
315 static void export_config(
316 int mask, /* Mask indicating which configuration to export */
317 const char *zMask, /* Name of the configuration */
318 const char *zFilename /* Write into this file */
319 ){
320 int i;
321 Blob out;
322 blob_zero(&out);
323 blob_appendf(&out,
324 "-- The \"%s\" configuration exported from\n"
325 "-- repository \"%s\"\n"
326 "-- on %s\n",
327 zMask, g.zRepositoryName,
328 db_text(0, "SELECT datetime('now')")
329 );
330 for(i=0; i<count(aConfig); i++){
331 if( (aConfig[i].groupMask & mask)!=0 ){
332 const char *zName = aConfig[i].zName;
333 if( zName[0]!='@' ){
334 char *zValue = db_text(0,
335 "SELECT value FROM config WHERE name=%Q", zName);
336 if( zValue ){
337 blob_appendf(&out,"REPLACE INTO config VALUES(%Q,%Q);\n",
338 zName, zValue);
339 }
340 free(zValue);
341 }else{
342 configure_render_special_name(zName, &out);
343 }
344 }
345 }
346 blob_write_to_file(&out, zFilename);
347 blob_reset(&out);
348 }
349
350
351 /*
352 ** COMMAND: configuration
353 **
354 ** Usage: %fossil configure METHOD ...
355 **
356 ** Where METHOD is one of: export import merge pull reset. All methods
357 ** accept the -R or --repository option to specific a repository.
358 **
359 ** %fossil configuration export AREA FILENAME
360 **
361 ** Write to FILENAME exported configuraton information for AREA.
@@ -145,104 +362,108 @@
362 ** AREA can be one of: all ticket skin project
363 **
364 ** %fossil configuration import FILENAME
365 **
366 ** Read a configuration from FILENAME, overwriting the current
367 ** configuration.
368 **
369 ** %fossil configuration merge FILENAME
370 **
371 ** Read a configuration from FILENAME and merge its values into
372 ** the current configuration. Existing values take priority over
373 ** values read from FILENAME.
374 **
375 ** %fossil configuration pull AREA ?URL?
376 **
377 ** Pull and install the configuration from a different server
378 ** identified by URL. If no URL is specified, then the default
379 ** server is used.
380 **
381 ** %fossil configuration reset AREA
382 **
383 ** Restore the configuration to the default. AREA as above.
384 **
385 ** WARNING: Do not import, merge, or pull configurations from an untrusted
386 ** source. The inbound configuration is not checked for safety and can
387 ** introduce security vulnerabilities.
388 */
389 void configuration_cmd(void){
390 int n;
391 const char *zMethod;
392 if( g.argc<3 ){
393 usage("export|import|merge|pull|reset ...");
394 }
395 db_find_and_open_repository(1);
396 zMethod = g.argv[2];
397 n = strlen(zMethod);
398 if( strncmp(zMethod, "export", n)==0 ){
 
399 int mask;
 
 
 
 
400 if( g.argc!=5 ){
401 usage("export AREA FILENAME");
402 }
403 mask = find_area(g.argv[3]);
404 export_config(mask, g.argv[3], g.argv[4]);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
405 }else
406 if( strncmp(zMethod, "import", n)==0
407 || strncmp(zMethod, "merge", n)==0 ){
408 Blob in;
409 if( g.argc!=4 ) usage(mprintf("%s FILENAME",zMethod));
410 blob_read_from_file(&in, g.argv[3]);
411 db_begin_transaction();
412 configure_prepare_to_receive(zMethod[0]=='i');
413 db_multi_exec("%s", blob_str(&in));
414 configure_finalize_receive();
415 db_end_transaction(0);
416 }else
417 if( strncmp(zMethod, "pull", n)==0 ){
418 int mask;
419 const char *zServer;
420 url_proxy_options();
421 if( g.argc!=4 && g.argc!=5 ){
422 usage("pull AREA ?URL?");
423 }
424 mask = find_area(g.argv[3]);
425 if( g.argc==5 ){
426 zServer = g.argv[4];
427 }else{
428 zServer = db_get("last-sync-url", 0);
429 if( zServer==0 ){
430 fossil_fatal("no server specified");
431 }
432 }
433 url_parse(zServer);
434 if( g.urlIsFile ){
435 fossil_fatal("network sync only");
436 }
437 user_select();
438 client_sync(0,0,0,mask);
439 }else
440 if( strncmp(zMethod, "reset", n)==0 ){
441 int mask, i;
442 char *zBackup;
443 if( g.argc!=4 ) usage("reset AREA");
444 mask = find_area(g.argv[3]);
445 zBackup = db_text(0,
446 "SELECT strftime('config-backup-%%Y%%m%%d%%H%%M%%f','now')");
447 db_begin_transaction();
448 export_config(mask, g.argv[3], zBackup);
449 for(i=0; i<count(aConfig); i++){
450 const char *zName = aConfig[i].zName;
451 if( (aConfig[i].groupMask & mask)==0 ) continue;
452 if( zName[0]!='@' ){
453 db_multi_exec("DELETE FROM config WHERE name=%Q", zName);
454 }else if( strcmp(zName,"@user")==0 ){
455 db_multi_exec("DELETE FROM user");
456 db_create_default_users();
457 }else if( strcmp(zName,"@reportfmt")==0 ){
458 db_multi_exec("DELETE FROM reportfmt");
459 }
460 }
461 db_end_transaction(0);
462 printf("Configuration reset to factory defaults.\n");
463 printf("To recover, use: %s %s import %s\n",
464 g.argv[0], g.argv[1], zBackup);
465 }else
466 {
467 fossil_fatal("METHOD should be one of: export import merge pull reset");
468 }
469 }
470
+33 -26
--- src/db.c
+++ src/db.c
@@ -745,37 +745,14 @@
745745
);
746746
isNewRepo = 1;
747747
}
748748
749749
/*
750
-** Fill an empty repository database with the basic information for a
751
-** repository. This function is shared between 'create_repository_cmd'
752
-** ('new') and 'reconstruct_cmd' ('reconstruct'), both of which create
753
-** new repositories.
754
-**
755
-** The makeInitialVersion flag determines whether or not an initial
756
-** manifest is created. The makeServerCodes flag determines whether or
757
-** not server and project codes are invented for this repository.
750
+** Create the default user accounts in the USER table.
758751
*/
759
-void db_initial_setup (int makeInitialVersion, int makeServerCodes){
760
- char *zDate;
752
+void db_create_default_users(void){
761753
const char *zUser;
762
- Blob hash;
763
- Blob manifest;
764
-
765
- db_set("content-schema", CONTENT_SCHEMA, 0);
766
- db_set("aux-schema", AUX_SCHEMA, 0);
767
- if( makeServerCodes ){
768
- db_multi_exec(
769
- "INSERT INTO config(name,value)"
770
- " VALUES('server-code', lower(hex(randomblob(20))));"
771
- "INSERT INTO config(name,value)"
772
- " VALUES('project-code', lower(hex(randomblob(20))));"
773
- );
774
- }
775
- if( !db_is_global("autosync") ) db_set_int("autosync", 1, 0);
776
- if( !db_is_global("localauth") ) db_set_int("localauth", 0, 0);
777754
zUser = db_get("default-user", 0);
778755
if( zUser==0 ){
779756
#ifdef __MINGW32__
780757
zUser = getenv("USERNAME");
781758
#else
@@ -789,16 +766,46 @@
789766
"INSERT INTO user(login, pw, cap, info)"
790767
"VALUES(%Q,'','s','')", zUser
791768
);
792769
db_multi_exec(
793770
"INSERT INTO user(login,pw,cap,info)"
794
- " VALUES('anonymous','anonymous','aghknw','Anon');"
771
+ " VALUES('anonymous','anonymous','ghknw','Anon');"
795772
"INSERT INTO user(login,pw,cap,info)"
796773
" VALUES('nobody','','jor','Nobody');"
797774
"INSERT INTO user(login,pw,cap,info)"
798775
" VALUES('developer','','deipt','Dev');"
799776
);
777
+}
778
+
779
+/*
780
+** Fill an empty repository database with the basic information for a
781
+** repository. This function is shared between 'create_repository_cmd'
782
+** ('new') and 'reconstruct_cmd' ('reconstruct'), both of which create
783
+** new repositories.
784
+**
785
+** The makeInitialVersion flag determines whether or not an initial
786
+** manifest is created. The makeServerCodes flag determines whether or
787
+** not server and project codes are invented for this repository.
788
+*/
789
+void db_initial_setup (int makeInitialVersion, int makeServerCodes){
790
+ char *zDate;
791
+ Blob hash;
792
+ Blob manifest;
793
+
794
+ db_set("content-schema", CONTENT_SCHEMA, 0);
795
+ db_set("aux-schema", AUX_SCHEMA, 0);
796
+ if( makeServerCodes ){
797
+ db_multi_exec(
798
+ "INSERT INTO config(name,value)"
799
+ " VALUES('server-code', lower(hex(randomblob(20))));"
800
+ "INSERT INTO config(name,value)"
801
+ " VALUES('project-code', lower(hex(randomblob(20))));"
802
+ );
803
+ }
804
+ if( !db_is_global("autosync") ) db_set_int("autosync", 1, 0);
805
+ if( !db_is_global("localauth") ) db_set_int("localauth", 0, 0);
806
+ db_create_default_users();
800807
user_select();
801808
802809
if (makeInitialVersion){
803810
blob_zero(&manifest);
804811
blob_appendf(&manifest, "C initial\\sempty\\sbaseline\n");
805812
--- src/db.c
+++ src/db.c
@@ -745,37 +745,14 @@
745 );
746 isNewRepo = 1;
747 }
748
749 /*
750 ** Fill an empty repository database with the basic information for a
751 ** repository. This function is shared between 'create_repository_cmd'
752 ** ('new') and 'reconstruct_cmd' ('reconstruct'), both of which create
753 ** new repositories.
754 **
755 ** The makeInitialVersion flag determines whether or not an initial
756 ** manifest is created. The makeServerCodes flag determines whether or
757 ** not server and project codes are invented for this repository.
758 */
759 void db_initial_setup (int makeInitialVersion, int makeServerCodes){
760 char *zDate;
761 const char *zUser;
762 Blob hash;
763 Blob manifest;
764
765 db_set("content-schema", CONTENT_SCHEMA, 0);
766 db_set("aux-schema", AUX_SCHEMA, 0);
767 if( makeServerCodes ){
768 db_multi_exec(
769 "INSERT INTO config(name,value)"
770 " VALUES('server-code', lower(hex(randomblob(20))));"
771 "INSERT INTO config(name,value)"
772 " VALUES('project-code', lower(hex(randomblob(20))));"
773 );
774 }
775 if( !db_is_global("autosync") ) db_set_int("autosync", 1, 0);
776 if( !db_is_global("localauth") ) db_set_int("localauth", 0, 0);
777 zUser = db_get("default-user", 0);
778 if( zUser==0 ){
779 #ifdef __MINGW32__
780 zUser = getenv("USERNAME");
781 #else
@@ -789,16 +766,46 @@
789 "INSERT INTO user(login, pw, cap, info)"
790 "VALUES(%Q,'','s','')", zUser
791 );
792 db_multi_exec(
793 "INSERT INTO user(login,pw,cap,info)"
794 " VALUES('anonymous','anonymous','aghknw','Anon');"
795 "INSERT INTO user(login,pw,cap,info)"
796 " VALUES('nobody','','jor','Nobody');"
797 "INSERT INTO user(login,pw,cap,info)"
798 " VALUES('developer','','deipt','Dev');"
799 );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
800 user_select();
801
802 if (makeInitialVersion){
803 blob_zero(&manifest);
804 blob_appendf(&manifest, "C initial\\sempty\\sbaseline\n");
805
--- src/db.c
+++ src/db.c
@@ -745,37 +745,14 @@
745 );
746 isNewRepo = 1;
747 }
748
749 /*
750 ** Create the default user accounts in the USER table.
 
 
 
 
 
 
 
751 */
752 void db_create_default_users(void){
 
753 const char *zUser;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
754 zUser = db_get("default-user", 0);
755 if( zUser==0 ){
756 #ifdef __MINGW32__
757 zUser = getenv("USERNAME");
758 #else
@@ -789,16 +766,46 @@
766 "INSERT INTO user(login, pw, cap, info)"
767 "VALUES(%Q,'','s','')", zUser
768 );
769 db_multi_exec(
770 "INSERT INTO user(login,pw,cap,info)"
771 " VALUES('anonymous','anonymous','ghknw','Anon');"
772 "INSERT INTO user(login,pw,cap,info)"
773 " VALUES('nobody','','jor','Nobody');"
774 "INSERT INTO user(login,pw,cap,info)"
775 " VALUES('developer','','deipt','Dev');"
776 );
777 }
778
779 /*
780 ** Fill an empty repository database with the basic information for a
781 ** repository. This function is shared between 'create_repository_cmd'
782 ** ('new') and 'reconstruct_cmd' ('reconstruct'), both of which create
783 ** new repositories.
784 **
785 ** The makeInitialVersion flag determines whether or not an initial
786 ** manifest is created. The makeServerCodes flag determines whether or
787 ** not server and project codes are invented for this repository.
788 */
789 void db_initial_setup (int makeInitialVersion, int makeServerCodes){
790 char *zDate;
791 Blob hash;
792 Blob manifest;
793
794 db_set("content-schema", CONTENT_SCHEMA, 0);
795 db_set("aux-schema", AUX_SCHEMA, 0);
796 if( makeServerCodes ){
797 db_multi_exec(
798 "INSERT INTO config(name,value)"
799 " VALUES('server-code', lower(hex(randomblob(20))));"
800 "INSERT INTO config(name,value)"
801 " VALUES('project-code', lower(hex(randomblob(20))));"
802 );
803 }
804 if( !db_is_global("autosync") ) db_set_int("autosync", 1, 0);
805 if( !db_is_global("localauth") ) db_set_int("localauth", 0, 0);
806 db_create_default_users();
807 user_select();
808
809 if (makeInitialVersion){
810 blob_zero(&manifest);
811 blob_appendf(&manifest, "C initial\\sempty\\sbaseline\n");
812
+29 -9
--- src/xfer.c
+++ src/xfer.c
@@ -678,15 +678,24 @@
678678
&& xfer.nToken==2
679679
){
680680
if( g.okRead ){
681681
char *zName = blob_str(&xfer.aToken[1]);
682682
if( configure_is_exportable(zName) ){
683
- char *zValue = db_get(zName, 0);
684
- if( zValue ){
685
- blob_appendf(xfer.pOut, "config %s %d\n%s\n", zName,
686
- strlen(zValue), zValue);
687
- free(zValue);
683
+ if( zName[0]!='@' ){
684
+ char *zValue = db_get(zName, 0);
685
+ if( zValue ){
686
+ blob_appendf(xfer.pOut, "config %s %d\n%s\n", zName,
687
+ strlen(zValue), zValue);
688
+ free(zValue);
689
+ }
690
+ }else{
691
+ Blob content;
692
+ blob_zero(&content);
693
+ configure_render_special_name(zName, &content);
694
+ blob_appendf(xfer.pOut, "config %s %d\n%s\n", zName,
695
+ blob_size(&content), blob_str(&content));
696
+ blob_reset(&content);
688697
}
689698
}
690699
}
691700
}else
692701
@@ -870,10 +879,13 @@
870879
while( zName ){
871880
blob_appendf(&send, "reqconfig %s\n", zName);
872881
zName = configure_next_name(configMask);
873882
nCard++;
874883
}
884
+ if( configMask & (CONFIGSET_USER|CONFIGSET_TKT) ){
885
+ configure_prepare_to_receive(0);
886
+ }
875887
configMask = 0;
876888
}
877889
878890
/* Append randomness to the end of the message */
879891
#if 0 /* Enable this after all servers have upgraded */
@@ -994,14 +1006,18 @@
9941006
const char *zName = blob_str(&xfer.aToken[1]);
9951007
Blob content;
9961008
blob_zero(&content);
9971009
blob_extract(xfer.pIn, size, &content);
9981010
if( configure_is_exportable(zName) & origConfigMask ){
999
- db_multi_exec(
1000
- "REPLACE INTO config(name,value) VALUES(%Q,%Q)",
1001
- zName, blob_str(&content)
1002
- );
1011
+ if( zName[0]!='@' ){
1012
+ db_multi_exec(
1013
+ "REPLACE INTO config(name,value) VALUES(%Q,%Q)",
1014
+ zName, blob_str(&content)
1015
+ );
1016
+ }else{
1017
+ db_multi_exec("%s", blob_str(&content));
1018
+ }
10031019
}
10041020
nCard++;
10051021
blob_reset(&content);
10061022
blob_seek(xfer.pIn, 1, BLOB_SEEK_CUR);
10071023
}else
@@ -1049,10 +1065,14 @@
10491065
fossil_fatal("%b", &xfer.err);
10501066
}
10511067
blobarray_reset(xfer.aToken, xfer.nToken);
10521068
blob_reset(&xfer.line);
10531069
}
1070
+ if( origConfigMask & (CONFIGSET_TKT|CONFIGSET_USER) ){
1071
+ configure_finalize_receive();
1072
+ }
1073
+ origConfigMask = 0;
10541074
printf(zValueFormat, "Received:",
10551075
blob_size(&recv), nCard,
10561076
xfer.nFileRcvd, xfer.nDeltaRcvd + xfer.nDanglingFile);
10571077
blob_reset(&recv);
10581078
nCycle++;
10591079
--- src/xfer.c
+++ src/xfer.c
@@ -678,15 +678,24 @@
678 && xfer.nToken==2
679 ){
680 if( g.okRead ){
681 char *zName = blob_str(&xfer.aToken[1]);
682 if( configure_is_exportable(zName) ){
683 char *zValue = db_get(zName, 0);
684 if( zValue ){
685 blob_appendf(xfer.pOut, "config %s %d\n%s\n", zName,
686 strlen(zValue), zValue);
687 free(zValue);
 
 
 
 
 
 
 
 
 
688 }
689 }
690 }
691 }else
692
@@ -870,10 +879,13 @@
870 while( zName ){
871 blob_appendf(&send, "reqconfig %s\n", zName);
872 zName = configure_next_name(configMask);
873 nCard++;
874 }
 
 
 
875 configMask = 0;
876 }
877
878 /* Append randomness to the end of the message */
879 #if 0 /* Enable this after all servers have upgraded */
@@ -994,14 +1006,18 @@
994 const char *zName = blob_str(&xfer.aToken[1]);
995 Blob content;
996 blob_zero(&content);
997 blob_extract(xfer.pIn, size, &content);
998 if( configure_is_exportable(zName) & origConfigMask ){
999 db_multi_exec(
1000 "REPLACE INTO config(name,value) VALUES(%Q,%Q)",
1001 zName, blob_str(&content)
1002 );
 
 
 
 
1003 }
1004 nCard++;
1005 blob_reset(&content);
1006 blob_seek(xfer.pIn, 1, BLOB_SEEK_CUR);
1007 }else
@@ -1049,10 +1065,14 @@
1049 fossil_fatal("%b", &xfer.err);
1050 }
1051 blobarray_reset(xfer.aToken, xfer.nToken);
1052 blob_reset(&xfer.line);
1053 }
 
 
 
 
1054 printf(zValueFormat, "Received:",
1055 blob_size(&recv), nCard,
1056 xfer.nFileRcvd, xfer.nDeltaRcvd + xfer.nDanglingFile);
1057 blob_reset(&recv);
1058 nCycle++;
1059
--- src/xfer.c
+++ src/xfer.c
@@ -678,15 +678,24 @@
678 && xfer.nToken==2
679 ){
680 if( g.okRead ){
681 char *zName = blob_str(&xfer.aToken[1]);
682 if( configure_is_exportable(zName) ){
683 if( zName[0]!='@' ){
684 char *zValue = db_get(zName, 0);
685 if( zValue ){
686 blob_appendf(xfer.pOut, "config %s %d\n%s\n", zName,
687 strlen(zValue), zValue);
688 free(zValue);
689 }
690 }else{
691 Blob content;
692 blob_zero(&content);
693 configure_render_special_name(zName, &content);
694 blob_appendf(xfer.pOut, "config %s %d\n%s\n", zName,
695 blob_size(&content), blob_str(&content));
696 blob_reset(&content);
697 }
698 }
699 }
700 }else
701
@@ -870,10 +879,13 @@
879 while( zName ){
880 blob_appendf(&send, "reqconfig %s\n", zName);
881 zName = configure_next_name(configMask);
882 nCard++;
883 }
884 if( configMask & (CONFIGSET_USER|CONFIGSET_TKT) ){
885 configure_prepare_to_receive(0);
886 }
887 configMask = 0;
888 }
889
890 /* Append randomness to the end of the message */
891 #if 0 /* Enable this after all servers have upgraded */
@@ -994,14 +1006,18 @@
1006 const char *zName = blob_str(&xfer.aToken[1]);
1007 Blob content;
1008 blob_zero(&content);
1009 blob_extract(xfer.pIn, size, &content);
1010 if( configure_is_exportable(zName) & origConfigMask ){
1011 if( zName[0]!='@' ){
1012 db_multi_exec(
1013 "REPLACE INTO config(name,value) VALUES(%Q,%Q)",
1014 zName, blob_str(&content)
1015 );
1016 }else{
1017 db_multi_exec("%s", blob_str(&content));
1018 }
1019 }
1020 nCard++;
1021 blob_reset(&content);
1022 blob_seek(xfer.pIn, 1, BLOB_SEEK_CUR);
1023 }else
@@ -1049,10 +1065,14 @@
1065 fossil_fatal("%b", &xfer.err);
1066 }
1067 blobarray_reset(xfer.aToken, xfer.nToken);
1068 blob_reset(&xfer.line);
1069 }
1070 if( origConfigMask & (CONFIGSET_TKT|CONFIGSET_USER) ){
1071 configure_finalize_receive();
1072 }
1073 origConfigMask = 0;
1074 printf(zValueFormat, "Received:",
1075 blob_size(&recv), nCard,
1076 xfer.nFileRcvd, xfer.nDeltaRcvd + xfer.nDanglingFile);
1077 blob_reset(&recv);
1078 nCycle++;
1079

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button