Index: src/checkin.c
==================================================================
--- src/checkin.c
+++ src/checkin.c
@@ -276,15 +276,15 @@
int dotfilesFlag;
const char *zIgnoreFlag;
Blob path, repo;
Stmt q;
int n;
+ Glob *pIgnore;
+
allFlag = find_option("force","f",0)!=0;
dotfilesFlag = find_option("dotfiles",0,0)!=0;
zIgnoreFlag = find_option("ignore",0,1);
- Glob *pIgnore;
-
db_must_be_within_tree();
if( zIgnoreFlag==0 ){
zIgnoreFlag = db_get("ignore-glob", 0);
}
db_multi_exec("CREATE TEMP TABLE sfile(x TEXT PRIMARY KEY)");
Index: src/clone.c
==================================================================
--- src/clone.c
+++ src/clone.c
@@ -64,14 +64,14 @@
file_copy(g.urlName, g.argv[3]);
db_close(1);
db_open_repository(g.argv[3]);
db_record_repository_filename(g.argv[3]);
db_multi_exec(
- "REPLACE INTO config(name,value)"
- " VALUES('server-code', lower(hex(randomblob(20))));"
- "REPLACE INTO config(name,value)"
- " VALUES('last-sync-url', '%q');",
+ "REPLACE INTO config(name,value,mtime)"
+ " VALUES('server-code', lower(hex(randomblob(20))),now());"
+ "REPLACE INTO config(name,value,mtime)"
+ " VALUES('last-sync-url', '%q',now());",
g.urlCanonical
);
db_multi_exec(
"DELETE FROM blob WHERE rid IN private;"
"DELETE FROM delta wHERE rid IN private;"
@@ -92,12 +92,12 @@
user_select();
db_set("content-schema", CONTENT_SCHEMA, 0);
db_set("aux-schema", AUX_SCHEMA, 0);
db_set("last-sync-url", g.argv[2], 0);
db_multi_exec(
- "REPLACE INTO config(name,value)"
- " VALUES('server-code', lower(hex(randomblob(20))));"
+ "REPLACE INTO config(name,value,mtime)"
+ " VALUES('server-code', lower(hex(randomblob(20))), now());"
);
url_enable_proxy(0);
url_get_password_if_needed();
g.xlinkClusterOnly = 1;
nErr = client_sync(0,0,1,bPrivate,CONFIGSET_ALL,0);
Index: src/configure.c
==================================================================
--- src/configure.c
+++ src/configure.c
@@ -2,11 +2,11 @@
** Copyright (c) 2008 D. Richard Hipp
**
** This program is free software; you can redistribute it and/or
** modify it under the terms of the Simplified BSD License (also
** known as the "2-Clause License" or "FreeBSD License".)
-
+**
** This program is distributed in the hope that it will be useful,
** but without any warranty; without even the implied warranty of
** merchantability or fitness for a particular purpose.
**
** Author contact information:
@@ -27,18 +27,21 @@
#if INTERFACE
/*
** Configuration transfers occur in groups. These are the allowed
** groupings:
*/
-#define CONFIGSET_SKIN 0x000001 /* WWW interface appearance */
-#define CONFIGSET_TKT 0x000002 /* Ticket configuration */
-#define CONFIGSET_PROJ 0x000004 /* Project name */
-#define CONFIGSET_SHUN 0x000008 /* Shun settings */
-#define CONFIGSET_USER 0x000010 /* The USER table */
-#define CONFIGSET_ADDR 0x000020 /* The CONCEALED table */
-
-#define CONFIGSET_ALL 0xffffff /* Everything */
+#define CONFIGSET_SKIN 0x000001 /* WWW interface appearance */
+#define CONFIGSET_TKT 0x000002 /* Ticket configuration */
+#define CONFIGSET_PROJ 0x000004 /* Project name */
+#define CONFIGSET_SHUN 0x000008 /* Shun settings */
+#define CONFIGSET_USER 0x000010 /* The USER table */
+#define CONFIGSET_ADDR 0x000020 /* The CONCEALED table */
+
+#define CONFIGSET_ALL 0x0000ff /* Everything */
+
+#define CONFIGSET_OVERWRITE 0x100000 /* Causes overwrite instead of merge */
+#define CONFIGSET_OLDFORMAT 0x200000 /* Use the legacy format */
#endif /* INTERFACE */
/*
** Names of the configuration sets
@@ -46,17 +49,17 @@
static struct {
const char *zName; /* Name of the configuration set */
int groupMask; /* Mask for that configuration set */
const char *zHelp; /* What it does */
} aGroupName[] = {
- { "email", CONFIGSET_ADDR, "Concealed email addresses in tickets" },
- { "project", CONFIGSET_PROJ, "Project name and description" },
- { "skin", CONFIGSET_SKIN, "Web interface apparance settings" },
- { "shun", CONFIGSET_SHUN, "List of shunned artifacts" },
- { "ticket", CONFIGSET_TKT, "Ticket setup", },
- { "user", CONFIGSET_USER, "Users and privilege settings" },
- { "all", CONFIGSET_ALL, "All of the above" },
+ { "/email", CONFIGSET_ADDR, "Concealed email addresses in tickets" },
+ { "/project", CONFIGSET_PROJ, "Project name and description" },
+ { "/skin", CONFIGSET_SKIN, "Web interface apparance settings" },
+ { "/shun", CONFIGSET_SHUN, "List of shunned artifacts" },
+ { "/ticket", CONFIGSET_TKT, "Ticket setup", },
+ { "/user", CONFIGSET_USER, "Users and privilege settings" },
+ { "/all", CONFIGSET_ALL, "All of the above" },
};
/*
** The following is a list of settings that we are willing to
@@ -106,15 +109,29 @@
const char *configure_first_name(int iMask){
iConfig = 0;
return configure_next_name(iMask);
}
const char *configure_next_name(int iMask){
- while( iConfig2 && zName[0]=='\'' && zName[n-1]=='\'' ){
+ zName++;
+ n -= 2;
+ }
for(i=0; i=count(aType) ) return;
+ while( blob_token(pContent, &name) && blob_sqltoken(pContent, &value) ){
+ char *z = blob_terminate(&name);
+ if( !safeSql(z) ) return;
+ if( nToken>0 ){
+ for(jj=0; jj=aType[ii].nField ) continue;
+ }else{
+ if( !safeInt(z) ) return;
+ }
+ azToken[nToken++] = z;
+ azToken[nToken++] = z = blob_terminate(&value);
+ if( !safeSql(z) ) return;
+ if( nToken>=count(azToken) ) break;
+ }
+ if( nToken<2 ) return;
+ if( aType[ii].zName[0]=='/' ){
+ thisMask = configure_is_exportable(azToken[1]);
+ }else{
+ thisMask = configure_is_exportable(aType[ii].zName);
+ }
+ if( (thisMask & groupMask)==0 ) return;
+
+ blob_zero(&sql);
+ if( groupMask & CONFIGSET_OVERWRITE ){
+ if( (thisMask & configHasBeenReset)==0 && aType[ii].zName[0]!='/' ){
+ db_multi_exec("DELETE FROM %s", &aType[ii].zName[1]);
+ configHasBeenReset |= thisMask;
+ }
+ blob_append(&sql, "REPLACE INTO ", -1);
+ }else{
+ blob_append(&sql, "INSERT OR IGNORE INTO ", -1);
+ }
+ blob_appendf(&sql, "%s(%s, mtime", &zName[1], aType[ii].zPrimKey);
+ for(jj=2; jj=%lld", iStart);
+ while( db_step(&q)==SQLITE_ROW ){
+ blob_appendf(&rec,"%s %s scom %s",
+ db_column_text(&q, 0),
+ db_column_text(&q, 1),
+ db_column_text(&q, 2)
+ );
+ blob_appendf(pOut, "config /shun %d\n%s\n",
+ blob_size(&rec), blob_str(&rec));
+ nCard++;
+ blob_reset(&rec);
+ }
+ db_finalize(&q);
+ }
+ if( groupMask & CONFIGSET_USER ){
+ db_prepare(&q, "SELECT mtime, quote(login), quote(pw), quote(cap),"
+ " quote(info), quote(photo) FROM user"
+ " WHERE mtime>=%lld", iStart);
+ while( db_step(&q)==SQLITE_ROW ){
+ blob_appendf(&rec,"%s %s pw %s cap %s info %s photo %s",
+ db_column_text(&q, 0),
+ db_column_text(&q, 1),
+ db_column_text(&q, 2),
+ db_column_text(&q, 3),
+ db_column_text(&q, 4),
+ db_column_text(&q, 5)
+ );
+ blob_appendf(pOut, "config /user %d\n%s\n",
+ blob_size(&rec), blob_str(&rec));
+ nCard++;
+ blob_reset(&rec);
+ }
+ db_finalize(&q);
+ }
+ if( groupMask & CONFIGSET_TKT ){
+ db_prepare(&q, "SELECT mtime, quote(title), quote(owner), quote(cols),"
+ " quote(sqlcode) FROM reportfmt"
+ " WHERE mtime>=%lld", iStart);
+ while( db_step(&q)==SQLITE_ROW ){
+ blob_appendf(&rec,"%s %s owner %s cols %s sqlcode %s",
+ db_column_text(&q, 0),
+ db_column_text(&q, 1),
+ db_column_text(&q, 2),
+ db_column_text(&q, 3),
+ db_column_text(&q, 4)
+ );
+ blob_appendf(pOut, "config /reportfmt %d\n%s\n",
+ blob_size(&rec), blob_str(&rec));
+ nCard++;
+ blob_reset(&rec);
+ }
+ db_finalize(&q);
+ }
+ if( groupMask & CONFIGSET_ADDR ){
+ db_prepare(&q, "SELECT mtime, quote(hash), quote(content) FROM concealed"
+ " WHERE mtime>=%lld", iStart);
+ while( db_step(&q)==SQLITE_ROW ){
+ blob_appendf(&rec,"%s %s content %s",
+ db_column_text(&q, 0),
+ db_column_text(&q, 1),
+ db_column_text(&q, 2)
+ );
+ blob_appendf(pOut, "config /concealed %d\n%s\n",
+ blob_size(&rec), blob_str(&rec));
+ nCard++;
+ blob_reset(&rec);
+ }
+ db_finalize(&q);
+ }
+ db_prepare(&q, "SELECT mtime, quote(name), quote(value) FROM config"
+ " WHERE name=:name AND mtime>=%lld", iStart);
+ for(ii=0; ii
#include
#include
#include
+#include
#include "db.h"
#if INTERFACE
/*
** An single SQL statement is represented as an instance of the following
@@ -606,10 +607,23 @@
}
va_end(ap);
sqlite3_exec(db, "COMMIT", 0, 0, 0);
sqlite3_close(db);
}
+
+/*
+** Function to return the number of seconds since 1970. This is
+** the same as strftime('%s','now') but is more compact.
+*/
+static void db_now_function(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ sqlite3_result_int64(context, time(0));
+}
+
/*
** Open a database file. Return a pointer to the new database
** connection. An error results in process abort.
*/
@@ -630,10 +644,11 @@
if( rc!=SQLITE_OK ){
db_err(sqlite3_errmsg(db));
}
sqlite3_busy_timeout(db, 5000);
sqlite3_wal_autocheckpoint(db, 1); /* Set to checkpoint frequently */
+ sqlite3_create_function(db, "now", 0, SQLITE_ANY, 0, db_now_function, 0, 0);
return db;
}
/*
@@ -1105,14 +1120,14 @@
db_set("content-schema", CONTENT_SCHEMA, 0);
db_set("aux-schema", AUX_SCHEMA, 0);
if( makeServerCodes ){
db_multi_exec(
- "INSERT INTO config(name,value)"
- " VALUES('server-code', lower(hex(randomblob(20))));"
- "INSERT INTO config(name,value)"
- " VALUES('project-code', lower(hex(randomblob(20))));"
+ "INSERT INTO config(name,value,mtime)"
+ " VALUES('server-code', lower(hex(randomblob(20))),now());"
+ "INSERT INTO config(name,value,mtime)"
+ " VALUES('project-code', lower(hex(randomblob(20))),now());"
);
}
if( !db_is_global("autosync") ) db_set_int("autosync", 1, 0);
if( !db_is_global("localauth") ) db_set_int("localauth", 0, 0);
db_create_default_users(0, zDefaultUser);
@@ -1295,11 +1310,12 @@
sha1sum_step_text(zContent, n);
sha1sum_finish(&out);
sqlite3_snprintf(sizeof(zHash), zHash, "%s", blob_str(&out));
blob_reset(&out);
db_multi_exec(
- "INSERT OR IGNORE INTO concealed VALUES(%Q,%#Q)",
+ "INSERT OR IGNORE INTO concealed(hash,content,mtime)"
+ " VALUES(%Q,%#Q,now())",
zHash, n, zContent
);
}
return zHash;
}
@@ -1406,11 +1422,11 @@
db_swap_connections();
db_multi_exec("REPLACE INTO global_config(name,value) VALUES(%Q,%Q)",
zName, zValue);
db_swap_connections();
}else{
- db_multi_exec("REPLACE INTO config(name,value) VALUES(%Q,%Q)",
+ db_multi_exec("REPLACE INTO config(name,value,mtime) VALUES(%Q,%Q,now())",
zName, zValue);
}
if( globalFlag && g.repositoryOpen ){
db_multi_exec("DELETE FROM config WHERE name=%Q", zName);
}
@@ -1465,11 +1481,11 @@
db_swap_connections();
db_multi_exec("REPLACE INTO global_config(name,value) VALUES(%Q,%d)",
zName, value);
db_swap_connections();
}else{
- db_multi_exec("REPLACE INTO config(name,value) VALUES(%Q,%d)",
+ db_multi_exec("REPLACE INTO config(name,value,mtime) VALUES(%Q,%d,now())",
zName, value);
}
if( globalFlag && g.repositoryOpen ){
db_multi_exec("DELETE FROM config WHERE name=%Q", zName);
}
Index: src/login.c
==================================================================
--- src/login.c
+++ src/login.c
@@ -1257,12 +1257,12 @@
db_multi_exec("DETACH other");
/* Propagate the changes to all other members of the login-group */
zSql = mprintf(
"BEGIN;"
- "REPLACE INTO config(name, value) VALUES('peer-name-%q', %Q);"
- "REPLACE INTO config(name, value) VALUES('peer-repo-%q', %Q);"
+ "REPLACE INTO config(name,value,mtime) VALUES('peer-name-%q',%Q,now());"
+ "REPLACE INTO config(name,value,mtime) VALUES('peer-repo-%q',%Q,now());"
"COMMIT;",
zSelfProjCode, zSelfLabel, zSelfProjCode, zSelfRepo
);
login_group_sql(zSql, "
", "
", pzErrMsg);
fossil_free(zSql);
Index: src/rebuild.c
==================================================================
--- src/rebuild.c
+++ src/rebuild.c
@@ -22,13 +22,15 @@
#include
#include
#include
/*
-** Schema changes
+** Make changes to the stable part of the schema (the part that is not
+** simply deleted and reconstructed on a rebuild) to bring the schema
+** up to the latest.
*/
-static const char zSchemaUpdates[] =
+static const char zSchemaUpdates1[] =
@ -- Index on the delta table
@ --
@ CREATE INDEX IF NOT EXISTS delta_i1 ON delta(srcid);
@
@ -- Artifacts that should not be processed are identified in the
@@ -39,41 +41,124 @@
@ --
@ -- Shunned artifacts do not exist in the blob table. Hence they
@ -- have not artifact ID (rid) and we thus must store their full
@ -- UUID.
@ --
-@ CREATE TABLE IF NOT EXISTS shun(uuid UNIQUE);
+@ CREATE TABLE IF NOT EXISTS shun(
+@ uuid UNIQUE, -- UUID of artifact to be shunned. Canonical form
+@ mtime INTEGER, -- When added. Seconds since 1970
+@ scom TEXT -- Optional text explaining why the shun occurred
+@ );
@
@ -- Artifacts that should not be pushed are stored in the "private"
@ -- table.
@ --
@ CREATE TABLE IF NOT EXISTS private(rid INTEGER PRIMARY KEY);
@
-@ -- An entry in this table describes a database query that generates a
-@ -- table of tickets.
-@ --
-@ CREATE TABLE IF NOT EXISTS reportfmt(
-@ rn integer primary key, -- Report number
-@ owner text, -- Owner of this report format (not used)
-@ title text, -- Title of this report
-@ cols text, -- A color-key specification
-@ sqlcode text -- An SQL SELECT statement for this report
-@ );
-@
@ -- Some ticket content (such as the originators email address or contact
@ -- information) needs to be obscured to protect privacy. This is achieved
@ -- by storing an SHA1 hash of the content. For display, the hash is
@ -- mapped back into the original text using this table.
@ --
@ -- This table contains sensitive information and should not be shared
@ -- with unauthorized users.
@ --
@ CREATE TABLE IF NOT EXISTS concealed(
-@ hash TEXT PRIMARY KEY,
-@ content TEXT
+@ hash TEXT PRIMARY KEY, -- The SHA1 hash of content
+@ mtime INTEGER, -- Time created. Seconds since 1970
+@ content TEXT -- Content intended to be concealed
+@ );
+;
+static const char zSchemaUpdates2[] =
+@ -- An entry in this table describes a database query that generates a
+@ -- table of tickets.
+@ --
+@ CREATE TABLE IF NOT EXISTS reportfmt(
+@ rn INTEGER PRIMARY KEY, -- Report number
+@ owner TEXT, -- Owner of this report format (not used)
+@ title TEXT UNIQUE, -- Title of this report
+@ mtime INTEGER, -- Time last modified. Seconds since 1970
+@ cols TEXT, -- A color-key specification
+@ sqlcode TEXT -- An SQL SELECT statement for this report
@ );
;
+
+static void rebuild_update_schema(void){
+ int rc;
+ db_multi_exec(zSchemaUpdates1);
+ db_multi_exec(zSchemaUpdates2);
+
+ rc = db_exists("SELECT 1 FROM sqlite_master"
+ " WHERE name='user' AND sql GLOB '* mtime *'");
+ if( rc==0 ){
+ db_multi_exec(
+ "CREATE TEMP TABLE temp_user AS SELECT * FROM user;"
+ "DROP TABLE user;"
+ "CREATE TABLE user(\n"
+ " uid INTEGER PRIMARY KEY,\n"
+ " login TEXT UNIQUE,\n"
+ " pw TEXT,\n"
+ " cap TEXT,\n"
+ " cookie TEXT,\n"
+ " ipaddr TEXT,\n"
+ " cexpire DATETIME,\n"
+ " info TEXT,\n"
+ " mtime DATE,\n"
+ " photo BLOB\n"
+ ");"
+ "INSERT OR IGNORE INTO user"
+ " SELECT uid, login, pw, cap, cookie,"
+ " ipaddr, cexpire, info, now(), photo FROM temp_user;"
+ "DROP TABLE temp_user;"
+ );
+ }
+
+ rc = db_exists("SELECT 1 FROM sqlite_master"
+ " WHERE name='config' AND sql GLOB '* mtime *'");
+ if( rc==0 ){
+ db_multi_exec(
+ "ALTER TABLE config ADD COLUMN mtime INTEGER;"
+ "UPDATE config SET mtime=now();"
+ );
+ }
+
+ rc = db_exists("SELECT 1 FROM sqlite_master"
+ " WHERE name='shun' AND sql GLOB '* mtime *'");
+ if( rc==0 ){
+ db_multi_exec(
+ "ALTER TABLE shun ADD COLUMN mtime INTEGER;"
+ "ALTER TABLE shun ADD COLUMN scom TEXT;"
+ "UPDATE shun SET mtime=now();"
+ );
+ }
+
+ rc = db_exists("SELECT 1 FROM sqlite_master"
+ " WHERE name='reportfmt' AND sql GLOB '* mtime *'");
+ if( rc==0 ){
+ db_multi_exec(
+ "CREATE TEMP TABLE old_fmt AS SELECT * FROM reportfmt;"
+ "DROP TABLE reportfmt;"
+ );
+ db_multi_exec(zSchemaUpdates2);
+ db_multi_exec(
+ "INSERT OR IGNORE INTO reportfmt(rn,owner,title,cols,sqlcode,mtime)"
+ " SELECT rn, owner, title, cols, sqlcode, now() FROM old_fmt;"
+ "INSERT OR IGNORE INTO reportfmt(rn,owner,title,cols,sqlcode,mtime)"
+ " SELECT rn, owner, title || ' (' || rn || ')', cols, sqlcode, now()"
+ " FROM old_fmt;"
+ );
+ }
+
+ rc = db_exists("SELECT 1 FROM sqlite_master"
+ " WHERE name='concealed' AND sql GLOB '* mtime *'");
+ if( rc==0 ){
+ db_multi_exec(
+ "ALTER TABLE concealed ADD COLUMN mtime INTEGER;"
+ "UPDATE concealed SET mtime=now();"
+ );
+ }
+}
/*
** Variables used to store state information about an on-going "rebuild"
** or "deconstruct".
*/
@@ -256,11 +341,11 @@
ttyOutput = doOut;
processCnt = 0;
if (!g.fQuiet) {
percent_complete(0);
}
- db_multi_exec(zSchemaUpdates);
+ rebuild_update_schema();
for(;;){
zTable = db_text(0,
"SELECT name FROM sqlite_master /*scan*/"
" WHERE type='table'"
" AND name NOT IN ('blob','delta','rcvfrom','user',"
@@ -459,12 +544,12 @@
}
db_begin_transaction();
ttyOutput = 1;
errCnt = rebuild_db(randomizeFlag, 1, doClustering);
db_multi_exec(
- "REPLACE INTO config(name,value) VALUES('content-schema','%s');"
- "REPLACE INTO config(name,value) VALUES('aux-schema','%s');",
+ "REPLACE INTO config(name,value,mtime) VALUES('content-schema','%s',now());"
+ "REPLACE INTO config(name,value,mtime) VALUES('aux-schema','%s',now());",
CONTENT_SCHEMA, AUX_SCHEMA
);
if( errCnt && !forceFlag ){
printf("%d errors. Rolling back changes. Use --force to force a commit.\n",
errCnt);
Index: src/report.c
==================================================================
--- src/report.c
+++ src/report.c
@@ -360,19 +360,25 @@
}else if( (zTitle = trim_string(zTitle))[0]==0 ){
zErr = "Please supply a title";
}else{
zErr = verify_sql_statement(zSQL);
}
+ if( zErr==0
+ && db_exists("SELECT 1 FROM reportfmt WHERE title=%Q and rn<>%d",
+ zTitle, rn)
+ ){
+ zErr = mprintf("There is already another report named \"%h\"", zTitle);
+ }
if( zErr==0 ){
login_verify_csrf_secret();
if( rn>0 ){
db_multi_exec("UPDATE reportfmt SET title=%Q, sqlcode=%Q,"
- " owner=%Q, cols=%Q WHERE rn=%d",
+ " owner=%Q, cols=%Q, mtime=now() WHERE rn=%d",
zTitle, zSQL, zOwner, zClrKey, rn);
}else{
- db_multi_exec("INSERT INTO reportfmt(title,sqlcode,owner,cols) "
- "VALUES(%Q,%Q,%Q,%Q)",
+ db_multi_exec("INSERT INTO reportfmt(title,sqlcode,owner,cols,mtime) "
+ "VALUES(%Q,%Q,%Q,%Q,now())",
zTitle, zSQL, zOwner, zClrKey);
rn = db_last_insert_rowid();
}
cgi_redirect(mprintf("rptview?rn=%d", rn));
return;
Index: src/schema.c
==================================================================
--- src/schema.c
+++ src/schema.c
@@ -39,12 +39,12 @@
** changes. The aux tables have an arbitrary version number (typically
** a date) which can change frequently. When the content schema changes,
** we have to execute special procedures to update the schema. When
** the aux schema changes, all we need to do is rebuild the database.
*/
-#define CONTENT_SCHEMA "1"
-#define AUX_SCHEMA "2011-02-25 14:52"
+#define CONTENT_SCHEMA "2"
+#define AUX_SCHEMA "2011-04-25 19:50"
#endif /* INTERFACE */
/*
@@ -51,21 +51,25 @@
** The schema for a repository database.
**
** Schema1[] contains parts of the schema that are fixed and unchanging
** across versions. Schema2[] contains parts of the schema that can
** change from one version to the next. The information in Schema2[]
-** can be reconstructed from the information in Schema1[].
+** is reconstructed from the information in Schema1[] by the "rebuild"
+** operation.
*/
const char zRepositorySchema1[] =
@ -- The BLOB and DELTA tables contain all records held in the repository.
@ --
-@ -- The BLOB.CONTENT column is always compressed using libz. This
+@ -- The BLOB.CONTENT column is always compressed using zlib. This
@ -- column might hold the full text of the record or it might hold
@ -- a delta that is able to reconstruct the record from some other
@ -- record. If BLOB.CONTENT holds a delta, then a DELTA table entry
@ -- will exist for the record and that entry will point to another
@ -- entry that holds the source of the delta. Deltas can be chained.
+@ --
+@ -- The blob and delta tables collectively hold the "global state" of
+@ -- a Fossil repository.
@ --
@ CREATE TABLE blob(
@ rid INTEGER PRIMARY KEY, -- Record ID
@ rcvid INTEGER, -- Origin of this record
@ size INTEGER, -- Size of content. -1 for a phantom.
@@ -77,17 +81,24 @@
@ rid INTEGER PRIMARY KEY, -- Record ID
@ srcid INTEGER NOT NULL REFERENCES blob -- Record holding source document
@ );
@ CREATE INDEX delta_i1 ON delta(srcid);
@
+@ -------------------------------------------------------------------------
+@ -- The BLOB and DELTA tables above hold the "global state" of a Fossil
+@ -- project; the stuff that is normally exchanged during "sync". The
+@ -- "local state" of a repository is contained in the remaining tables of
+@ -- the zRepositorySchema1 string.
+@ -------------------------------------------------------------------------
+@
@ -- Whenever new blobs are received into the repository, an entry
@ -- in this table records the source of the blob.
@ --
@ CREATE TABLE rcvfrom(
@ rcvid INTEGER PRIMARY KEY, -- Received-From ID
@ uid INTEGER REFERENCES user, -- User login
-@ mtime DATETIME, -- Time or receipt
+@ mtime DATETIME, -- Time of receipt. Julian day.
@ nonce TEXT UNIQUE, -- Nonce used for login
@ ipaddr TEXT -- Remote IP address. NULL for direct.
@ );
@
@ -- Information about users
@@ -99,26 +110,28 @@
@ -- hash based on the project-code, the user login, and the cleartext
@ -- password.
@ --
@ CREATE TABLE user(
@ uid INTEGER PRIMARY KEY, -- User ID
-@ login TEXT, -- login name of the user
+@ login TEXT UNIQUE, -- login name of the user
@ pw TEXT, -- password
@ cap TEXT, -- Capabilities of this user
@ cookie TEXT, -- WWW login cookie
@ ipaddr TEXT, -- IP address for which cookie is valid
@ cexpire DATETIME, -- Time when cookie expires
@ info TEXT, -- contact information
+@ mtime DATE, -- last change. seconds since 1970
@ photo BLOB -- JPEG image of this user
@ );
@
@ -- The VAR table holds miscellanous information about the repository.
@ -- in the form of name-value pairs.
@ --
@ CREATE TABLE config(
@ name TEXT PRIMARY KEY NOT NULL, -- Primary name of the entry
@ value CLOB, -- Content of the named parameter
+@ mtime DATE, -- last modified. seconds since 1970
@ CHECK( typeof(name)='text' AND length(name)>=1 )
@ );
@
@ -- Artifacts that should not be processed are identified in the
@ -- "shun" table. Artifacts that are control-file forgeries or
@@ -128,11 +141,15 @@
@ --
@ -- Shunned artifacts do not exist in the blob table. Hence they
@ -- have not artifact ID (rid) and we thus must store their full
@ -- UUID.
@ --
-@ CREATE TABLE shun(uuid UNIQUE);
+@ CREATE TABLE shun(
+@ uuid UNIQUE, -- UUID of artifact to be shunned. Canonical form
+@ mtime DATE, -- When added. seconds since 1970
+@ scom TEXT -- Optional text explaining why the shun occurred
+@ );
@
@ -- Artifacts that should not be pushed are stored in the "private"
@ -- table. Private artifacts are omitted from the "unclustered" and
@ -- "unsent" tables.
@ --
@@ -140,17 +157,19 @@
@
@ -- An entry in this table describes a database query that generates a
@ -- table of tickets.
@ --
@ CREATE TABLE reportfmt(
-@ rn integer primary key, -- Report number
-@ owner text, -- Owner of this report format (not used)
-@ title text, -- Title of this report
-@ cols text, -- A color-key specification
-@ sqlcode text -- An SQL SELECT statement for this report
+@ rn INTEGER PRIMARY KEY, -- Report number
+@ owner TEXT, -- Owner of this report format (not used)
+@ title TEXT UNIQUE, -- Title of this report
+@ mtime DATE, -- Last modified. seconds since 1970
+@ cols TEXT, -- A color-key specification
+@ sqlcode TEXT -- An SQL SELECT statement for this report
@ );
-@ INSERT INTO reportfmt(title,cols,sqlcode) VALUES('All Tickets','#ffffff Key:
+@ INSERT INTO reportfmt(title,mtime,cols,sqlcode)
+@ VALUES('All Tickets',julianday('1970-01-01'),'#ffffff Key:
@ #f2dcdc Active
@ #e8e8e8 Review
@ #cfe8bd Fixed
@ #bde5d6 Tested
@ #cacae5 Deferred
@@ -176,12 +195,13 @@
@ --
@ -- This table contains sensitive information and should not be shared
@ -- with unauthorized users.
@ --
@ CREATE TABLE concealed(
-@ hash TEXT PRIMARY KEY,
-@ content TEXT
+@ hash TEXT PRIMARY KEY, -- The SHA1 hash of content
+@ mtime DATE, -- Time created. Seconds since 1970
+@ content TEXT -- Content intended to be concealed
@ );
;
const char zRepositorySchema2[] =
@ -- Filenames
@@ -214,11 +234,11 @@
@ --
@ CREATE TABLE plink(
@ pid INTEGER REFERENCES blob, -- Parent manifest
@ cid INTEGER REFERENCES blob, -- Child manifest
@ isprim BOOLEAN, -- pid is the primary parent of cid
-@ mtime DATETIME, -- the date/time stamp on cid
+@ mtime DATETIME, -- the date/time stamp on cid. Julian day.
@ UNIQUE(pid, cid)
@ );
@ CREATE INDEX plink_i2 ON plink(cid,pid);
@
@ -- A "leaf" checkin is a checkin that has no children in the same
@@ -232,11 +252,11 @@
@
@ -- Events used to generate a timeline
@ --
@ CREATE TABLE event(
@ type TEXT, -- Type of event: 'ci', 'w', 'e', 't'
-@ mtime DATETIME, -- Date and time when the event occurs
+@ mtime DATETIME, -- Time of occurrence. Julian day.
@ objid INTEGER PRIMARY KEY, -- Associated record ID
@ tagid INTEGER, -- Associated ticket or wiki name tag
@ uid INTEGER REFERENCES user, -- User who caused the event
@ bgcolor TEXT, -- Color set by 'bgcolor' property
@ euser TEXT, -- User set by 'user' property
@@ -319,11 +339,11 @@
@ tagid INTEGER REFERENCES tag, -- The tag that added or removed
@ tagtype INTEGER, -- 0:-,cancel 1:+,single 2:*,propagate
@ srcid INTEGER REFERENCES blob, -- Artifact of tag. 0 for propagated tags
@ origid INTEGER REFERENCES blob, -- check-in holding propagated tag
@ value TEXT, -- Value of the tag. Might be NULL.
-@ mtime TIMESTAMP, -- Time of addition or removal
+@ mtime TIMESTAMP, -- Time of addition or removal. Julian day
@ rid INTEGER REFERENCE blob, -- Artifact tag is applied to
@ UNIQUE(rid, tagid)
@ );
@ CREATE INDEX tagxref_i1 ON tagxref(tagid, mtime);
@
@@ -334,11 +354,11 @@
@ --
@ CREATE TABLE backlink(
@ target TEXT, -- Where the hyperlink points to
@ srctype INT, -- 0: check-in 1: ticket 2: wiki
@ srcid INT, -- rid for checkin or wiki. tkt_id for ticket.
-@ mtime TIMESTAMP, -- time that the hyperlink was added
+@ mtime TIMESTAMP, -- time that the hyperlink was added. Julian day.
@ UNIQUE(target, srctype, srcid)
@ );
@ CREATE INDEX backlink_src ON backlink(srcid, srctype);
@
@ -- Each attachment is an entry in the following table. Only
@@ -345,11 +365,11 @@
@ -- the most recent attachment (identified by the D card) is saved.
@ --
@ CREATE TABLE attachment(
@ attachid INTEGER PRIMARY KEY, -- Local id for this attachment
@ isLatest BOOLEAN DEFAULT 0, -- True if this is the one to use
-@ mtime TIMESTAMP, -- Time when attachment last changed
+@ mtime TIMESTAMP, -- Last changed. Julian day.
@ src TEXT, -- UUID of the attachment. NULL to delete
@ target TEXT, -- Object attached to. Wikiname or Tkt UUID
@ filename TEXT, -- Filename for the attachment
@ comment TEXT, -- Comment associated with this attachment
@ user TEXT -- Name of user adding attachment
@@ -443,11 +463,11 @@
@ chnged INT DEFAULT 0, -- 0:unchnged 1:edited 2:m-chng 3:m-add
@ deleted BOOLEAN DEFAULT 0, -- True if deleted
@ isexe BOOLEAN, -- True if file should be executable
@ rid INTEGER, -- Originally from this repository record
@ mrid INTEGER, -- Based on this record due to a merge
-@ mtime INTEGER, -- Modification time of file on disk
+@ mtime INTEGER, -- Mtime of file on disk. sec since 1970
@ pathname TEXT, -- Full pathname relative to root
@ origname TEXT, -- Original pathname. NULL if unchanged
@ UNIQUE(pathname,vid)
@ );
@
Index: src/setup.c
==================================================================
--- src/setup.c
+++ src/setup.c
@@ -354,12 +354,12 @@
style_footer();
return;
}
login_verify_csrf_secret();
db_multi_exec(
- "REPLACE INTO user(uid,login,info,pw,cap) "
- "VALUES(nullif(%d,0),%Q,%Q,%Q,'%s')",
+ "REPLACE INTO user(uid,login,info,pw,cap,mtime) "
+ "VALUES(nullif(%d,0),%Q,%Q,%Q,'%s',now())",
uid, P("login"), P("info"), zPw, zCap
);
if( atoi(PD("all","0"))>0 ){
Blob sql;
char *zErr = 0;
@@ -375,11 +375,12 @@
blob_appendf(&sql,
"UPDATE user SET login=%Q,"
" pw=coalesce(shared_secret(%Q,%Q,"
"(SELECT value FROM config WHERE name='project-code')),pw),"
" info=%Q,"
- " cap=%Q"
+ " cap=%Q,"
+ " mtime=now()"
" WHERE login=%Q;",
zLogin, P("pw"), zLogin, P("info"), zCap,
zOldLogin
);
login_group_sql(blob_str(&sql), "
", "
\n", &zErr);
@@ -1286,18 +1287,18 @@
if( P("set")!=0 && zMime && zMime[0] && szImg>0 ){
Blob img;
Stmt ins;
blob_init(&img, aImg, szImg);
db_prepare(&ins,
- "REPLACE INTO config(name, value)"
- " VALUES('logo-image',:bytes)"
+ "REPLACE INTO config(name,value,mtime)"
+ " VALUES('logo-image',:bytes,now())"
);
db_bind_blob(&ins, ":bytes", &img);
db_step(&ins);
db_finalize(&ins);
db_multi_exec(
- "REPLACE INTO config(name, value) VALUES('logo-mimetype',%Q)",
+ "REPLACE INTO config(name,value,mtime) VALUES('logo-mimetype',%Q,now())",
zMime
);
db_end_transaction(0);
cgi_redirect("setup_logo");
}else if( P("clr")!=0 ){
Index: src/shun.c
==================================================================
--- src/shun.c
+++ src/shun.c
@@ -81,17 +81,31 @@
@ fossil rebuild command-line before the artifact content
@ can pulled in from other respositories.
}
}
if( zUuid && P("add") ){
+ int rid, tagid;
login_verify_csrf_secret();
- db_multi_exec("INSERT OR IGNORE INTO shun VALUES('%s')", zUuid);
+ db_multi_exec(
+ "INSERT OR IGNORE INTO shun(uuid,mtime)"
+ " VALUES('%s', now())", zUuid);
@
Artifact
@ %s(zUuid) has been
@ shunned. It will no longer be pushed.
@ It will be removed from the repository the next time the respository
@ is rebuilt using the fossil rebuild command-line
+ db_multi_exec("DELETE FROM attachment WHERE src=%Q", zUuid);
+ rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", zUuid);
+ if( rid ){
+ db_multi_exec("DELETE FROM event WHERE objid=%d", rid);
+ }
+ tagid = db_int(0, "SELECT tagid FROM tag WHERE tagname='tkt-%q'", zUuid);
+ if( tagid ){
+ db_multi_exec("DELETE FROM ticket WHERE tkt_uuid=%Q", zUuid);
+ db_multi_exec("DELETE FROM tag WHERE tagid=%d", tagid);
+ db_multi_exec("DELETE FROM tagxref WHERE tagid=%d", tagid);
+ }
}
@
A shunned artifact will not be pushed nor accepted in a pull and the
@ artifact content will be purged from the repository the next time the
@ repository is rebuilt. A list of shunned artifacts can be seen at the
@ bottom of this page.
Index: src/skins.c
==================================================================
--- src/skins.c
+++ src/skins.c
@@ -25,11 +25,12 @@
/*
** A black-and-white theme with the project title in a bar across the top
** and no logo image.
*/
static const char zBuiltinSkin1[] =
-@ REPLACE INTO config VALUES('css','/* General settings for the entire page */
+@ REPLACE INTO config(name,mtime,value)
+@ VALUES('css',now(),'/* General settings for the entire page */
@ body {
@ margin: 0ex 1ex;
@ padding: 0px;
@ background-color: white;
@ font-family: sans-serif;
@@ -152,11 +153,11 @@
@ table.label-value th {
@ vertical-align: top;
@ text-align: right;
@ padding: 0.2ex 2ex;
@ }');
-@ REPLACE INTO config VALUES('header','
+@ REPLACE INTO config(name,mtime,value) VALUES('header',now(),'
@
@ $: $
@
@ Login "
@ }
@
@ ');
-@ REPLACE INTO config VALUES('footer','
@ ');
-@ REPLACE INTO config VALUES('footer','
@
@ ');
-@ REPLACE INTO config VALUES('footer','
+@ REPLACE INTO config(name,mtime,value) VALUES('footer',now(),'
@
@
@ ');
@@ -653,11 +658,12 @@
/*
** Gradients and rounded corners.
*/
static const char zBuiltinSkin4[] =
-@ REPLACE INTO config VALUES('css','/* General settings for the entire page */
+@ REPLACE INTO config(name,mtime,value)
+@ VALUES('css',now(),'/* General settings for the entire page */
@ html {
@ min-height: 100%;
@ }
@ body {
@ margin: 0ex 1ex;
@@ -880,11 +886,11 @@
@ }
@
@ textarea {
@ font-size: 1em;
@ }');
-@ REPLACE INTO config VALUES('header','
+@ REPLACE INTO config(name,mtime,value) VALUES('header',now(),'
@
@ $: $
@
@ Login"
@ }
@
@
@ ');
-@ REPLACE INTO config VALUES('footer','
+@ REPLACE INTO config(name,mtime,value) VALUES('footer',now(),'
@
@
@ ');
@@ -984,17 +990,20 @@
** Memory to hold the returned string is obtained from malloc.
*/
static char *getSkin(int useDefault){
Blob val;
blob_zero(&val);
- blob_appendf(&val, "REPLACE INTO config VALUES('css',%Q);\n",
+ blob_appendf(&val,
+ "REPLACE INTO config(name,value,mtime) VALUES('css',%Q,now());\n",
useDefault ? zDefaultCSS : db_get("css", (char*)zDefaultCSS)
);
- blob_appendf(&val, "REPLACE INTO config VALUES('header',%Q);\n",
+ blob_appendf(&val,
+ "REPLACE INTO config(name,value,mtime) VALUES('header',%Q,now());\n",
useDefault ? zDefaultHeader : db_get("header", (char*)zDefaultHeader)
);
- blob_appendf(&val, "REPLACE INTO config VALUES('footer',%Q);\n",
+ blob_appendf(&val,
+ "REPLACE INTO config(name,value,mtime) VALUES('footer',%Q,now());\n",
useDefault ? zDefaultFooter : db_get("footer", (char*)zDefaultFooter)
);
return blob_str(&val);
}
@@ -1048,11 +1057,11 @@
if( db_exists("SELECT 1 FROM config WHERE name=%Q", zName)
|| strcmp(zName, "Default")==0 ){
zErr = mprintf("Skin name \"%h\" already exists. "
"Choose a different name.", P("sn"));
}else{
- db_multi_exec("INSERT INTO config VALUES(%Q,%Q)",
+ db_multi_exec("INSERT INTO config(name,value,mtime) VALUES(%Q,%Q,now())",
zName, zCurrent
);
}
}
@@ -1069,13 +1078,13 @@
seen = db_exists("SELECT 1 FROM config WHERE name GLOB 'skin:*'"
" AND value=%Q", zCurrent);
}
if( !seen ){
db_multi_exec(
- "INSERT INTO config VALUES("
+ "INSERT INTO config(name,value,mtime) VALUES("
" strftime('skin:Backup On %%Y-%%m-%%d %%H:%%M:%%S'),"
- " %Q)", zCurrent
+ " %Q,now())", zCurrent
);
}
seen = 0;
for(i=0; i=2 && strncmp(g.argv[2],"default",n)==0 ){
user_select();
@@ -249,11 +249,12 @@
}
if( blob_size(&pw)==0 ){
printf("password unchanged\n");
}else{
char *zSecret = sha1_shared_secret(blob_str(&pw), g.argv[3], 0);
- db_multi_exec("UPDATE user SET pw=%Q WHERE uid=%d", zSecret, uid);
+ db_multi_exec("UPDATE user SET pw=%Q, mtime=now() WHERE uid=%d",
+ zSecret, uid);
free(zSecret);
}
}else if( n>=2 && strncmp(g.argv[2],"capabilities",2)==0 ){
int uid;
if( g.argc!=4 && g.argc!=5 ){
@@ -263,12 +264,12 @@
if( uid==0 ){
fossil_fatal("no such user: %s", g.argv[3]);
}
if( g.argc==5 ){
db_multi_exec(
- "UPDATE user SET cap=%Q WHERE uid=%d", g.argv[4],
- uid
+ "UPDATE user SET cap=%Q, mtime=now() WHERE uid=%d",
+ g.argv[4], uid
);
}
printf("%s\n", db_text(0, "SELECT cap FROM user WHERE uid=%d", uid));
}else{
fossil_panic("user subcommand should be one of: "
@@ -340,12 +341,12 @@
db_finalize(&s);
}
if( g.userUid==0 ){
db_multi_exec(
- "INSERT INTO user(login, pw, cap, info)"
- "VALUES('anonymous', '', 'cfghjkmnoqw', '')"
+ "INSERT INTO user(login, pw, cap, info, mtime)"
+ "VALUES('anonymous', '', 'cfghjkmnoqw', '', now())"
);
g.userUid = db_last_insert_rowid();
g.zLogin = "anonymous";
}
}
@@ -364,11 +365,11 @@
if( g.argc!=3 ) usage("REPOSITORY");
db_open_repository(g.argv[2]);
sqlite3_create_function(g.db, "shared_secret", 2, SQLITE_UTF8, 0,
sha1_shared_secret_sql_function, 0, 0);
db_multi_exec(
- "UPDATE user SET pw=shared_secret(pw,login)"
+ "UPDATE user SET pw=shared_secret(pw,login), mtime=now()"
" WHERE length(pw)>0 AND length(pw)!=40"
);
}
/*
Index: src/xfer.c
==================================================================
--- src/xfer.c
+++ src/xfer.c
@@ -454,11 +454,11 @@
" (SELECT uuid FROM delta, blob"
" WHERE delta.rid=:rid AND delta.srcid=blob.rid)"
" FROM blob"
" WHERE rid=:rid"
" AND size>=0"
- " AND uuid NOT IN shun"
+ " AND NOT EXISTS(SELECT 1 FROM shun WHERE shun.uuid=blob.uuid)"
);
db_bind_int(&q1, ":rid", rid);
rc = db_step(&q1);
if( rc==SQLITE_ROW ){
zUuid = db_column_text(&q1, 0);
@@ -738,13 +738,16 @@
}
db_finalize(&q);
}
/*
-** Send a single config card for configuration item zName
+** Send a single old-style config card for configuration item zName.
+**
+** This routine and the functionality it implements is scheduled for
+** removal on 2012-05-01.
*/
-static void send_config_card(Xfer *pXfer, const char *zName){
+static void send_legacy_config_card(Xfer *pXfer, const char *zName){
if( zName[0]!='@' ){
Blob val;
blob_zero(&val);
db_blob(&val, "SELECT value FROM config WHERE name=%Q", zName);
if( blob_size(&val)>0 ){
@@ -760,11 +763,10 @@
blob_appendf(pXfer->pOut, "config %s %d\n%s\n", zName,
blob_size(&content), blob_str(&content));
blob_reset(&content);
}
}
-
/*
** Called when there is an attempt to transfer private content to and
** from a server without authorization.
*/
@@ -1007,11 +1009,11 @@
*/
if( blob_eq(&xfer.aToken[0], "login")
&& xfer.nToken==4
){
if( disableLogin ){
- g.okRead = g.okWrite = g.okPrivate = 1;
+ g.okRead = g.okWrite = g.okPrivate = g.okAdmin = 1;
}else{
if( check_tail_hash(&xfer.aToken[2], xfer.pIn)
|| check_login(&xfer.aToken[1], &xfer.aToken[2], &xfer.aToken[3])
){
cgi_reset_content();
@@ -1029,12 +1031,19 @@
if( blob_eq(&xfer.aToken[0], "reqconfig")
&& xfer.nToken==2
){
if( g.okRead ){
char *zName = blob_str(&xfer.aToken[1]);
- if( configure_is_exportable(zName) ){
- send_config_card(&xfer, zName);
+ if( zName[0]=='/' ){
+ /* New style configuration transfer */
+ int groupMask = configure_name_to_mask(&zName[1], 0);
+ if( !g.okAdmin ) groupMask &= ~CONFIGSET_USER;
+ if( !g.okRdAddr ) groupMask &= ~CONFIGSET_ADDR;
+ configure_send_group(xfer.pOut, groupMask, 0);
+ }else if( configure_is_exportable(zName) ){
+ /* Old style configuration transfer */
+ send_legacy_config_card(&xfer, zName);
}
}
}else
/* config NAME SIZE \n CONTENT
@@ -1052,11 +1061,11 @@
cgi_reset_content();
@ error not\sauthorized\sto\spush\sconfiguration
nErr++;
break;
}
- if( !recvConfig ){
+ if( !recvConfig && zName[0]=='@' ){
configure_prepare_to_receive(0);
recvConfig = 1;
}
configure_receive(zName, &content, CONFIGSET_ALL);
blob_reset(&content);
@@ -1185,14 +1194,14 @@
** gdb fossil
** r test-xfer out.txt
*/
void cmd_test_xfer(void){
int notUsed;
+ db_find_and_open_repository(0,0);
if( g.argc!=2 && g.argc!=3 ){
usage("?MESSAGEFILE?");
}
- db_must_be_within_tree();
blob_zero(&g.cgiIn);
blob_read_from_file(&g.cgiIn, g.argc==2 ? "-" : g.argv[2]);
disableLogin = 1;
page_xfer();
printf("%s\n", cgi_extract_content(¬Used));
@@ -1331,25 +1340,32 @@
while( zName ){
blob_appendf(&send, "reqconfig %s\n", zName);
zName = configure_next_name(configRcvMask);
nCardSent++;
}
- if( configRcvMask & (CONFIGSET_USER|CONFIGSET_TKT) ){
- configure_prepare_to_receive(0);
+ if( (configRcvMask & (CONFIGSET_USER|CONFIGSET_TKT))!=0
+ && (configRcvMask & CONFIGSET_OLDFORMAT)!=0
+ ){
+ int overwrite = (configRcvMask & CONFIGSET_OVERWRITE)!=0;
+ configure_prepare_to_receive(overwrite);
}
origConfigRcvMask = configRcvMask;
configRcvMask = 0;
}
/* Send configuration parameters being pushed */
if( configSendMask ){
- const char *zName;
- zName = configure_first_name(configSendMask);
- while( zName ){
- send_config_card(&xfer, zName);
- zName = configure_next_name(configSendMask);
- nCardSent++;
+ if( configSendMask & CONFIGSET_OLDFORMAT ){
+ const char *zName;
+ zName = configure_first_name(configSendMask);
+ while( zName ){
+ send_legacy_config_card(&xfer, zName);
+ zName = configure_next_name(configSendMask);
+ nCardSent++;
+ }
+ }else{
+ nCardSent += configure_send_group(xfer.pOut, configSendMask, 0);
}
configSendMask = 0;
}
/* Append randomness to the end of the message. This makes all
@@ -1647,11 +1663,13 @@
break;
}
blobarray_reset(xfer.aToken, xfer.nToken);
blob_reset(&xfer.line);
}
- if( origConfigRcvMask & (CONFIGSET_TKT|CONFIGSET_USER) ){
+ if( (configRcvMask & (CONFIGSET_USER|CONFIGSET_TKT))!=0
+ && (configRcvMask & CONFIGSET_OLDFORMAT)!=0
+ ){
configure_finalize_receive();
}
origConfigRcvMask = 0;
if( nCardRcvd>0 ){
fossil_print(zValueFormat, "Received:",