Fossil SCM

fossil-scm / src / db.c
Source Blame History 5152 lines
c19f34c… drh 1 ** Copyright (c) 2006 D. Richard Hipp
c06edd2… drh 2 ** modify it under the terms of the Simplified BSD License (also
c06edd2… drh 3 ** known as the "2-Clause License" or "FreeBSD License".)
fd9b7bd… drh 4 **
c06edd2… drh 5 ** but without any warranty; without even the implied warranty of
c06edd2… drh 6 ** merchantability or fitness for a particular purpose.
4645e9b… drh 7 ** (1) The "configdb" database in ~/.fossil or ~/.config/fossil.db
4645e9b… drh 8 ** or in %LOCALAPPDATA%/_fossil
bc36fdc… danield 9 ** (3) A local check-out database named "_FOSSIL_" or ".fslckout"
8bdea95… drh 10 ** and located at the root of the local copy of the source tree.
7aeeb30… mistachkin 11 #if defined(_WIN32)
7aeeb30… mistachkin 12 # if USE_SEE
7aeeb30… mistachkin 13 # include <windows.h>
f41cf03… mistachkin 14 # define GETPID (int)GetCurrentProcessId
7aeeb30… mistachkin 15 # endif
7aeeb30… mistachkin 16 #else
3564af0… drh 17 # include <pwd.h>
f41cf03… mistachkin 18 # if USE_SEE
f41cf03… mistachkin 19 # define GETPID getpid
f41cf03… mistachkin 20 # endif
00dfbdb… mistachkin 21 #endif
00dfbdb… mistachkin 22 #if USE_SEE && !defined(SQLITE_HAS_CODEC)
00dfbdb… mistachkin 23 # define SQLITE_HAS_CODEC
f41cf03… mistachkin 24 #endif
f41cf03… mistachkin 25 #if USE_SEE && defined(__linux__)
f41cf03… mistachkin 26 # include <sys/uio.h>
3564af0… drh 27 #endif
1654456… drh 28 #include <time.h>
f41cf03… mistachkin 29
f41cf03… mistachkin 30 /* BUGBUG: This (PID_T) does not work inside of INTERFACE block. */
f41cf03… mistachkin 31 #if USE_SEE
f41cf03… mistachkin 32 #if defined(_WIN32)
f41cf03… mistachkin 33 typedef DWORD PID_T;
f41cf03… mistachkin 34 #else
f41cf03… mistachkin 35 typedef pid_t PID_T;
f41cf03… mistachkin 36 #endif
f41cf03… mistachkin 37 #endif
f41cf03… mistachkin 38
f41cf03… mistachkin 39 /*
f41cf03… mistachkin 40 ** Type definitions used for handling the saved encryption key for SEE.
f41cf03… mistachkin 41 */
f41cf03… mistachkin 42 #if !defined(_WIN32)
f41cf03… mistachkin 43 typedef void *LPVOID;
f41cf03… mistachkin 44 typedef size_t SIZE_T;
f41cf03… mistachkin 45 #endif
f41cf03… mistachkin 46
f41cf03… mistachkin 47 /*
f41cf03… mistachkin 48 ** Operations for db_maybe_handle_saved_encryption_key_for_process, et al.
f41cf03… mistachkin 49 */
f41cf03… mistachkin 50 #define SEE_KEY_READ ((int)0)
f41cf03… mistachkin 51 #define SEE_KEY_WRITE ((int)1)
f41cf03… mistachkin 52 #define SEE_KEY_ZERO ((int)2)
f41cf03… mistachkin 53
ca6cb44… jan.nijtmans 54 sqlite3_stmt *pStmt; /* The results of sqlite3_prepare_v2() */
791a513… drh 55 Stmt *pNext, *pPrev; /* List of all unfinalized statements */
6454153… drh 56 int nStep; /* Number of sqlite3_step() calls */
421fe24… drh 57 int rc; /* Error from db_vprepare() */
796dcfe… drh 58
796dcfe… drh 59 /*
796dcfe… drh 60 ** Copy this to initialize a Stmt object to a clean/empty state. This
796dcfe… drh 61 ** is useful to help avoid assertions when performing cleanup in some
796dcfe… drh 62 ** error handling cases.
796dcfe… drh 63 */
dc7d0a2… drh 64 #define empty_Stmt_m {BLOB_INITIALIZER,NULL, NULL, NULL, 0, 0}
796dcfe… drh 65 const struct Stmt empty_Stmt = empty_Stmt_m;
f741baa… drh 66 ** This routine throws a fatal error. It does not return.
796dcfe… drh 67 #ifdef FOSSIL_ENABLE_JSON
e7f13b8… stephan 68 if( g.json.isJsonMode!=0 ){
a588e55… mistachkin 69 /*
a588e55… mistachkin 70 ** Avoid calling into the JSON support subsystem if it
a588e55… mistachkin 71 ** has not yet been initialized, e.g. early SQLite log
a588e55… mistachkin 72 ** messages, etc.
a588e55… mistachkin 73 */
e7f13b8… stephan 74 json_bootstrap_early();
796dcfe… drh 75 json_err( 0, z, 1 );
796dcfe… drh 76 }
796dcfe… drh 77 else
796dcfe… drh 78 #endif /* FOSSIL_ENABLE_JSON */
99fcc43… drh 79 if( g.xferPanic && g.cgiOutput==1 ){
2be82dc… drh 80 cgi_reset_content();
2be82dc… drh 81 @ error Database\serror:\s%F(z)
99fcc43… drh 82 cgi_reply();
e7f13b8… stephan 83 }
0ea8703… drh 84 if( strstr(z,"attempt to write a readonly database") ){
0ea8703… drh 85 static const char *azDbNames[] = { "repository", "localdb", "configdb" };
0ea8703… drh 86 int i;
0ea8703… drh 87 for(i=0; i<3; i++){
0ea8703… drh 88 if( sqlite3_db_readonly(g.db, azDbNames[i])==1 ){
0ea8703… drh 89 z = mprintf("\"%s\" is readonly.\n%s",
0ea8703… drh 90 sqlite3_db_filename(g.db,azDbNames[i]), z);
0ea8703… drh 91 }
0ea8703… drh 92 }
0ea8703… drh 93 }
c0eca1f… drh 94 fossil_fatal("Database error: %s", z);
b94e15c… drh 95 }
b94e15c… drh 96
b94e15c… drh 97 /*
b94e15c… drh 98 ** Check a result code. If it is not SQLITE_OK, print the
b94e15c… drh 99 ** corresponding error message and exit.
b94e15c… drh 100 */
b94e15c… drh 101 static void db_check_result(int rc, Stmt *pStmt){
b94e15c… drh 102 if( rc!=SQLITE_OK ){
b94e15c… drh 103 db_err("SQL error (%d,%d: %s) while running [%s]",
b94e15c… drh 104 rc, sqlite3_extended_errcode(g.db),
b94e15c… drh 105 sqlite3_errmsg(g.db), blob_str(&pStmt->sql));
b94e15c… drh 106 }
a537c99… drh 107 }
a537c99… drh 108
a537c99… drh 109 /*
a537c99… drh 110 ** All static variable that a used by only this file are gathered into
a537c99… drh 111 ** the following structure.
a537c99… drh 112 */
a537c99… drh 113 static struct DbLocalData {
f741baa… drh 114 unsigned protectMask; /* Prevent changes to database */
a537c99… drh 115 int nBegin; /* Nesting depth of BEGIN */
a537c99… drh 116 int doRollback; /* True to force a rollback */
a537c99… drh 117 int nCommitHook; /* Number of commit hooks */
d9543f4… drh 118 int wrTxn; /* Outer-most TNX is a write */
a537c99… drh 119 Stmt *pAllStmt; /* List of all unfinalized statements */
ca6cb44… jan.nijtmans 120 int nPrepare; /* Number of calls to sqlite3_prepare_v2() */
a537c99… drh 121 int nDeleteOnFail; /* Number of entries in azDeleteOnFail[] */
a537c99… drh 122 struct sCommitHook {
a537c99… drh 123 int (*xHook)(void); /* Functions to call at db_end_transaction() */
a537c99… drh 124 int sequence; /* Call functions in sequence order */
974cf36… drh 125 } aHook[6];
a537c99… drh 126 char *azDeleteOnFail[3]; /* Files to delete on a failure */
e604d48… drh 127 char *azBeforeCommit[5]; /* Commands to run prior to COMMIT */
e604d48… drh 128 int nBeforeCommit; /* Number of entries in azBeforeCommit */
b22cc4e… drh 129 int nPriorChanges; /* sqlite3_total_changes() at transaction start */
43336f6… drh 130 const char *zStartFile; /* File in which transaction was started */
43336f6… drh 131 int iStartLine; /* Line of zStartFile where transaction started */
f741baa… drh 132 int (*xAuth)(void*,int,const char*,const char*,const char*,const char*);
f741baa… drh 133 void *pAuthArg; /* Argument to the authorizer */
f741baa… drh 134 const char *zAuthName; /* Name of the authorizer */
f741baa… drh 135 int bProtectTriggers; /* True if protection triggers already exist */
f741baa… drh 136 int nProtect; /* Slots of aProtect used */
03e21b9… drh 137 unsigned aProtect[12]; /* Saved values of protectMask */
ebd239d… drh 138 int pauseDmlLog; /* Ignore pDmlLog if positive */
ebd239d… drh 139 Blob *pDmlLog; /* Append DML statements here, of not NULL */
f741baa… drh 140 } db = {
f741baa… drh 141 PROTECT_USER|PROTECT_CONFIG|PROTECT_BASELINE, /* protectMask */
ab05475… danield 142 0, 0, 0, 0, 0, 0, 0, {{0}}, {0}, {0}, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0}};
0aee050… drh 143
0aee050… drh 144 /*
0aee050… drh 145 ** Arrange for the given file to be deleted on a failure.
0aee050… drh 146 */
0aee050… drh 147 void db_delete_on_failure(const char *zFilename){
a537c99… drh 148 assert( db.nDeleteOnFail<count(db.azDeleteOnFail) );
0ac64da… drh 149 if( zFilename==0 ) return;
a537c99… drh 150 db.azDeleteOnFail[db.nDeleteOnFail++] = fossil_strdup(zFilename);
f87fb02… drh 151 }
f87fb02… drh 152
f87fb02… drh 153 /*
f87fb02… drh 154 ** Return the transaction nesting depth. 0 means we are currently
f87fb02… drh 155 ** not in a transaction.
f87fb02… drh 156 */
f87fb02… drh 157 int db_transaction_nesting_depth(void){
f87fb02… drh 158 return db.nBegin;
43336f6… drh 159 }
43336f6… drh 160
43336f6… drh 161 /*
43336f6… drh 162 ** Return a pointer to a string that is the code point where the
43336f6… drh 163 ** current transaction was started.
43336f6… drh 164 */
43336f6… drh 165 char *db_transaction_start_point(void){
43336f6… drh 166 return mprintf("%s:%d", db.zStartFile, db.iStartLine);
0aee050… drh 167 }
7c3cb28… drh 168 ** just prior to each commit. All this routine does is verify
a537c99… drh 169 if( db.nBegin ){
43336f6… drh 170 ** Silently add the filename and line number as parameter to each
43336f6… drh 171 ** db_begin_transaction call.
43336f6… drh 172 #if INTERFACE
0f6a5ee… drh 173 #define db_begin_transaction() db_begin_transaction_real(__FILE__,__LINE__)
0f6a5ee… drh 174 #define db_begin_write() db_begin_write_real(__FILE__,__LINE__)
0f6a5ee… drh 175 #define db_commit_transaction() db_end_transaction(0)
0f6a5ee… drh 176 #define db_rollback_transaction() db_end_transaction(1)
43336f6… drh 177 #endif
43336f6… drh 178
43336f6… drh 179 /*
0f6a5ee… drh 180 ** Begin a nested transaction
43336f6… drh 181 */
43336f6… drh 182 void db_begin_transaction_real(const char *zStartFile, int iStartLine){
a537c99… drh 183 if( db.nBegin==0 ){
b22cc4e… drh 184 db.nPriorChanges = sqlite3_total_changes(g.db);
f3de8b6… drh 185 db.doRollback = 0;
43336f6… drh 186 db.zStartFile = zStartFile;
43336f6… drh 187 db.iStartLine = iStartLine;
d9543f4… drh 188 db.wrTxn = 0;
0f6a5ee… drh 189 }
0f6a5ee… drh 190 db.nBegin++;
0f6a5ee… drh 191 }
0f6a5ee… drh 192 /*
0f6a5ee… drh 193 ** Begin a new transaction for writing.
0f6a5ee… drh 194 */
0f6a5ee… drh 195 void db_begin_write_real(const char *zStartFile, int iStartLine){
0f6a5ee… drh 196 if( db.nBegin==0 ){
147bf47… drh 197 if( !db_is_writeable("repository") ){
147bf47… drh 198 db_multi_exec("BEGIN");
147bf47… drh 199 }else{
147bf47… drh 200 db_multi_exec("BEGIN IMMEDIATE");
147bf47… drh 201 sqlite3_commit_hook(g.db, db_verify_at_commit, 0);
147bf47… drh 202 db.nPriorChanges = sqlite3_total_changes(g.db);
147bf47… drh 203 db.doRollback = 0;
147bf47… drh 204 db.zStartFile = zStartFile;
147bf47… drh 205 db.iStartLine = iStartLine;
147bf47… drh 206 db.wrTxn = 1;
147bf47… drh 207 }
d9543f4… drh 208 }else if( !db.wrTxn ){
0f6a5ee… drh 209 fossil_warning("read txn at %s:%d might cause SQLITE_BUSY "
0f6a5ee… drh 210 "for the write txn at %s:%d",
0f6a5ee… drh 211 db.zStartFile, db.iStartLine, zStartFile, iStartLine);
a537c99… drh 212 }
a537c99… drh 213 db.nBegin++;
a537c99… drh 214 }
0f6a5ee… drh 215
0f6a5ee… drh 216 /* End a transaction previously started using db_begin_transaction()
0f6a5ee… drh 217 ** or db_begin_write().
0f6a5ee… drh 218 */
932825b… drh 219 if( g.db==0 ) return;
f87fb02… drh 220 if( db.nBegin<=0 ){
8d9ad75… drh 221 fossil_warning("Extra call to db_end_transaction");
f87fb02… drh 222 return;
f87fb02… drh 223 }
f3de8b6… drh 224 if( rollbackFlag ){
f3de8b6… drh 225 db.doRollback = 1;
f3de8b6… drh 226 if( g.fSqlTrace ) fossil_trace("-- ROLLBACK by request\n");
f3de8b6… drh 227 }
a537c99… drh 228 db.nBegin--;
a537c99… drh 229 if( db.nBegin==0 ){
b22cc4e… drh 230 if( db.doRollback==0 && db.nPriorChanges<sqlite3_total_changes(g.db) ){
9491f1d… mistachkin 231 i = 0;
f741baa… drh 232 db_protect_only(PROTECT_SENSITIVE);
e604d48… drh 233 while( db.nBeforeCommit ){
e604d48… drh 234 db.nBeforeCommit--;
9491f1d… mistachkin 235 sqlite3_exec(g.db, db.azBeforeCommit[i], 0, 0, 0);
9491f1d… mistachkin 236 sqlite3_free(db.azBeforeCommit[i]);
9491f1d… mistachkin 237 i++;
e604d48… drh 238 }
e604d48… drh 239 leaf_do_pending_checks();
f741baa… drh 240 db_protect_pop();
e604d48… drh 241 }
a537c99… drh 242 for(i=0; db.doRollback==0 && i<db.nCommitHook; i++){
f3de8b6… drh 243 int rc = db.aHook[i].xHook();
f3de8b6… drh 244 if( rc ){
f3de8b6… drh 245 db.doRollback = 1;
f3de8b6… drh 246 if( g.fSqlTrace ) fossil_trace("-- ROLLBACK due to aHook[%d]\n", i);
f3de8b6… drh 247 }
a537c99… drh 248 }
a537c99… drh 249 while( db.pAllStmt ){
a537c99… drh 250 db_finalize(db.pAllStmt);
a537c99… drh 251 }
49b0ff1… drh 252 db_multi_exec("%s", db.doRollback ? "ROLLBACK" : "COMMIT");
a537c99… drh 253 db.doRollback = 0;
c2f5dbe… drh 254
c2f5dbe… drh 255 /*
c2f5dbe… drh 256 ** Force a rollback and shutdown the database
c2f5dbe… drh 257 */
0aee050… drh 258 int i;
7c3cb28… drh 259 static int busy = 0;
9515143… drh 260 sqlite3_stmt *pStmt = 0;
932825b… drh 261 if( busy || g.db==0 ) return;
7c3cb28… drh 262 busy = 1;
7c3cb28… drh 263 undo_rollback();
9515143… drh 264 while( (pStmt = sqlite3_next_stmt(g.db,pStmt))!=0 ){
9515143… drh 265 sqlite3_reset(pStmt);
c2f5dbe… drh 266 }
a537c99… drh 267 while( db.pAllStmt ){
a537c99… drh 268 db_finalize(db.pAllStmt);
9515143… drh 269 }
a537c99… drh 270 if( db.nBegin ){
a537c99… drh 271 db.nBegin = 0;
7c3cb28… drh 272 busy = 0;
c2f5dbe… drh 273 db_close(0);
a537c99… drh 274 for(i=0; i<db.nDeleteOnFail; i++){
a537c99… drh 275 file_delete(db.azDeleteOnFail[i]);
0aee050… drh 276 }
db0c512… drh 277 ** Each commit hook is called (in order of ascending sequence) at
beb91c9… jan.nijtmans 278 assert( db.nCommitHook < count(db.aHook) );
a537c99… drh 279 for(i=0; i<db.nCommitHook; i++){
a537c99… drh 280 assert( x!=db.aHook[i].xHook );
a537c99… drh 281 if( db.aHook[i].sequence>sequence ){
a537c99… drh 282 sequence = db.aHook[i].sequence;
a537c99… drh 283 x = db.aHook[i].xHook;
a537c99… drh 284 db.aHook[i].sequence = s;
a537c99… drh 285 db.aHook[i].xHook = xS;
a537c99… drh 286 }
a537c99… drh 287 }
a537c99… drh 288 db.aHook[db.nCommitHook].sequence = sequence;
a537c99… drh 289 db.aHook[db.nCommitHook].xHook = x;
a537c99… drh 290 db.nCommitHook++;
a537c99… drh 291 }
a537c99… drh 292
11c46fd… drh 293 #if INTERFACE
11c46fd… drh 294 /*
f741baa… drh 295 ** Flag bits for db_protect() and db_unprotect() indicating which parts
f741baa… drh 296 ** of the databases should be write protected or write enabled, respectively.
f741baa… drh 297 */
f741baa… drh 298 #define PROTECT_USER 0x01 /* USER table */
f741baa… drh 299 #define PROTECT_CONFIG 0x02 /* CONFIG and GLOBAL_CONFIG tables */
f741baa… drh 300 #define PROTECT_SENSITIVE 0x04 /* Sensitive and/or global settings */
f741baa… drh 301 #define PROTECT_READONLY 0x08 /* everything except TEMP tables */
f741baa… drh 302 #define PROTECT_BASELINE 0x10 /* protection system is working */
f741baa… drh 303 #define PROTECT_ALL 0x1f /* All of the above */
f741baa… drh 304 #define PROTECT_NONE 0x00 /* Nothing. Everything is open */
f741baa… drh 305 #endif /* INTERFACE */
f741baa… drh 306
f741baa… drh 307 /*
f741baa… drh 308 ** Enable or disable database write protections.
f741baa… drh 309 **
f741baa… drh 310 ** db_protext(X) Add protects on X
f741baa… drh 311 ** db_unprotect(X) Remove protections on X
f741baa… drh 312 ** db_protect_only(X) Remove all prior protections then set
f741baa… drh 313 ** protections to only X.
f741baa… drh 314 **
f741baa… drh 315 ** Each of these routines pushes the previous protection mask onto
f741baa… drh 316 ** a finite-size stack. Each should be followed by a call to
f741baa… drh 317 ** db_protect_pop() to pop the stack and restore the protections that
f741baa… drh 318 ** existed prior to the call. The protection mask stack has a limited
ca5a5c7… stephan 319 ** depth, so take care not to nest calls too deeply.
f741baa… drh 320 **
f741baa… drh 321 ** About Database Write Protection
f741baa… drh 322 ** -------------------------------
f741baa… drh 323 **
f741baa… drh 324 ** This is *not* a primary means of defending the application from
f741baa… drh 325 ** attack. Fossil should be secure even if this mechanism is disabled.
f741baa… drh 326 ** The purpose of database write protection is to provide an additional
f741baa… drh 327 ** layer of defense in case SQL injection bugs somehow slip into other
f741baa… drh 328 ** parts of the system. In other words, database write protection is
0aa3483… drh 329 ** not the primary defense but rather defense in depth.
f741baa… drh 330 **
f741baa… drh 331 ** This mechanism mostly focuses on the USER table, to prevent an
f741baa… drh 332 ** attacker from giving themselves Admin privilegs, and on the
0aa3483… drh 333 ** CONFIG table and especially "sensitive" settings such as
f741baa… drh 334 ** "diff-command" or "editor" that if compromised by an attacker
f741baa… drh 335 ** could lead to an RCE.
f741baa… drh 336 **
f741baa… drh 337 ** By default, the USER and CONFIG tables are read-only. Various
f741baa… drh 338 ** subsystems that legitimately need to change those tables can
f741baa… drh 339 ** temporarily do so using:
f741baa… drh 340 **
f741baa… drh 341 ** db_unprotect(PROTECT_xxx);
f741baa… drh 342 ** // make the legitmate changes here
f741baa… drh 343 ** db_protect_pop();
f741baa… drh 344 **
f741baa… drh 345 ** Code that runs inside of reduced protections should be carefully
f741baa… drh 346 ** reviewed to ensure that it is harmless and not subject to SQL
f741baa… drh 347 ** injection.
f741baa… drh 348 **
f741baa… drh 349 ** Read-only operations (such as many web pages like /timeline)
f741baa… drh 350 ** can invoke db_protect(PROTECT_ALL) to effectively make the database
f741baa… drh 351 ** read-only. TEMP tables (which are often used for these kinds of
f741baa… drh 352 ** pages) are still writable, however.
f741baa… drh 353 **
f741baa… drh 354 ** The PROTECT_SENSITIVE protection is a subset of PROTECT_CONFIG
f741baa… drh 355 ** that blocks changes to all of the global_config table, but only
f741baa… drh 356 ** "sensitive" settings in the config table. PROTECT_SENSITIVE
f741baa… drh 357 ** relies on triggers and the protected_setting() SQL function to
f741baa… drh 358 ** prevent changes to sensitive settings.
f741baa… drh 359 **
0aa3483… drh 360 ** PROTECT_READONLY is set for any HTTP request for which the HTTP_REFERER
0aa3483… drh 361 ** is not the same origin. This is an additional defense against cross-site-
0aa3483… drh 362 ** scripting attacks. As with all of these defenses, this is only an extra
0aa3483… drh 363 ** backup layer. Fossil should be proof against XSS attacks even without this.
0aa3483… drh 364 **
0aa3483… drh 365 ** Any violation of these security restrictions results in a SECURITY message
0aa3483… drh 366 ** in the server log (if enabled). A violation of any of these restrictions
0aa3483… drh 367 ** probably indicates a bug in Fossil and should be reported to the
0aa3483… drh 368 ** developers.
0aa3483… drh 369 **
f741baa… drh 370 ** Additional Notes
f741baa… drh 371 ** ----------------
f741baa… drh 372 **
f741baa… drh 373 ** Calls to routines like db_set() and db_unset() temporarily disable
f741baa… drh 374 ** the PROTECT_CONFIG protection. The assumption is that these calls
f741baa… drh 375 ** cannot be invoked by an SQL injection and are thus safe. Make sure
f741baa… drh 376 ** this is the case by always using a string literal as the name argument
f741baa… drh 377 ** to db_set() and db_unset() and friend, not a variable that might
f741baa… drh 378 ** be compromised by an attack.
f741baa… drh 379 */
f741baa… drh 380 void db_protect_only(unsigned flags){
f741baa… drh 381 if( db.nProtect>=count(db.aProtect)-2 ){
f741baa… drh 382 fossil_panic("too many db_protect() calls");
f741baa… drh 383 }
f741baa… drh 384 db.aProtect[db.nProtect++] = db.protectMask;
275da70… danield 385 if( (flags & PROTECT_SENSITIVE)!=0
f741baa… drh 386 && db.bProtectTriggers==0
f741baa… drh 387 && g.repositoryOpen
f741baa… drh 388 ){
f741baa… drh 389 /* Create the triggers needed to protect sensitive settings from
f741baa… drh 390 ** being created or modified the first time that PROTECT_SENSITIVE
f741baa… drh 391 ** is enabled. Deleting a sensitive setting is harmless, so there
f741baa… drh 392 ** is not trigger to block deletes. After being created once, the
f741baa… drh 393 ** triggers persist for the life of the database connection. */
f8363db… drh 394 unsigned savedProtectMask = db.protectMask;
f8363db… drh 395 db.protectMask = 0;
f741baa… drh 396 db_multi_exec(
f741baa… drh 397 "CREATE TEMP TRIGGER protect_1 BEFORE INSERT ON config"
f741baa… drh 398 " WHEN protected_setting(new.name) BEGIN"
f741baa… drh 399 " SELECT raise(abort,'not authorized');"
f741baa… drh 400 "END;\n"
f741baa… drh 401 "CREATE TEMP TRIGGER protect_2 BEFORE UPDATE ON config"
f741baa… drh 402 " WHEN protected_setting(new.name) BEGIN"
f741baa… drh 403 " SELECT raise(abort,'not authorized');"
f741baa… drh 404 "END;\n"
f741baa… drh 405 );
f741baa… drh 406 db.bProtectTriggers = 1;
f8363db… drh 407 db.protectMask = savedProtectMask;
f741baa… drh 408 }
f741baa… drh 409 db.protectMask = flags;
f741baa… drh 410 }
f741baa… drh 411 void db_protect(unsigned flags){
f741baa… drh 412 db_protect_only(db.protectMask | flags);
f741baa… drh 413 }
f741baa… drh 414 void db_unprotect(unsigned flags){
f741baa… drh 415 if( db.nProtect>=count(db.aProtect)-2 ){
f741baa… drh 416 fossil_panic("too many db_unprotect() calls");
f741baa… drh 417 }
f741baa… drh 418 db.aProtect[db.nProtect++] = db.protectMask;
8e85d6c… drh 419 db.protectMask &= ~(flags|PROTECT_READONLY);
f741baa… drh 420 }
f741baa… drh 421 void db_protect_pop(void){
f741baa… drh 422 if( db.nProtect<1 ){
f741baa… drh 423 fossil_panic("too many db_protect_pop() calls");
f741baa… drh 424 }
f741baa… drh 425 db.protectMask = db.aProtect[--db.nProtect];
f8363db… drh 426 }
f8363db… drh 427 int db_is_protected(unsigned flags){
f8363db… drh 428 return (db.protectMask & flags)!=0;
31c7bdb… larrybr 429 }
f741baa… drh 430
f741baa… drh 431 /*
31c7bdb… larrybr 432 ** Verify that the desired database write protections are in place.
f741baa… drh 433 ** Throw a fatal error if not.
f741baa… drh 434 */
f741baa… drh 435 void db_assert_protected(unsigned flags){
f741baa… drh 436 if( (flags & db.protectMask)!=flags ){
31c7bdb… larrybr 437 fossil_fatal("missing database write protection bits: %02x",
f741baa… drh 438 flags & ~db.protectMask);
f741baa… drh 439 }
f741baa… drh 440 }
f741baa… drh 441
f741baa… drh 442 /*
f741baa… drh 443 ** Assert that either all protections are off (including PROTECT_BASELINE
f741baa… drh 444 ** which is usually always enabled), or the setting named in the argument
f741baa… drh 445 ** is no a sensitive setting.
f741baa… drh 446 **
f741baa… drh 447 ** This assert() is used to verify that the db_set() and db_set_int()
f741baa… drh 448 ** interfaces do not modify a sensitive setting.
f741baa… drh 449 */
f741baa… drh 450 void db_assert_protection_off_or_not_sensitive(const char *zName){
f741baa… drh 451 if( db.protectMask!=0 && db_setting_is_protected(zName) ){
f741baa… drh 452 fossil_panic("unauthorized change to protected setting \"%s\"", zName);
f741baa… drh 453 }
f741baa… drh 454 }
f741baa… drh 455
f741baa… drh 456 /*
f741baa… drh 457 ** Every Fossil database connection automatically registers the following
f741baa… drh 458 ** overarching authenticator callback, and leaves it registered for the
f741baa… drh 459 ** duration of the connection. This authenticator will call any
f741baa… drh 460 ** sub-authenticators that are registered using db_set_authorizer().
3d8bb63… drh 461 **
3d8bb63… drh 462 ** == Testing Notes ==
3d8bb63… drh 463 **
3d8bb63… drh 464 ** Run Fossil as using a command like this:
3d8bb63… drh 465 **
3d8bb63… drh 466 ** ./fossil sql --test --errorlog -
3d8bb63… drh 467 **
3d8bb63… drh 468 ** Then enter SQL commands like one of these:
3d8bb63… drh 469 **
3d8bb63… drh 470 ** SELECT db_protect('user');
3d8bb63… drh 471 ** SELECT db_protect('config');
3d8bb63… drh 472 ** SELECT db_protect('sensitive');
3d8bb63… drh 473 ** SELECT db_protect('readonly');
3d8bb63… drh 474 ** SELECT db_protect('all');
3d8bb63… drh 475 **
3d8bb63… drh 476 ** Then try to do SQL statements that would violate the constraints and
3d8bb63… drh 477 ** verify that SECURITY warnings appear in the error log output. See
3d8bb63… drh 478 ** also the sqlcmd_db_protect() function in sqlcmd.c.
f741baa… drh 479 */
f741baa… drh 480 int db_top_authorizer(
f741baa… drh 481 void *pNotUsed,
f741baa… drh 482 int eCode,
f741baa… drh 483 const char *z0,
f741baa… drh 484 const char *z1,
f741baa… drh 485 const char *z2,
f741baa… drh 486 const char *z3
f741baa… drh 487 ){
f741baa… drh 488 int rc = SQLITE_OK;
f741baa… drh 489 switch( eCode ){
f741baa… drh 490 case SQLITE_INSERT:
f741baa… drh 491 case SQLITE_UPDATE:
f741baa… drh 492 case SQLITE_DELETE: {
f741baa… drh 493 if( (db.protectMask & PROTECT_USER)!=0
f741baa… drh 494 && sqlite3_stricmp(z0,"user")==0 ){
3d8bb63… drh 495 fossil_errorlog(
3d8bb63… drh 496 "SECURITY: authorizer blocks DML on protected USER table\n");
f741baa… drh 497 rc = SQLITE_DENY;
f741baa… drh 498 }else if( (db.protectMask & PROTECT_CONFIG)!=0 &&
f741baa… drh 499 (sqlite3_stricmp(z0,"config")==0 ||
f741baa… drh 500 sqlite3_stricmp(z0,"global_config")==0) ){
3d8bb63… drh 501 fossil_errorlog(
3d8bb63… drh 502 "SECURITY: authorizer blocks DML on protected table \"%s\"\n", z0);
f741baa… drh 503 rc = SQLITE_DENY;
f741baa… drh 504 }else if( (db.protectMask & PROTECT_SENSITIVE)!=0 &&
f741baa… drh 505 sqlite3_stricmp(z0,"global_config")==0 ){
3d8bb63… drh 506 fossil_errorlog(
3d8bb63… drh 507 "SECURITY: authorizer blocks DML on protected GLOBAL_CONFIG table\n");
f741baa… drh 508 rc = SQLITE_DENY;
f741baa… drh 509 }else if( (db.protectMask & PROTECT_READONLY)!=0
b4e0062… drh 510 && (sqlite3_stricmp(z2, "repository")==0
b4e0062… drh 511 || sqlite3_stricmp(z2,"configdb")==0
b4e0062… drh 512 || sqlite3_stricmp(z2,"localdb")==0) ){
b4e0062… drh 513 /* The READONLY constraint only applies to persistent database files.
b4e0062… drh 514 ** "temp" and "mem1" and other transient databases are not
b4e0062… drh 515 ** constrained by READONLY. */
3d8bb63… drh 516 fossil_errorlog(
f8363db… drh 517 "SECURITY: authorizer blocks DML on table \"%s\" due to the "
3d8bb63… drh 518 "request coming from a different origin\n", z0);
f741baa… drh 519 rc = SQLITE_DENY;
f741baa… drh 520 }
f741baa… drh 521 break;
f741baa… drh 522 }
f741baa… drh 523 case SQLITE_DROP_TEMP_TRIGGER: {
f741baa… drh 524 /* Do not allow the triggers that enforce PROTECT_SENSITIVE
f741baa… drh 525 ** to be dropped */
3d8bb63… drh 526 fossil_errorlog(
3d8bb63… drh 527 "SECURITY: authorizer blocks attempt to drop a temporary trigger\n");
f741baa… drh 528 rc = SQLITE_DENY;
f741baa… drh 529 break;
f741baa… drh 530 }
f741baa… drh 531 }
f741baa… drh 532 if( db.xAuth && rc==SQLITE_OK ){
f741baa… drh 533 rc = db.xAuth(db.pAuthArg, eCode, z0, z1, z2, z3);
f741baa… drh 534 }
f741baa… drh 535 return rc;
f741baa… drh 536 }
f741baa… drh 537
f741baa… drh 538 /*
f741baa… drh 539 ** Set or unset the query authorizer callback function
f741baa… drh 540 */
f741baa… drh 541 void db_set_authorizer(
f741baa… drh 542 int(*xAuth)(void*,int,const char*,const char*,const char*,const char*),
f741baa… drh 543 void *pArg,
f741baa… drh 544 const char *zName /* for tracing */
f741baa… drh 545 ){
f741baa… drh 546 if( db.xAuth ){
f741baa… drh 547 fossil_panic("multiple active db_set_authorizer() calls");
f741baa… drh 548 }
f741baa… drh 549 db.xAuth = xAuth;
f741baa… drh 550 db.pAuthArg = pArg;
f741baa… drh 551 db.zAuthName = zName;
f741baa… drh 552 if( g.fSqlTrace ) fossil_trace("-- set authorizer %s\n", zName);
f741baa… drh 553 }
f741baa… drh 554 void db_clear_authorizer(void){
f741baa… drh 555 if( db.zAuthName && g.fSqlTrace ){
f741baa… drh 556 fossil_trace("-- discontinue authorizer %s\n", db.zAuthName);
f741baa… drh 557 }
f741baa… drh 558 db.xAuth = 0;
f741baa… drh 559 db.pAuthArg = 0;
f741baa… drh 560 db.zAuthName = 0;
f741baa… drh 561 }
f741baa… drh 562
f741baa… drh 563 #if INTERFACE
f741baa… drh 564 /*
11c46fd… drh 565 ** Possible flags to db_vprepare
11c46fd… drh 566 */
11c46fd… drh 567 #define DB_PREPARE_IGNORE_ERROR 0x001 /* Suppress errors */
11c46fd… drh 568 #define DB_PREPARE_PERSISTENT 0x002 /* Stmt will stick around for a while */
11c46fd… drh 569 #endif
f741baa… drh 570
f741baa… drh 571 /*
ebd239d… drh 572 ** If zSql is a DML statement, append it db.pDmlLog.
ebd239d… drh 573 */
ebd239d… drh 574 static void db_append_dml(const char *zSql){
ebd239d… drh 575 size_t nSql;
ebd239d… drh 576 if( db.pDmlLog==0 ) return;
ebd239d… drh 577 if( db.pauseDmlLog ) return;
ebd239d… drh 578 if( zSql==0 ) return;
ebd239d… drh 579 nSql = strlen(zSql);
ebd239d… drh 580 while( nSql>0 && fossil_isspace(zSql[0]) ){ nSql--; zSql++; }
ebd239d… drh 581 while( nSql>0 && fossil_isspace(zSql[nSql-1]) ) nSql--;
ebd239d… drh 582 if( nSql<6 ) return;
02491c2… drh 583 if( fossil_strnicmp(zSql, "SELECT", 6)==0 ) return;
02491c2… drh 584 if( fossil_strnicmp(zSql, "PRAGMA", 6)==0 ) return;
ebd239d… drh 585 blob_append(db.pDmlLog, zSql, nSql);
ebd239d… drh 586 if( zSql[nSql-1]!=';' ) blob_append_char(db.pDmlLog, ';');
ebd239d… drh 587 blob_append_char(db.pDmlLog, '\n');
ebd239d… drh 588 }
ebd239d… drh 589
ebd239d… drh 590 /*
ebd239d… drh 591 ** Set the Blob to which DML statement text should be appended. Set it
ebd239d… drh 592 ** to zero to stop appending DML statement text.
ebd239d… drh 593 */
ebd239d… drh 594 void db_append_dml_to_blob(Blob *pBlob){
ebd239d… drh 595 db.pDmlLog = pBlob;
ebd239d… drh 596 }
ebd239d… drh 597
ebd239d… drh 598 /*
ebd239d… drh 599 ** Pause or unpause the DML log
ebd239d… drh 600 */
ebd239d… drh 601 void db_pause_dml_log(void){ db.pauseDmlLog++; }
ebd239d… drh 602 void db_unpause_dml_log(void){ db.pauseDmlLog--; }
ebd239d… drh 603
ebd239d… drh 604 /*
11c46fd… drh 605 int db_vprepare(Stmt *pStmt, int flags, const char *zFormat, va_list ap){
f1efc90… drh 606 int rc;
11c46fd… drh 607 int prepFlags = 0;
791a513… drh 608 char *zSql;
f741baa… drh 609 const char *zExtra = 0;
791a513… drh 610 zSql = blob_str(&pStmt->sql);
a537c99… drh 611 db.nPrepare++;
ebd239d… drh 612 db_append_dml(zSql);
11c46fd… drh 613 if( flags & DB_PREPARE_PERSISTENT ){
11c46fd… drh 614 prepFlags = SQLITE_PREPARE_PERSISTENT;
11c46fd… drh 615 }
f741baa… drh 616 rc = sqlite3_prepare_v3(g.db, zSql, -1, prepFlags, &pStmt->pStmt, &zExtra);
9a2e5f4… drh 617 if( rc!=0 && (flags & DB_PREPARE_IGNORE_ERROR)==0 ){
3dce979… drh 618 db_err("%s\n%s", sqlite3_errmsg(g.db), zSql);
f741baa… drh 619 }else if( zExtra && !fossil_all_whitespace(zExtra) ){
f741baa… drh 620 db_err("surplus text follows SQL: \"%s\"", zExtra);
d216ea9… drh 621 }
d216ea9… drh 622 pStmt->pNext = db.pAllStmt;
d216ea9… drh 623 pStmt->pPrev = 0;
d216ea9… drh 624 if( db.pAllStmt ) db.pAllStmt->pPrev = pStmt;
d216ea9… drh 625 db.pAllStmt = pStmt;
6454153… drh 626 pStmt->nStep = 0;
421fe24… drh 627 pStmt->rc = rc;
f1efc90… drh 628 return rc;
f1efc90… drh 629 rc = db_vprepare(pStmt, 0, zFormat, ap);
f1efc90… drh 630 va_end(ap);
f1efc90… drh 631 return rc;
f1efc90… drh 632 }
f1efc90… drh 633 int db_prepare_ignore_error(Stmt *pStmt, const char *zFormat, ...){
f1efc90… drh 634 int rc;
f1efc90… drh 635 va_list ap;
f1efc90… drh 636 va_start(ap, zFormat);
11c46fd… drh 637 rc = db_vprepare(pStmt, DB_PREPARE_IGNORE_ERROR, zFormat, ap);
d216ea9… drh 638
d216ea9… drh 639 /* This variant of db_prepare() checks to see if the statement has
d216ea9… drh 640 ** already been prepared, and if it has it becomes a no-op.
d216ea9… drh 641 */
11c46fd… drh 642 rc = db_vprepare(pStmt, DB_PREPARE_PERSISTENT, zFormat, ap);
70f3092… drh 643 }
70f3092… drh 644
70f3092… drh 645 /* Return TRUE if static Stmt object pStmt has been initialized.
70f3092… drh 646 */
70f3092… drh 647 int db_static_stmt_is_init(Stmt *pStmt){
70f3092… drh 648 return blob_size(&pStmt->sql)>0;
b77f1aa… drh 649 }
b77f1aa… drh 650
b77f1aa… drh 651 /* Prepare a statement using text placed inside a Blob
b77f1aa… drh 652 ** using blob_append_sql().
b77f1aa… drh 653 */
b77f1aa… drh 654 int db_prepare_blob(Stmt *pStmt, Blob *pSql){
b77f1aa… drh 655 int rc;
b77f1aa… drh 656 char *zSql;
b77f1aa… drh 657 pStmt->sql = *pSql;
b77f1aa… drh 658 blob_init(pSql, 0, 0);
b77f1aa… drh 659 zSql = blob_sql_text(&pStmt->sql);
b77f1aa… drh 660 db.nPrepare++;
b77f1aa… drh 661 rc = sqlite3_prepare_v3(g.db, zSql, -1, 0, &pStmt->pStmt, 0);
b77f1aa… drh 662 if( rc!=0 ){
b77f1aa… drh 663 db_err("%s\n%s", sqlite3_errmsg(g.db), zSql);
b77f1aa… drh 664 }
b77f1aa… drh 665 pStmt->pNext = pStmt->pPrev = 0;
b77f1aa… drh 666 pStmt->nStep = 0;
b77f1aa… drh 667 pStmt->rc = rc;
b77f1aa… drh 668 return rc;
b77f1aa… drh 669 }
b77f1aa… drh 670
3dce979… drh 671 db_err("no such bind parameter: %s\nSQL: %b", zParamName, &pStmt->sql);
5a66b6e… drh 672 int db_bind_text16(Stmt *pStmt, const char *zParamName, const char *zValue){
5a66b6e… drh 673 return sqlite3_bind_text16(pStmt->pStmt, paramIdx(pStmt, zParamName), zValue,
5a66b6e… drh 674 -1, SQLITE_STATIC);
5a66b6e… drh 675 }
db0c512… drh 676 ** to the SQL variable. Contrast this to bind_blob() which treats
791a513… drh 677 int rc;
421fe24… drh 678 if( pStmt->pStmt==0 ) return pStmt->rc;
791a513… drh 679 rc = sqlite3_step(pStmt->pStmt);
6454153… drh 680 pStmt->nStep++;
6454153… drh 681 }
6454153… drh 682
6454153… drh 683 /*
6454153… drh 684 ** Print warnings if a query is inefficient.
6454153… drh 685 */
6454153… drh 686 static void db_stats(Stmt *pStmt){
6454153… drh 687 #ifdef FOSSIL_DEBUG
96722b6… drh 688 int c1, c2, c3;
6454153… drh 689 const char *zSql = sqlite3_sql(pStmt->pStmt);
6454153… drh 690 if( zSql==0 ) return;
6454153… drh 691 c1 = sqlite3_stmt_status(pStmt->pStmt, SQLITE_STMTSTATUS_FULLSCAN_STEP, 1);
96722b6… drh 692 c2 = sqlite3_stmt_status(pStmt->pStmt, SQLITE_STMTSTATUS_AUTOINDEX, 1);
96722b6… drh 693 c3 = sqlite3_stmt_status(pStmt->pStmt, SQLITE_STMTSTATUS_SORT, 1);
6454153… drh 694 if( c1>pStmt->nStep*4 && strstr(zSql,"/*scan*/")==0 ){
6454153… drh 695 fossil_warning("%d scan steps for %d rows in [%s]", c1, pStmt->nStep, zSql);
96722b6… drh 696 }else if( c2 ){
96722b6… drh 697 fossil_warning("%d automatic index rows in [%s]", c2, zSql);
96722b6… drh 698 }else if( c3 && strstr(zSql,"/*sort*/")==0 && strstr(zSql,"/*scan*/")==0 ){
6454153… drh 699 fossil_warning("sort w/o index in [%s]", zSql);
6454153… drh 700 }
6454153… drh 701 pStmt->nStep = 0;
6454153… drh 702 #endif
6454153… drh 703 int rc;
2a0c020… mistachkin 704 if( g.fSqlStats ){ db_stats(pStmt); }
6454153… drh 705 rc = sqlite3_reset(pStmt->pStmt);
b94e15c… drh 706 db_check_result(rc, pStmt);
791a513… drh 707 if( pStmt->pNext ){
791a513… drh 708 pStmt->pNext->pPrev = pStmt->pPrev;
791a513… drh 709 }
791a513… drh 710 if( pStmt->pPrev ){
791a513… drh 711 pStmt->pPrev->pNext = pStmt->pNext;
a537c99… drh 712 }else if( db.pAllStmt==pStmt ){
a537c99… drh 713 db.pAllStmt = pStmt->pNext;
791a513… drh 714 }
791a513… drh 715 pStmt->pNext = 0;
791a513… drh 716 pStmt->pPrev = 0;
2a0c020… mistachkin 717 if( g.fSqlStats ){ db_stats(pStmt); }
99fcc43… drh 718 blob_reset(&pStmt->sql);
99fcc43… drh 719 rc = sqlite3_finalize(pStmt->pStmt);
b94e15c… drh 720 db_check_result(rc, pStmt);
99fcc43… drh 721 pStmt->pStmt = 0;
5aa2aee… drh 722 int db_last_insert_rowid(void){
5aa2aee… drh 723 i64 x = sqlite3_last_insert_rowid(g.db);
5aa2aee… drh 724 if( x<0 || x>(i64)2147483647 ){
3f5ab71… drh 725 fossil_panic("rowid out of range (0..2147483647)");
5aa2aee… drh 726 }
5aa2aee… drh 727 return (int)x;
a81a47f… drh 728 int db_column_type(Stmt *pStmt, int N){
a81a47f… drh 729 return sqlite3_column_type(pStmt->pStmt, N);
a81a47f… drh 730 }
3543ed6… drh 731 const char *db_column_raw(Stmt *pStmt, int N){
3543ed6… drh 732 return (const char*)sqlite3_column_blob(pStmt->pStmt, N);
3543ed6… drh 733 }
0c9dff6… drh 734 return fossil_strdup_nn(db_column_text(pStmt, N));
264223f… drh 735 }
264223f… drh 736 Blob db_column_text_as_blob(Stmt *pStmt, int N){
264223f… drh 737 Blob x;
43336f6… drh 738 blob_init(&x, (char*)sqlite3_column_text(pStmt->pStmt,N),
264223f… drh 739 sqlite3_column_bytes(pStmt->pStmt,N));
264223f… drh 740 return x;
db0c512… drh 741 }
db0c512… drh 742 ** Initialize a blob to an ephemeral copy of the content of a
b94e15c… drh 743 db_check_result(rc, pStmt);
f97e1cf… drh 744 return rc;
b94e15c… drh 745 }
b94e15c… drh 746
b94e15c… drh 747 /*
b94e15c… drh 748 ** COMMAND: test-db-exec-error
f741baa… drh 749 ** Usage: %fossil test-db-exec-error
b94e15c… drh 750 **
b94e15c… drh 751 ** Invoke the db_exec() interface with an erroneous SQL statement
b94e15c… drh 752 ** in order to verify the error handling logic.
b94e15c… drh 753 */
b94e15c… drh 754 void db_test_db_exec_cmd(void){
b94e15c… drh 755 Stmt err;
b94e15c… drh 756 db_find_and_open_repository(0,0);
b94e15c… drh 757 db_prepare(&err, "INSERT INTO repository.config(name) VALUES(NULL);");
b94e15c… drh 758 db_exec(&err);
f741baa… drh 759 }
f741baa… drh 760
f741baa… drh 761 /*
f741baa… drh 762 ** COMMAND: test-db-prepare
c717f1e… drh 763 ** Usage: %fossil test-db-prepare ?OPTIONS? SQL-STATEMENT
c717f1e… drh 764 **
c717f1e… drh 765 ** Options:
2512d2d… km 766 ** --auth-report Enable the ticket report query authorizer
2512d2d… km 767 ** --auth-ticket Enable the ticket schema query authorizer
f741baa… drh 768 **
f741baa… drh 769 ** Invoke db_prepare() on the SQL input. Report any errors encountered.
f741baa… drh 770 ** This command is used to verify error detection logic in the db_prepare()
f741baa… drh 771 ** utility routine.
f741baa… drh 772 */
f741baa… drh 773 void db_test_db_prepare(void){
c717f1e… drh 774 const int fAuthReport = find_option("auth-report",0,0)!=0;
c717f1e… drh 775 const int fAuthSchema = find_option("auth-ticket",0,0)!=0;
c717f1e… drh 776 char * zReportErr = 0; /* auth-report error string. */
c717f1e… drh 777 int nSchemaErr = 0; /* Number of auth-ticket errors. */
f741baa… drh 778 Stmt err;
c717f1e… drh 779
e0686dd… stephan 780 if(fAuthReport + fAuthSchema > 1){
c717f1e… drh 781 fossil_fatal("Only one of --auth-report or --auth-ticket "
c717f1e… drh 782 "may be used.");
c717f1e… drh 783 }
f741baa… drh 784 db_find_and_open_repository(0,0);
f741baa… drh 785 verify_all_options();
f741baa… drh 786 if( g.argc!=3 ) usage("?OPTIONS? SQL");
c717f1e… drh 787 if(fAuthReport){
c717f1e… drh 788 report_restrict_sql(&zReportErr);
c717f1e… drh 789 }else if(fAuthSchema){
c717f1e… drh 790 ticket_restrict_sql(&nSchemaErr);
c717f1e… drh 791 }
f741baa… drh 792 db_prepare(&err, "%s", g.argv[2]/*safe-for-%s*/);
f741baa… drh 793 db_finalize(&err);
c717f1e… drh 794 if(fAuthReport){
b18c06e… stephan 795 report_unrestrict_sql();
b18c06e… stephan 796 if(zReportErr){
b18c06e… stephan 797 fossil_warning("Report authorizer error: %s\n", zReportErr);
b18c06e… stephan 798 fossil_free(zReportErr);
b18c06e… stephan 799 }
c717f1e… drh 800 }else if(fAuthSchema){
b18c06e… stephan 801 ticket_unrestrict_sql();
b18c06e… stephan 802 if(nSchemaErr){
b18c06e… stephan 803 fossil_warning("Ticket schema authorizer error count: %d\n",
b18c06e… stephan 804 nSchemaErr);
b18c06e… stephan 805 }
c717f1e… drh 806 }
a81a47f… drh 807 }
a81a47f… drh 808
a81a47f… drh 809 /*
a81a47f… drh 810 ** Print the output of one or more SQL queries on standard output.
a81a47f… drh 811 ** This routine is used for debugging purposes only.
a81a47f… drh 812 */
a81a47f… drh 813 int db_debug(const char *zSql, ...){
f97e1cf… drh 814 Blob sql;
f97e1cf… drh 815 int rc = SQLITE_OK;
f97e1cf… drh 816 va_list ap;
f97e1cf… drh 817 const char *z, *zEnd;
f97e1cf… drh 818 sqlite3_stmt *pStmt;
f97e1cf… drh 819 blob_init(&sql, 0, 0);
f97e1cf… drh 820 va_start(ap, zSql);
f97e1cf… drh 821 blob_vappendf(&sql, zSql, ap);
f97e1cf… drh 822 va_end(ap);
f97e1cf… drh 823 z = blob_str(&sql);
f97e1cf… drh 824 while( rc==SQLITE_OK && z[0] ){
f97e1cf… drh 825 pStmt = 0;
f97e1cf… drh 826 rc = sqlite3_prepare_v2(g.db, z, -1, &pStmt, &zEnd);
f97e1cf… drh 827 if( rc!=SQLITE_OK ) break;
f97e1cf… drh 828 if( pStmt ){
a81a47f… drh 829 int nRow = 0;
a81a47f… drh 830 db.nPrepare++;
a81a47f… drh 831 while( sqlite3_step(pStmt)==SQLITE_ROW ){
a81a47f… drh 832 int i, n;
a81a47f… drh 833 if( nRow++ > 0 ) fossil_print("\n");
a81a47f… drh 834 n = sqlite3_column_count(pStmt);
a81a47f… drh 835 for(i=0; i<n; i++){
a81a47f… drh 836 fossil_print("%s = %s\n", sqlite3_column_name(pStmt, i),
a81a47f… drh 837 sqlite3_column_text(pStmt,i));
a81a47f… drh 838 }
a81a47f… drh 839 }
a81a47f… drh 840 rc = sqlite3_finalize(pStmt);
a81a47f… drh 841 if( rc ) db_err("%s: {%.*s}", sqlite3_errmsg(g.db), (int)(zEnd-z), z);
a81a47f… drh 842 }
a81a47f… drh 843 z = zEnd;
a81a47f… drh 844 }
a81a47f… drh 845 blob_reset(&sql);
a81a47f… drh 846 return rc;
a81a47f… drh 847 }
a81a47f… drh 848
a81a47f… drh 849 /*
0ea56bb… drh 850 ** Execute multiple SQL statements. The input text is executed
0ea56bb… drh 851 ** directly without any formatting.
a81a47f… drh 852 */
0ea56bb… drh 853 int db_exec_sql(const char *z){
a81a47f… drh 854 int rc = SQLITE_OK;
a81a47f… drh 855 sqlite3_stmt *pStmt;
0ea56bb… drh 856 const char *zEnd;
a81a47f… drh 857 while( rc==SQLITE_OK && z[0] ){
a81a47f… drh 858 pStmt = 0;
a81a47f… drh 859 rc = sqlite3_prepare_v2(g.db, z, -1, &pStmt, &zEnd);
a00a140… drh 860 if( rc ){
a00a140… drh 861 db_err("%s: {%s}", sqlite3_errmsg(g.db), z);
a00a140… drh 862 }else if( pStmt ){
f97e1cf… drh 863 db.nPrepare++;
ebd239d… drh 864 db_append_dml(sqlite3_sql(pStmt));
f97e1cf… drh 865 while( sqlite3_step(pStmt)==SQLITE_ROW ){}
f97e1cf… drh 866 rc = sqlite3_finalize(pStmt);
f97e1cf… drh 867 if( rc ) db_err("%s: {%.*s}", sqlite3_errmsg(g.db), (int)(zEnd-z), z);
f97e1cf… drh 868 }
f97e1cf… drh 869 z = zEnd;
f97e1cf… drh 870 }
0ea56bb… drh 871 return rc;
0ea56bb… drh 872 }
0ea56bb… drh 873
0ea56bb… drh 874 /*
0ea56bb… drh 875 ** Execute multiple SQL statements using printf-style formatting.
0ea56bb… drh 876 */
0ea56bb… drh 877 int db_multi_exec(const char *zSql, ...){
0ea56bb… drh 878 Blob sql;
0ea56bb… drh 879 int rc;
0ea56bb… drh 880 va_list ap;
0ea56bb… drh 881
0ea56bb… drh 882 blob_init(&sql, 0, 0);
0ea56bb… drh 883 va_start(ap, zSql);
0ea56bb… drh 884 blob_vappendf(&sql, zSql, ap);
0ea56bb… drh 885 va_end(ap);
0ea56bb… drh 886 rc = db_exec_sql(blob_str(&sql));
e604d48… drh 887 }
e604d48… drh 888
e604d48… drh 889 /*
e604d48… drh 890 ** Optionally make the following changes to the database if feasible and
e604d48… drh 891 ** convenient. Do not start a transaction for these changes, but only
e604d48… drh 892 ** make these changes if other changes are also being made.
e604d48… drh 893 */
0205148… drh 894 void db_optional_sql(const char *zDb, const char *zSql, ...){
0205148… drh 895 if( db_is_writeable(zDb) && db.nBeforeCommit < count(db.azBeforeCommit) ){
e604d48… drh 896 va_list ap;
e604d48… drh 897 va_start(ap, zSql);
e604d48… drh 898 db.azBeforeCommit[db.nBeforeCommit++] = sqlite3_vmprintf(zSql, ap);
e604d48… drh 899 va_end(ap);
e604d48… drh 900 }
f1efc90… drh 901 db_vprepare(&s, 0, zSql, ap);
f1efc90… drh 902 db_vprepare(&s, 0, zSql, ap);
f1efc90… drh 903 db_vprepare(&s, 0, zSql, ap);
f1efc90… drh 904 db_vprepare(&s, 0, zSql, ap);
f1efc90… drh 905 db_vprepare(&s, 0, zSql, ap);
6ce705b… drh 906 ** obtained from fossil_strdup() and should be freed using fossil_free().
6ce705b… drh 907 ** If the result set is empty, return a copy of zDefault instead.
4e18dba… jan.nijtmans 908 char *db_text(const char *zDefault, const char *zSql, ...){
7c2577b… drh 909 char *z;
f1efc90… drh 910 db_vprepare(&s, 0, zSql, ap);
0c9dff6… drh 911 z = fossil_strdup_nn((const char*)sqlite3_column_text(s.pStmt, 0));
7c2577b… drh 912 }else{
0c9dff6… drh 913 z = fossil_strdup(zDefault);
0ac64da… drh 914 **
0ac64da… drh 915 ** If zFilename is NULL, then create an empty repository in an in-memory
0ac64da… drh 916 ** database.
f741baa… drh 917 sqlite3 *xdb;
f741baa… drh 918 xdb = db_open(zFileName ? zFileName : ":memory:");
f741baa… drh 919 sqlite3_exec(xdb, "BEGIN EXCLUSIVE", 0, 0, 0);
f741baa… drh 920 rc = sqlite3_exec(xdb, zSchema, 0, 0, 0);
7536c6a… drh 921 if( rc!=SQLITE_OK ){
f741baa… drh 922 db_err("%s", sqlite3_errmsg(xdb));
f741baa… drh 923 rc = sqlite3_exec(xdb, zSql, 0, 0, 0);
f741baa… drh 924 db_err("%s", sqlite3_errmsg(xdb));
f741baa… drh 925 sqlite3_exec(xdb, "COMMIT", 0, 0, 0);
0ac64da… drh 926 if( zFileName || g.db!=0 ){
f741baa… drh 927 sqlite3_close(xdb);
0ac64da… drh 928 }else{
f741baa… drh 929 g.db = xdb;
0ac64da… drh 930 }
1654456… drh 931 }
1654456… drh 932
1654456… drh 933 /*
1654456… drh 934 ** Function to return the number of seconds since 1970. This is
1654456… drh 935 ** the same as strftime('%s','now') but is more compact.
1654456… drh 936 */
74ecc4d… drh 937 void db_now_function(
1654456… drh 938 sqlite3_context *context,
1654456… drh 939 int argc,
1654456… drh 940 sqlite3_value **argv
1654456… drh 941 ){
1654456… drh 942 sqlite3_result_int64(context, time(0));
1654456… drh 943 }
1654456… drh 944
9ba8a39… drh 945 /*
9ba8a39… drh 946 ** Function to return the check-in time for a file.
3b2dcd9… drh 947 **
3b2dcd9… drh 948 ** checkin_mtime(CKINID,RID)
3b2dcd9… drh 949 **
3b2dcd9… drh 950 ** CKINID: The RID for the manifest for a check-in.
3b2dcd9… drh 951 ** RID: The RID of a file in CKINID for which the check-in time
3b2dcd9… drh 952 ** is desired.
3b2dcd9… drh 953 **
3b2dcd9… drh 954 ** Returns: The check-in time in seconds since 1970.
9ba8a39… drh 955 */
9ba8a39… drh 956 void db_checkin_mtime_function(
9ba8a39… drh 957 sqlite3_context *context,
9ba8a39… drh 958 int argc,
9ba8a39… drh 959 sqlite3_value **argv
9ba8a39… drh 960 ){
9ba8a39… drh 961 i64 mtime;
9ba8a39… drh 962 int rc = mtime_of_manifest_file(sqlite3_value_int(argv[0]),
9ba8a39… drh 963 sqlite3_value_int(argv[1]), &mtime);
9ba8a39… drh 964 if( rc==0 ){
9ba8a39… drh 965 sqlite3_result_int64(context, mtime);
9ba8a39… drh 966 }
9ba8a39… drh 967 }
9ba8a39… drh 968
ba1429c… drh 969 /*
ba1429c… drh 970 ** SQL wrapper around the symbolic_name_to_rid() C-language API.
ba1429c… drh 971 ** Examples:
ba1429c… drh 972 **
ba1429c… drh 973 ** symbolic_name_to_rid('trunk');
ba1429c… drh 974 ** symbolic_name_to_rid('trunk','w');
ba1429c… drh 975 **
ba1429c… drh 976 */
8815b65… stephan 977 void db_sym2rid_function(
8815b65… stephan 978 sqlite3_context *context,
8815b65… stephan 979 int argc,
8815b65… stephan 980 sqlite3_value **argv
8815b65… stephan 981 ){
5330d10… jan.nijtmans 982 const char *arg;
5330d10… jan.nijtmans 983 const char *type;
8815b65… stephan 984 if(1 != argc && 2 != argc){
8815b65… stephan 985 sqlite3_result_error(context, "Expecting one or two arguments", -1);
8815b65… stephan 986 return;
8815b65… stephan 987 }
8815b65… stephan 988 arg = (const char*)sqlite3_value_text(argv[0]);
8815b65… stephan 989 if(!arg){
8815b65… stephan 990 sqlite3_result_error(context, "Expecting a STRING argument", -1);
8815b65… stephan 991 }else{
8815b65… stephan 992 int rid;
a81a47f… drh 993 type = (2==argc) ? (const char*)sqlite3_value_text(argv[1]) : 0;
8815b65… stephan 994 if(!type) type = "ci";
8815b65… stephan 995 rid = symbolic_name_to_rid( arg, type );
8815b65… stephan 996 if(rid<0){
8815b65… stephan 997 sqlite3_result_error(context, "Symbolic name is ambiguous.", -1);
8815b65… stephan 998 }else if(0==rid){
8815b65… stephan 999 sqlite3_result_null(context);
8815b65… stephan 1000 }else{
8815b65… stephan 1001 sqlite3_result_int64(context, rid);
8815b65… stephan 1002 }
8815b65… stephan 1003 }
8815b65… stephan 1004 }
8815b65… stephan 1005
b1bb31e… drh 1006
b1bb31e… drh 1007 /*
b1bb31e… drh 1008 ** SETTING: timeline-utc boolean default=on
b1bb31e… drh 1009 **
b1bb31e… drh 1010 ** If the timeline-utc setting is true, then Fossil tries to understand
b1bb31e… drh 1011 ** and display all time values using UTC. If this setting is false, Fossil
b1bb31e… drh 1012 ** tries to understand and display time values using the local timezone.
b1bb31e… drh 1013 **
b1bb31e… drh 1014 ** The word "timeline" in the name of this setting is historical.
b1bb31e… drh 1015 ** This setting applies to all user interfaces of Fossil,
b1bb31e… drh 1016 ** not just the timeline.
b1bb31e… drh 1017 **
b1bb31e… drh 1018 ** Note that when accessing Fossil using the web interface, the localtime
b1bb31e… drh 1019 ** used is the localtime on the server, not on the client.
b1bb31e… drh 1020 */
b1bb31e… drh 1021 /*
b1bb31e… drh 1022 ** Return true if Fossil is set to display times as UTC. Return false
b1bb31e… drh 1023 ** if it wants to display times using the local timezone.
b1bb31e… drh 1024 **
b1bb31e… drh 1025 ** False is returned if display is set to localtime even if the localtime
b1bb31e… drh 1026 ** happens to be the same as UTC.
b1bb31e… drh 1027 */
b1bb31e… drh 1028 int fossil_ui_utctime(void){
b1bb31e… drh 1029 if( g.fTimeFormat==0 ){
b1bb31e… drh 1030 if( db_get_int("timeline-utc", 1) ){
b1bb31e… drh 1031 g.fTimeFormat = 1; /* UTC */
b1bb31e… drh 1032 }else{
b1bb31e… drh 1033 g.fTimeFormat = 2; /* Localtime */
b1bb31e… drh 1034 }
b1bb31e… drh 1035 }
b1bb31e… drh 1036 return g.fTimeFormat==1;
b1bb31e… drh 1037 }
b1bb31e… drh 1038
b1bb31e… drh 1039 /*
b1bb31e… drh 1040 ** Return true if Fossil is set to display times using the local timezone.
b1bb31e… drh 1041 */
36eb0b4… drh 1042 int fossil_ui_localtime(void){
b1bb31e… drh 1043 return fossil_ui_utctime()==0;
b1bb31e… drh 1044 }
b1bb31e… drh 1045
ea63a2d… drh 1046 /*
ea63a2d… drh 1047 ** The toLocal() SQL function returns a string that is an argument to a
ea63a2d… drh 1048 ** date/time function that is appropriate for modifying the time for display.
ea63a2d… drh 1049 ** If UTC time display is selected, no modification occurs. If local time
ea63a2d… drh 1050 ** display is selected, the time is adjusted appropriately.
ea63a2d… drh 1051 **
ea63a2d… drh 1052 ** Example usage:
ea63a2d… drh 1053 **
ea63a2d… drh 1054 ** SELECT datetime('now',toLocal());
ea63a2d… drh 1055 */
ea63a2d… drh 1056 void db_tolocal_function(
ea63a2d… drh 1057 sqlite3_context *context,
ea63a2d… drh 1058 int argc,
ea63a2d… drh 1059 sqlite3_value **argv
ea63a2d… drh 1060 ){
b1bb31e… drh 1061 if( fossil_ui_utctime() ){
ea63a2d… drh 1062 sqlite3_result_text(context, "0 seconds", -1, SQLITE_STATIC);
ea63a2d… drh 1063 }else{
ea63a2d… drh 1064 sqlite3_result_text(context, "localtime", -1, SQLITE_STATIC);
ea63a2d… drh 1065 }
ea63a2d… drh 1066 }
ea63a2d… drh 1067
ea63a2d… drh 1068 /*
ea63a2d… drh 1069 ** The fromLocal() SQL function returns a string that is an argument to a
ea63a2d… drh 1070 ** date/time function that is appropriate to convert an input time to UTC.
ea63a2d… drh 1071 ** If UTC time display is selected, no modification occurs. If local time
ea63a2d… drh 1072 ** display is selected, the time is adjusted from local to UTC.
ea63a2d… drh 1073 **
ea63a2d… drh 1074 ** Example usage:
ea63a2d… drh 1075 **
ea63a2d… drh 1076 ** SELECT julianday(:user_input,fromLocal());
ea63a2d… drh 1077 */
ea63a2d… drh 1078 void db_fromlocal_function(
ea63a2d… drh 1079 sqlite3_context *context,
ea63a2d… drh 1080 int argc,
ea63a2d… drh 1081 sqlite3_value **argv
ea63a2d… drh 1082 ){
b1bb31e… drh 1083 if( fossil_ui_utctime() ){
ea63a2d… drh 1084 sqlite3_result_text(context, "0 seconds", -1, SQLITE_STATIC);
ea63a2d… drh 1085 }else{
ea63a2d… drh 1086 sqlite3_result_text(context, "utc", -1, SQLITE_STATIC);
ea63a2d… drh 1087 }
b77f1aa… drh 1088 }
b77f1aa… drh 1089
b77f1aa… drh 1090 /*
b77f1aa… drh 1091 ** If the input is a hexadecimal string, convert that string into a BLOB.
b77f1aa… drh 1092 ** If the input is not a hexadecimal string, return NULL.
b77f1aa… drh 1093 */
b77f1aa… drh 1094 void db_hextoblob(
b77f1aa… drh 1095 sqlite3_context *context,
b77f1aa… drh 1096 int argc,
b77f1aa… drh 1097 sqlite3_value **argv
b77f1aa… drh 1098 ){
b77f1aa… drh 1099 const unsigned char *zIn = sqlite3_value_text(argv[0]);
b77f1aa… drh 1100 int nIn = sqlite3_value_bytes(argv[0]);
b77f1aa… drh 1101 unsigned char *zOut;
b77f1aa… drh 1102 if( zIn==0 ) return;
b77f1aa… drh 1103 if( nIn&1 ) return;
b77f1aa… drh 1104 if( !validate16((const char*)zIn, nIn) ) return;
5519c6b… drh 1105 zOut = sqlite3_malloc64( nIn/2 + 1 );
b77f1aa… drh 1106 if( zOut==0 ){
b77f1aa… drh 1107 sqlite3_result_error_nomem(context);
b77f1aa… drh 1108 return;
b77f1aa… drh 1109 }
b77f1aa… drh 1110 decode16(zIn, zOut, nIn);
b77f1aa… drh 1111 sqlite3_result_blob(context, zOut, nIn/2, sqlite3_free);
b77f1aa… drh 1112 }
b77f1aa… drh 1113
b77f1aa… drh 1114 /*
f7e572b… drh 1115 ** Return the XOR-obscured version of the input text. Useful for
f7e572b… drh 1116 ** updating authentication strings in Fossil settings. To change
f7e572b… drh 1117 ** the password locally stored for sync, for instance:
f7e572b… drh 1118 **
f7e572b… drh 1119 ** echo "UPDATE config
f7e572b… drh 1120 ** SET value = obscure('monkey123')
f7e572b… drh 1121 ** WHERE name = 'last-sync-pw'" |
f7e572b… drh 1122 ** fossil sql
f7e572b… drh 1123 **
f7e572b… drh 1124 ** Note that user.pw uses a different obscuration algorithm, but
f7e572b… drh 1125 ** you don't need to use 'fossil sql' for that anyway. Just call
f7e572b… drh 1126 **
f7e572b… drh 1127 ** fossil user pass monkey123
f7e572b… drh 1128 **
f7e572b… drh 1129 ** to change the local user entry's password in the same way.
aa1a0b3… drh 1130 **
aa1a0b3… drh 1131 ** 2022-12-30: If the user-data pointer is not NULL, then operate
aa1a0b3… drh 1132 ** as unobscure() rather than obscure(). The obscure() variant of
aa1a0b3… drh 1133 ** this routine is commonly available. But unobscure is (currently)
aa1a0b3… drh 1134 ** only registered by the "fossil remote config-data --show-passwords"
aa1a0b3… drh 1135 ** command.
f7e572b… drh 1136 */
f7e572b… drh 1137 void db_obscure(
f7e572b… drh 1138 sqlite3_context *context,
f7e572b… drh 1139 int argc,
f7e572b… drh 1140 sqlite3_value **argv
f7e572b… drh 1141 ){
f7e572b… drh 1142 const unsigned char *zIn = sqlite3_value_text(argv[0]);
f7e572b… drh 1143 int nIn = sqlite3_value_bytes(argv[0]);
f7e572b… drh 1144 char *zOut, *zTemp;
f7e572b… drh 1145 if( 0==zIn ) return;
f7e572b… drh 1146 if( 0==(zOut = sqlite3_malloc64( nIn * 2 + 3 )) ){
f7e572b… drh 1147 sqlite3_result_error_nomem(context);
f7e572b… drh 1148 return;
f7e572b… drh 1149 }
41ba6ea… drh 1150 if( sqlite3_user_data(context)==0 ){
41ba6ea… drh 1151 zTemp = obscure((char*)zIn);
41ba6ea… drh 1152 }else{
41ba6ea… drh 1153 zTemp = unobscure((char*)zIn);
41ba6ea… drh 1154 }
c0b9b44… stephan 1155 fossil_strcpy(zOut, zTemp);
f7e572b… drh 1156 fossil_free(zTemp);
f7e572b… drh 1157 sqlite3_result_text(context, zOut, strlen(zOut), sqlite3_free);
f7e572b… drh 1158 }
f741baa… drh 1159
f741baa… drh 1160 /*
f741baa… drh 1161 ** Return True if zName is a protected (a.k.a. "sensitive") setting.
f741baa… drh 1162 */
f741baa… drh 1163 int db_setting_is_protected(const char *zName){
f741baa… drh 1164 const Setting *pSetting = zName ? db_find_setting(zName,0) : 0;
f741baa… drh 1165 return pSetting!=0 && pSetting->sensitive!=0;
f741baa… drh 1166 }
f741baa… drh 1167
f741baa… drh 1168 /*
f741baa… drh 1169 ** Implement the protected_setting(X) SQL function. This function returns
f741baa… drh 1170 ** true if X is the name of a protected (security-sensitive) setting and
f741baa… drh 1171 ** the db.protectSensitive flag is enabled. It returns false otherwise.
f741baa… drh 1172 */
f741baa… drh 1173 LOCAL void db_protected_setting_func(
f741baa… drh 1174 sqlite3_context *context,
f741baa… drh 1175 int argc,
f741baa… drh 1176 sqlite3_value **argv
f741baa… drh 1177 ){
f741baa… drh 1178 const char *zSetting;
f741baa… drh 1179 if( (db.protectMask & PROTECT_SENSITIVE)==0 ){
f741baa… drh 1180 sqlite3_result_int(context, 0);
f741baa… drh 1181 return;
f741baa… drh 1182 }
f741baa… drh 1183 zSetting = (const char*)sqlite3_value_text(argv[0]);
f741baa… drh 1184 sqlite3_result_int(context, db_setting_is_protected(zSetting));
f741baa… drh 1185 }
f741baa… drh 1186
f741baa… drh 1187 /*
59dfca5… drh 1188 ** Copied from SQLite ext/misc/uint.c...
59dfca5… drh 1189 **
59dfca5… drh 1190 ** Compare text in lexicographic order, except strings of digits
59dfca5… drh 1191 ** compare in numeric order.
59dfca5… drh 1192 **
59dfca5… drh 1193 ** This version modified to also ignore case.
59dfca5… drh 1194 */
59dfca5… drh 1195 static int uintNocaseCollFunc(
59dfca5… drh 1196 void *notUsed,
59dfca5… drh 1197 int nKey1, const void *pKey1,
59dfca5… drh 1198 int nKey2, const void *pKey2
59dfca5… drh 1199 ){
59dfca5… drh 1200 const unsigned char *zA = (const unsigned char*)pKey1;
59dfca5… drh 1201 const unsigned char *zB = (const unsigned char*)pKey2;
59dfca5… drh 1202 int i=0, j=0, x;
59dfca5… drh 1203 (void)notUsed;
59dfca5… drh 1204 while( i<nKey1 && j<nKey2 ){
b39475b… drh 1205 if( fossil_isdigit(zA[i]) && fossil_isdigit(zB[j]) ){
59dfca5… drh 1206 int k;
59dfca5… drh 1207 while( i<nKey1 && zA[i]=='0' ){ i++; }
59dfca5… drh 1208 while( j<nKey2 && zB[j]=='0' ){ j++; }
59dfca5… drh 1209 k = 0;
59dfca5… drh 1210 while( i+k<nKey1 && fossil_isdigit(zA[i+k])
59dfca5… drh 1211 && j+k<nKey2 && fossil_isdigit(zB[j+k]) ){
59dfca5… drh 1212 k++;
59dfca5… drh 1213 }
59dfca5… drh 1214 if( i+k<nKey1 && fossil_isdigit(zA[i+k]) ){
59dfca5… drh 1215 return +1;
59dfca5… drh 1216 }else if( j+k<nKey2 && fossil_isdigit(zB[j+k]) ){
59dfca5… drh 1217 return -1;
59dfca5… drh 1218 }else{
59dfca5… drh 1219 x = memcmp(zA+i, zB+j, k);
59dfca5… drh 1220 if( x ) return x;
59dfca5… drh 1221 i += k;
59dfca5… drh 1222 j += k;
59dfca5… drh 1223 }
59dfca5… drh 1224 }else
59dfca5… drh 1225 if( zA[i]!=zB[j]
59dfca5… drh 1226 && (x = fossil_tolower(zA[i]) - fossil_tolower(zB[j]))!=0
59dfca5… drh 1227 ){
59dfca5… drh 1228 return x;
59dfca5… drh 1229 }else{
59dfca5… drh 1230 i++;
59dfca5… drh 1231 j++;
59dfca5… drh 1232 }
59dfca5… drh 1233 }
59dfca5… drh 1234 return (nKey1 - i) - (nKey2 - j);
59dfca5… drh 1235 }
59dfca5… drh 1236
59dfca5… drh 1237
59dfca5… drh 1238 /*
9e318f6… jan.nijtmans 1239 ** Register the SQL functions that are useful both to the internal
ba1429c… drh 1240 ** representation and to the "fossil sql" command.
ba1429c… drh 1241 */
ba1429c… drh 1242 void db_add_aux_functions(sqlite3 *db){
59dfca5… drh 1243 sqlite3_create_collation(db, "uintnocase", SQLITE_UTF8,0,uintNocaseCollFunc);
bd7f5c0… drh 1244 sqlite3_create_function(db, "checkin_mtime", 2,
bd7f5c0… drh 1245 SQLITE_UTF8|SQLITE_DETERMINISTIC|SQLITE_INNOCUOUS,
bd7f5c0… drh 1246 0, db_checkin_mtime_function, 0, 0);
bd7f5c0… drh 1247 sqlite3_create_function(db, "symbolic_name_to_rid", 1,
bd7f5c0… drh 1248 SQLITE_UTF8|SQLITE_DETERMINISTIC|SQLITE_INNOCUOUS,
bd7f5c0… drh 1249 0, db_sym2rid_function, 0, 0);
bd7f5c0… drh 1250 sqlite3_create_function(db, "symbolic_name_to_rid", 2,
bd7f5c0… drh 1251 SQLITE_UTF8|SQLITE_DETERMINISTIC|SQLITE_INNOCUOUS,
bd7f5c0… drh 1252 0, db_sym2rid_function, 0, 0);
bd7f5c0… drh 1253 sqlite3_create_function(db, "now", 0,
bd7f5c0… drh 1254 SQLITE_UTF8|SQLITE_INNOCUOUS, 0,
ea63a2d… drh 1255 db_now_function, 0, 0);
bd7f5c0… drh 1256 sqlite3_create_function(db, "toLocal", 0,
bd7f5c0… drh 1257 SQLITE_UTF8|SQLITE_DETERMINISTIC|SQLITE_INNOCUOUS,
bd7f5c0… drh 1258 0, db_tolocal_function, 0, 0);
bd7f5c0… drh 1259 sqlite3_create_function(db, "fromLocal", 0,
bd7f5c0… drh 1260 SQLITE_UTF8|SQLITE_DETERMINISTIC|SQLITE_INNOCUOUS,
bd7f5c0… drh 1261 0, db_fromlocal_function, 0, 0);
bd7f5c0… drh 1262 sqlite3_create_function(db, "hextoblob", 1,
bd7f5c0… drh 1263 SQLITE_UTF8|SQLITE_DETERMINISTIC|SQLITE_INNOCUOUS,
bd7f5c0… drh 1264 0, db_hextoblob, 0, 0);
99fcc43… drh 1265 sqlite3_create_function(db, "capunion", 1, SQLITE_UTF8, 0,
99fcc43… drh 1266 0, capability_union_step, capability_union_finalize);
99fcc43… drh 1267 sqlite3_create_function(db, "fullcap", 1, SQLITE_UTF8, 0,
99fcc43… drh 1268 capability_fullcap, 0, 0);
8a20d41… drh 1269 sqlite3_create_function(db, "find_emailaddr", 1, SQLITE_UTF8, 0,
fc5c7d2… drh 1270 alert_find_emailaddr_func, 0, 0);
2e71dc2… drh 1271 sqlite3_create_function(db, "display_name", 1, SQLITE_UTF8, 0,
2e71dc2… drh 1272 alert_display_name_func, 0, 0);
f7e572b… drh 1273 sqlite3_create_function(db, "obscure", 1, SQLITE_UTF8, 0,
f7e572b… drh 1274 db_obscure, 0, 0);
f741baa… drh 1275 sqlite3_create_function(db, "protected_setting", 1, SQLITE_UTF8, 0,
f741baa… drh 1276 db_protected_setting_func, 0, 0);
d0a8582… stephan 1277 sqlite3_create_function(db, "win_reserved", 1, SQLITE_UTF8, 0,
f0cdbd8… drh 1278 db_win_reserved_func,0,0);
f0cdbd8… drh 1279 sqlite3_create_function(db, "url_nouser", 1, SQLITE_UTF8, 0,
f0cdbd8… drh 1280 url_nouser_func,0,0);
974cf36… drh 1281 sqlite3_create_function(db, "chat_msg_from_event", 4,
275da70… danield 1282 SQLITE_UTF8 | SQLITE_INNOCUOUS, 0,
974cf36… drh 1283 chat_msg_from_event, 0, 0);
106de27… drh 1284 sqlite3_create_function(db, "inode", 1, SQLITE_UTF8, 0,
106de27… drh 1285 file_inode_sql_func,0,0);
1e2d602… stephan 1286 sqlite3_create_function(db, "artifact_to_json", 1, SQLITE_UTF8, 0,
1e2d602… stephan 1287 artifact_to_json_sql_func,0,0);
275da70… danield 1288
a8484dc… mistachkin 1289 }
a8484dc… mistachkin 1290
a8484dc… mistachkin 1291 #if USE_SEE
a8484dc… mistachkin 1292 /*
a8484dc… mistachkin 1293 ** This is a pointer to the saved database encryption key string.
a8484dc… mistachkin 1294 */
a8484dc… mistachkin 1295 static char *zSavedKey = 0;
a8484dc… mistachkin 1296
a8484dc… mistachkin 1297 /*
a8484dc… mistachkin 1298 ** This is the size of the saved database encryption key, in bytes.
a8484dc… mistachkin 1299 */
f41cf03… mistachkin 1300 static size_t savedKeySize = 0;
f41cf03… mistachkin 1301
f41cf03… mistachkin 1302 /*
f41cf03… mistachkin 1303 ** This function returns non-zero if there is a saved database encryption
f41cf03… mistachkin 1304 ** key available.
f41cf03… mistachkin 1305 */
f41cf03… mistachkin 1306 int db_have_saved_encryption_key(){
f41cf03… mistachkin 1307 return db_is_valid_saved_encryption_key(zSavedKey, savedKeySize);
f41cf03… mistachkin 1308 }
f41cf03… mistachkin 1309
f41cf03… mistachkin 1310 /*
f41cf03… mistachkin 1311 ** This function returns non-zero if the specified database encryption key
f41cf03… mistachkin 1312 ** is valid.
f41cf03… mistachkin 1313 */
f41cf03… mistachkin 1314 int db_is_valid_saved_encryption_key(const char *p, size_t n){
f41cf03… mistachkin 1315 if( p==0 ) return 0;
f41cf03… mistachkin 1316 if( n==0 ) return 0;
f41cf03… mistachkin 1317 if( p[0]==0 ) return 0;
f41cf03… mistachkin 1318 return 1;
f41cf03… mistachkin 1319 }
a8484dc… mistachkin 1320
a8484dc… mistachkin 1321 /*
a8484dc… mistachkin 1322 ** This function returns the saved database encryption key -OR- zero if
a8484dc… mistachkin 1323 ** no database encryption key is saved.
a8484dc… mistachkin 1324 */
7aeeb30… mistachkin 1325 char *db_get_saved_encryption_key(){
a8484dc… mistachkin 1326 return zSavedKey;
a8484dc… mistachkin 1327 }
a8484dc… mistachkin 1328
a8484dc… mistachkin 1329 /*
7aeeb30… mistachkin 1330 ** This function returns the size of the saved database encryption key
7aeeb30… mistachkin 1331 ** -OR- zero if no database encryption key is saved.
7aeeb30… mistachkin 1332 */
7aeeb30… mistachkin 1333 size_t db_get_saved_encryption_key_size(){
7aeeb30… mistachkin 1334 return savedKeySize;
7aeeb30… mistachkin 1335 }
7aeeb30… mistachkin 1336
7aeeb30… mistachkin 1337 /*
f41cf03… mistachkin 1338 ** This function arranges for the saved database encryption key buffer
f41cf03… mistachkin 1339 ** to be allocated and then sets up the environment variable to allow
f41cf03… mistachkin 1340 ** a child process to initialize it with the actual database encryption
f41cf03… mistachkin 1341 ** key.
f41cf03… mistachkin 1342 */
f41cf03… mistachkin 1343 void db_setup_for_saved_encryption_key(){
f41cf03… mistachkin 1344 void *p = NULL;
f41cf03… mistachkin 1345 size_t n = 0;
f41cf03… mistachkin 1346 size_t pageSize = 0;
f41cf03… mistachkin 1347 Blob pidKey;
f41cf03… mistachkin 1348
f41cf03… mistachkin 1349 assert( !db_have_saved_encryption_key() );
f41cf03… mistachkin 1350 db_unsave_encryption_key();
f41cf03… mistachkin 1351 fossil_get_page_size(&pageSize);
f41cf03… mistachkin 1352 assert( pageSize>0 );
f41cf03… mistachkin 1353 p = fossil_secure_alloc_page(&n);
f41cf03… mistachkin 1354 assert( p!=NULL );
f41cf03… mistachkin 1355 assert( n==pageSize );
f41cf03… mistachkin 1356 blob_zero(&pidKey);
f41cf03… mistachkin 1357 blob_appendf(&pidKey, "%lu:%p:%u", (unsigned long)GETPID(), p, n);
f41cf03… mistachkin 1358 fossil_setenv("FOSSIL_SEE_PID_KEY", blob_str(&pidKey));
f41cf03… mistachkin 1359 zSavedKey = p;
f41cf03… mistachkin 1360 savedKeySize = n;
f41cf03… mistachkin 1361 }
f41cf03… mistachkin 1362
f41cf03… mistachkin 1363 /*
a8484dc… mistachkin 1364 ** This function arranges for the database encryption key to be securely
e2bdc10… danield 1365 ** saved in non-pageable memory (on platforms where this is possible).
a8484dc… mistachkin 1366 */
a8484dc… mistachkin 1367 static void db_save_encryption_key(
a8484dc… mistachkin 1368 Blob *pKey
a8484dc… mistachkin 1369 ){
a8484dc… mistachkin 1370 void *p = NULL;
a8484dc… mistachkin 1371 size_t n = 0;
a8484dc… mistachkin 1372 size_t pageSize = 0;
a8484dc… mistachkin 1373 size_t blobSize = 0;
a8484dc… mistachkin 1374
f41cf03… mistachkin 1375 assert( !db_have_saved_encryption_key() );
a8484dc… mistachkin 1376 blobSize = blob_size(pKey);
a8484dc… mistachkin 1377 if( blobSize==0 ) return;
a8484dc… mistachkin 1378 fossil_get_page_size(&pageSize);
a8484dc… mistachkin 1379 assert( pageSize>0 );
a8484dc… mistachkin 1380 if( blobSize>pageSize ){
3f5ab71… drh 1381 fossil_panic("key blob too large: %u versus %u", blobSize, pageSize);
a8484dc… mistachkin 1382 }
a8484dc… mistachkin 1383 p = fossil_secure_alloc_page(&n);
a8484dc… mistachkin 1384 assert( p!=NULL );
a8484dc… mistachkin 1385 assert( n==pageSize );
a8484dc… mistachkin 1386 assert( n>=blobSize );
a8484dc… mistachkin 1387 memcpy(p, blob_str(pKey), blobSize);
a8484dc… mistachkin 1388 zSavedKey = p;
a8484dc… mistachkin 1389 savedKeySize = n;
a8484dc… mistachkin 1390 }
a8484dc… mistachkin 1391
a8484dc… mistachkin 1392 /*
a8484dc… mistachkin 1393 ** This function arranges for the saved database encryption key to be
a8484dc… mistachkin 1394 ** securely zeroed, unlocked (if necessary), and freed.
a8484dc… mistachkin 1395 */
a8484dc… mistachkin 1396 void db_unsave_encryption_key(){
a8484dc… mistachkin 1397 fossil_secure_free_page(zSavedKey, savedKeySize);
a8484dc… mistachkin 1398 zSavedKey = NULL;
a8484dc… mistachkin 1399 savedKeySize = 0;
a8484dc… mistachkin 1400 }
a8484dc… mistachkin 1401
a8484dc… mistachkin 1402 /*
a8484dc… mistachkin 1403 ** This function sets the saved database encryption key to the specified
a8484dc… mistachkin 1404 ** string value, allocating or freeing the underlying memory if needed.
a8484dc… mistachkin 1405 */
f41cf03… mistachkin 1406 static void db_set_saved_encryption_key(
a8484dc… mistachkin 1407 Blob *pKey
a8484dc… mistachkin 1408 ){
a8484dc… mistachkin 1409 if( zSavedKey!=NULL ){
a8484dc… mistachkin 1410 size_t blobSize = blob_size(pKey);
a8484dc… mistachkin 1411 if( blobSize==0 ){
a8484dc… mistachkin 1412 db_unsave_encryption_key();
a8484dc… mistachkin 1413 }else{
a8484dc… mistachkin 1414 if( blobSize>savedKeySize ){
3f5ab71… drh 1415 fossil_panic("key blob too large: %u versus %u",
a8484dc… mistachkin 1416 blobSize, savedKeySize);
a8484dc… mistachkin 1417 }
a8484dc… mistachkin 1418 fossil_secure_zero(zSavedKey, savedKeySize);
a8484dc… mistachkin 1419 memcpy(zSavedKey, blob_str(pKey), blobSize);
a8484dc… mistachkin 1420 }
a8484dc… mistachkin 1421 }else{
a8484dc… mistachkin 1422 db_save_encryption_key(pKey);
a8484dc… mistachkin 1423 }
7aeeb30… mistachkin 1424 }
7aeeb30… mistachkin 1425
f41cf03… mistachkin 1426 /*
f41cf03… mistachkin 1427 ** WEBPAGE: setseekey
f41cf03… mistachkin 1428 **
f41cf03… mistachkin 1429 ** Sets the sets the saved database encryption key to one that gets passed
f41cf03… mistachkin 1430 ** via the "key" query string parameter. If the saved database encryption
f41cf03… mistachkin 1431 ** key has already been set, does nothing. This web page does not produce
f41cf03… mistachkin 1432 ** any output on success or failure. No permissions are required and none
f41cf03… mistachkin 1433 ** are checked (partially due to lack of encrypted database access).
f41cf03… mistachkin 1434 **
f41cf03… mistachkin 1435 ** Query parameters:
f41cf03… mistachkin 1436 **
f41cf03… mistachkin 1437 ** key The string to set as the saved database encryption
f41cf03… mistachkin 1438 ** key.
f41cf03… mistachkin 1439 */
f41cf03… mistachkin 1440 void db_set_see_key_page(void){
f41cf03… mistachkin 1441 Blob key;
f41cf03… mistachkin 1442 const char *zKey;
f41cf03… mistachkin 1443 if( db_have_saved_encryption_key() ){
f41cf03… mistachkin 1444 fossil_trace("SEE: encryption key was already set\n");
f41cf03… mistachkin 1445 return;
f41cf03… mistachkin 1446 }
f41cf03… mistachkin 1447 zKey = P("key");
f41cf03… mistachkin 1448 blob_init(&key, 0, 0);
f41cf03… mistachkin 1449 if( zKey!=0 ){
f41cf03… mistachkin 1450 PID_T processId;
f41cf03… mistachkin 1451 blob_set(&key, zKey);
f41cf03… mistachkin 1452 db_set_saved_encryption_key(&key);
f41cf03… mistachkin 1453 processId = db_maybe_handle_saved_encryption_key_for_process(
f41cf03… mistachkin 1454 SEE_KEY_WRITE
f41cf03… mistachkin 1455 );
f41cf03… mistachkin 1456 fossil_trace("SEE: set encryption key for process %lu, length %u\n",
f41cf03… mistachkin 1457 (unsigned long)processId, blob_size(&key));
f41cf03… mistachkin 1458 }else{
f41cf03… mistachkin 1459 fossil_trace("SEE: no encryption key specified\n");
f41cf03… mistachkin 1460 }
f41cf03… mistachkin 1461 blob_reset(&key);
f41cf03… mistachkin 1462 }
f41cf03… mistachkin 1463
f41cf03… mistachkin 1464 /*
f41cf03… mistachkin 1465 ** WEBPAGE: unsetseekey
f41cf03… mistachkin 1466 **
f41cf03… mistachkin 1467 ** Sets the saved database encryption key to zeros in the current and parent
f41cf03… mistachkin 1468 ** Fossil processes. This web page does not produce any output on success
f41cf03… mistachkin 1469 ** or failure. Setup permission is required.
f41cf03… mistachkin 1470 */
f41cf03… mistachkin 1471 void db_unset_see_key_page(void){
f41cf03… mistachkin 1472 PID_T processId;
f41cf03… mistachkin 1473 login_check_credentials();
f41cf03… mistachkin 1474 if( !g.perm.Setup ){ login_needed(0); return; }
f41cf03… mistachkin 1475 processId = db_maybe_handle_saved_encryption_key_for_process(
f41cf03… mistachkin 1476 SEE_KEY_ZERO
f41cf03… mistachkin 1477 );
f41cf03… mistachkin 1478 fossil_trace("SEE: unset encryption key for process %lu\n",
f41cf03… mistachkin 1479 (unsigned long)processId);
f41cf03… mistachkin 1480 }
f41cf03… mistachkin 1481
f41cf03… mistachkin 1482 /*
f41cf03… mistachkin 1483 ** This function reads the saved database encryption key from the
f41cf03… mistachkin 1484 ** specified Fossil parent process. This is only necessary (or
f41cf03… mistachkin 1485 ** functional) on Windows or Linux.
f41cf03… mistachkin 1486 */
f41cf03… mistachkin 1487 static void db_read_saved_encryption_key_from_process(
f41cf03… mistachkin 1488 PID_T processId, /* Identifier for Fossil parent process. */
f41cf03… mistachkin 1489 LPVOID pAddress, /* Pointer to saved key buffer in the parent process. */
f41cf03… mistachkin 1490 SIZE_T nSize /* Size of saved key buffer in the parent process. */
f41cf03… mistachkin 1491 ){
f41cf03… mistachkin 1492 void *p = NULL;
f41cf03… mistachkin 1493 size_t n = 0;
f41cf03… mistachkin 1494 size_t pageSize = 0;
f41cf03… mistachkin 1495
f41cf03… mistachkin 1496 fossil_get_page_size(&pageSize);
f41cf03… mistachkin 1497 assert( pageSize>0 );
f41cf03… mistachkin 1498 if( nSize>pageSize ){
f41cf03… mistachkin 1499 fossil_panic("key too large: %u versus %u", nSize, pageSize);
f41cf03… mistachkin 1500 }
f41cf03… mistachkin 1501 p = fossil_secure_alloc_page(&n);
f41cf03… mistachkin 1502 assert( p!=NULL );
f41cf03… mistachkin 1503 assert( n==pageSize );
f41cf03… mistachkin 1504 assert( n>=nSize );
f41cf03… mistachkin 1505 {
f41cf03… mistachkin 1506 #if defined(_WIN32)
f41cf03… mistachkin 1507 HANDLE hProcess = OpenProcess(PROCESS_VM_READ, FALSE, processId);
f41cf03… mistachkin 1508 if( hProcess!=NULL ){
f41cf03… mistachkin 1509 SIZE_T nRead = 0;
f41cf03… mistachkin 1510 if( ReadProcessMemory(hProcess, pAddress, p, nSize, &nRead) ){
f41cf03… mistachkin 1511 CloseHandle(hProcess);
f41cf03… mistachkin 1512 if( nRead==nSize ){
f41cf03… mistachkin 1513 db_unsave_encryption_key();
f41cf03… mistachkin 1514 zSavedKey = p;
f41cf03… mistachkin 1515 savedKeySize = n;
f41cf03… mistachkin 1516 }else{
f41cf03… mistachkin 1517 fossil_secure_free_page(p, n);
f41cf03… mistachkin 1518 fossil_panic("bad size read, %u out of %u bytes at %p from pid %lu",
f41cf03… mistachkin 1519 nRead, nSize, pAddress, processId);
f41cf03… mistachkin 1520 }
f41cf03… mistachkin 1521 }else{
f41cf03… mistachkin 1522 CloseHandle(hProcess);
f41cf03… mistachkin 1523 fossil_secure_free_page(p, n);
f41cf03… mistachkin 1524 fossil_panic("failed read, %u bytes at %p from pid %lu: %lu", nSize,
f41cf03… mistachkin 1525 pAddress, processId, GetLastError());
f41cf03… mistachkin 1526 }
f41cf03… mistachkin 1527 }else{
f41cf03… mistachkin 1528 fossil_secure_free_page(p, n);
f41cf03… mistachkin 1529 fossil_panic("failed to open pid %lu: %lu", processId, GetLastError());
f41cf03… mistachkin 1530 }
f41cf03… mistachkin 1531 #elif defined(__linux__)
f41cf03… mistachkin 1532 ssize_t nRead;
f41cf03… mistachkin 1533 struct iovec liov = {0};
f41cf03… mistachkin 1534 struct iovec riov = {0};
f41cf03… mistachkin 1535 liov.iov_base = p;
f41cf03… mistachkin 1536 liov.iov_len = n;
f41cf03… mistachkin 1537 riov.iov_base = pAddress;
f41cf03… mistachkin 1538 riov.iov_len = nSize;
f41cf03… mistachkin 1539 nRead = process_vm_readv(processId, &liov, 1, &riov, 1, 0);
f41cf03… mistachkin 1540 if( nRead==nSize ){
f41cf03… mistachkin 1541 db_unsave_encryption_key();
f41cf03… mistachkin 1542 zSavedKey = p;
f41cf03… mistachkin 1543 savedKeySize = n;
f41cf03… mistachkin 1544 }else{
f41cf03… mistachkin 1545 fossil_secure_free_page(p, n);
f41cf03… mistachkin 1546 fossil_panic("bad size read, %zd out of %zu bytes at %p from pid %lu",
f41cf03… mistachkin 1547 nRead, nSize, pAddress, (unsigned long)processId);
f41cf03… mistachkin 1548 }
f41cf03… mistachkin 1549 #else
f41cf03… mistachkin 1550 fossil_secure_free_page(p, n);
f41cf03… mistachkin 1551 fossil_trace("db_read_saved_encryption_key_from_process unsupported");
f41cf03… mistachkin 1552 #endif
f41cf03… mistachkin 1553 }
f41cf03… mistachkin 1554 }
f41cf03… mistachkin 1555
f41cf03… mistachkin 1556 /*
f41cf03… mistachkin 1557 ** This function writes the saved database encryption key into the
f41cf03… mistachkin 1558 ** specified Fossil parent process. This is only necessary (or
f41cf03… mistachkin 1559 ** functional) on Windows or Linux.
f41cf03… mistachkin 1560 */
f41cf03… mistachkin 1561 static void db_write_saved_encryption_key_to_process(
f41cf03… mistachkin 1562 PID_T processId, /* Identifier for Fossil parent process. */
f41cf03… mistachkin 1563 LPVOID pAddress, /* Pointer to saved key buffer in the parent process. */
f41cf03… mistachkin 1564 SIZE_T nSize /* Size of saved key buffer in the parent process. */
f41cf03… mistachkin 1565 ){
f41cf03… mistachkin 1566 void *p = db_get_saved_encryption_key();
f41cf03… mistachkin 1567 size_t n = db_get_saved_encryption_key_size();
f41cf03… mistachkin 1568 size_t pageSize = 0;
f41cf03… mistachkin 1569
f41cf03… mistachkin 1570 fossil_get_page_size(&pageSize);
f41cf03… mistachkin 1571 assert( pageSize>0 );
f41cf03… mistachkin 1572 if( nSize>pageSize ){
f41cf03… mistachkin 1573 fossil_panic("key too large: %u versus %u", nSize, pageSize);
f41cf03… mistachkin 1574 }
f41cf03… mistachkin 1575 assert( p!=NULL );
f41cf03… mistachkin 1576 assert( n==pageSize );
f41cf03… mistachkin 1577 assert( n>=nSize );
f41cf03… mistachkin 1578 {
7aeeb30… mistachkin 1579 #if defined(_WIN32)
f41cf03… mistachkin 1580 HANDLE hProcess = OpenProcess(PROCESS_VM_OPERATION|PROCESS_VM_WRITE,
f41cf03… mistachkin 1581 FALSE, processId);
f41cf03… mistachkin 1582 if( hProcess!=NULL ){
f41cf03… mistachkin 1583 SIZE_T nWrite = 0;
f41cf03… mistachkin 1584 if( WriteProcessMemory(hProcess, pAddress, p, nSize, &nWrite) ){
f41cf03… mistachkin 1585 CloseHandle(hProcess);
f41cf03… mistachkin 1586 if( nWrite!=nSize ){
f41cf03… mistachkin 1587 fossil_panic("bad size write, %u out of %u bytes at %p from pid %lu",
f41cf03… mistachkin 1588 nWrite, nSize, pAddress, processId);
f41cf03… mistachkin 1589 }
f41cf03… mistachkin 1590 }else{
f41cf03… mistachkin 1591 CloseHandle(hProcess);
f41cf03… mistachkin 1592 fossil_panic("failed write, %u bytes at %p from pid %lu: %lu", nSize,
f41cf03… mistachkin 1593 pAddress, processId, GetLastError());
f41cf03… mistachkin 1594 }
f41cf03… mistachkin 1595 }else{
f41cf03… mistachkin 1596 fossil_panic("failed to open pid %lu: %lu", processId, GetLastError());
f41cf03… mistachkin 1597 }
f41cf03… mistachkin 1598 #elif defined(__linux__)
f41cf03… mistachkin 1599 ssize_t nWrite;
f41cf03… mistachkin 1600 struct iovec liov = {0};
f41cf03… mistachkin 1601 struct iovec riov = {0};
f41cf03… mistachkin 1602 liov.iov_base = p;
f41cf03… mistachkin 1603 liov.iov_len = n;
f41cf03… mistachkin 1604 riov.iov_base = pAddress;
f41cf03… mistachkin 1605 riov.iov_len = nSize;
f41cf03… mistachkin 1606 nWrite = process_vm_writev(processId, &liov, 1, &riov, 1, 0);
f41cf03… mistachkin 1607 if( nWrite!=nSize ){
f41cf03… mistachkin 1608 fossil_panic("bad size write, %zd out of %zu bytes at %p from pid %lu",
f41cf03… mistachkin 1609 nWrite, nSize, pAddress, (unsigned long)processId);
f41cf03… mistachkin 1610 }
f41cf03… mistachkin 1611 #else
f41cf03… mistachkin 1612 fossil_trace("db_write_saved_encryption_key_to_process unsupported");
f41cf03… mistachkin 1613 #endif
f41cf03… mistachkin 1614 }
f41cf03… mistachkin 1615 }
f41cf03… mistachkin 1616
7aeeb30… mistachkin 1617 /*
f41cf03… mistachkin 1618 ** This function zeros the saved database encryption key in the specified
f41cf03… mistachkin 1619 ** Fossil parent process. This is only necessary (or functional) on
f41cf03… mistachkin 1620 ** Windows or Linux.
7aeeb30… mistachkin 1621 */
f41cf03… mistachkin 1622 static void db_zero_saved_encryption_key_in_process(
f41cf03… mistachkin 1623 PID_T processId, /* Identifier for Fossil parent process. */
7aeeb30… mistachkin 1624 LPVOID pAddress, /* Pointer to saved key buffer in the parent process. */
7aeeb30… mistachkin 1625 SIZE_T nSize /* Size of saved key buffer in the parent process. */
7aeeb30… mistachkin 1626 ){
7aeeb30… mistachkin 1627 void *p = NULL;
7aeeb30… mistachkin 1628 size_t n = 0;
7aeeb30… mistachkin 1629 size_t pageSize = 0;
7aeeb30… mistachkin 1630
7aeeb30… mistachkin 1631 fossil_get_page_size(&pageSize);
7aeeb30… mistachkin 1632 assert( pageSize>0 );
7aeeb30… mistachkin 1633 if( nSize>pageSize ){
3f5ab71… drh 1634 fossil_panic("key too large: %u versus %u", nSize, pageSize);
7aeeb30… mistachkin 1635 }
7aeeb30… mistachkin 1636 p = fossil_secure_alloc_page(&n);
7aeeb30… mistachkin 1637 assert( p!=NULL );
7aeeb30… mistachkin 1638 assert( n==pageSize );
7aeeb30… mistachkin 1639 assert( n>=nSize );
f41cf03… mistachkin 1640 {
f41cf03… mistachkin 1641 #if defined(_WIN32)
f41cf03… mistachkin 1642 HANDLE hProcess = OpenProcess(PROCESS_VM_OPERATION|PROCESS_VM_WRITE,
f41cf03… mistachkin 1643 FALSE, processId);
f41cf03… mistachkin 1644 if( hProcess!=NULL ){
f41cf03… mistachkin 1645 SIZE_T nWrite = 0;
f41cf03… mistachkin 1646 if( WriteProcessMemory(hProcess, pAddress, p, nSize, &nWrite) ){
f41cf03… mistachkin 1647 CloseHandle(hProcess);
f41cf03… mistachkin 1648 fossil_secure_free_page(p, n);
f41cf03… mistachkin 1649 if( nWrite!=nSize ){
f41cf03… mistachkin 1650 fossil_panic("bad size zero, %u out of %u bytes at %p from pid %lu",
f41cf03… mistachkin 1651 nWrite, nSize, pAddress, processId);
f41cf03… mistachkin 1652 }
f41cf03… mistachkin 1653 }else{
f41cf03… mistachkin 1654 CloseHandle(hProcess);
f41cf03… mistachkin 1655 fossil_secure_free_page(p, n);
f41cf03… mistachkin 1656 fossil_panic("failed zero, %u bytes at %p from pid %lu: %lu", nSize,
f41cf03… mistachkin 1657 pAddress, processId, GetLastError());
f41cf03… mistachkin 1658 }
f41cf03… mistachkin 1659 }else{
f41cf03… mistachkin 1660 fossil_secure_free_page(p, n);
f41cf03… mistachkin 1661 fossil_panic("failed to open pid %lu: %lu", processId, GetLastError());
f41cf03… mistachkin 1662 }
f41cf03… mistachkin 1663 #elif defined(__linux__)
f41cf03… mistachkin 1664 ssize_t nWrite;
f41cf03… mistachkin 1665 struct iovec liov = {0};
f41cf03… mistachkin 1666 struct iovec riov = {0};
f41cf03… mistachkin 1667 liov.iov_base = p;
f41cf03… mistachkin 1668 liov.iov_len = n;
f41cf03… mistachkin 1669 riov.iov_base = pAddress;
f41cf03… mistachkin 1670 riov.iov_len = nSize;
f41cf03… mistachkin 1671 nWrite = process_vm_writev(processId, &liov, 1, &riov, 1, 0);
f41cf03… mistachkin 1672 if( nWrite!=nSize ){
f41cf03… mistachkin 1673 fossil_secure_free_page(p, n);
f41cf03… mistachkin 1674 fossil_panic("bad size zero, %zd out of %zu bytes at %p from pid %lu",
f41cf03… mistachkin 1675 nWrite, nSize, pAddress, (unsigned long)processId);
f41cf03… mistachkin 1676 }
f41cf03… mistachkin 1677 #else
f41cf03… mistachkin 1678 fossil_secure_free_page(p, n);
f41cf03… mistachkin 1679 fossil_trace("db_zero_saved_encryption_key_in_process unsupported");
f41cf03… mistachkin 1680 #endif
57d8a71… mistachkin 1681 }
57d8a71… mistachkin 1682 }
57d8a71… mistachkin 1683
57d8a71… mistachkin 1684 /*
57d8a71… mistachkin 1685 ** This function evaluates the specified TH1 script and attempts to parse
57d8a71… mistachkin 1686 ** its result as a colon-delimited triplet containing a process identifier,
57d8a71… mistachkin 1687 ** address, and size (in bytes) of the database encryption key. This is
f41cf03… mistachkin 1688 ** only necessary (or functional) on Windows or Linux.
57d8a71… mistachkin 1689 */
f41cf03… mistachkin 1690 static PID_T db_handle_saved_encryption_key_for_process_via_th1(
f41cf03… mistachkin 1691 const char *zConfig, /* The TH1 script to evaluate. */
f41cf03… mistachkin 1692 int eType /* Non-zero to write key to parent process -OR-
f41cf03… mistachkin 1693 * zero to read it from the parent process. */
57d8a71… mistachkin 1694 ){
f41cf03… mistachkin 1695 PID_T processId = 0;
57d8a71… mistachkin 1696 int rc;
57d8a71… mistachkin 1697 char *zResult;
660c2b1… mistachkin 1698 char *zPwd = file_getcwd(0, 0);
57d8a71… mistachkin 1699 Th_FossilInit(TH_INIT_DEFAULT | TH_INIT_NEED_CONFIG | TH_INIT_NO_REPO);
57d8a71… mistachkin 1700 rc = Th_Eval(g.interp, 0, zConfig, -1);
57d8a71… mistachkin 1701 zResult = (char*)Th_GetResult(g.interp, 0);
57d8a71… mistachkin 1702 if( rc!=TH_OK ){
57d8a71… mistachkin 1703 fossil_fatal("script for pid key failed: %s", zResult);
57d8a71… mistachkin 1704 }
57d8a71… mistachkin 1705 if( zResult ){
57d8a71… mistachkin 1706 LPVOID pAddress = NULL;
57d8a71… mistachkin 1707 SIZE_T nSize = 0;
57d8a71… mistachkin 1708 parse_pid_key_value(zResult, &processId, &pAddress, &nSize);
f41cf03… mistachkin 1709 if( eType==SEE_KEY_READ ){
f41cf03… mistachkin 1710 db_read_saved_encryption_key_from_process(processId, pAddress, nSize);
f41cf03… mistachkin 1711 }else if( eType==SEE_KEY_WRITE ){
f41cf03… mistachkin 1712 db_write_saved_encryption_key_to_process(processId, pAddress, nSize);
f41cf03… mistachkin 1713 }else if( eType==SEE_KEY_ZERO ){
f41cf03… mistachkin 1714 db_zero_saved_encryption_key_in_process(processId, pAddress, nSize);
f41cf03… mistachkin 1715 }else{
f41cf03… mistachkin 1716 fossil_panic("unsupported SEE key operation %d", eType);
f41cf03… mistachkin 1717 }
57d8a71… mistachkin 1718 }
660c2b1… mistachkin 1719 file_chdir(zPwd, 0);
660c2b1… mistachkin 1720 fossil_free(zPwd);
f41cf03… mistachkin 1721 return processId;
57d8a71… mistachkin 1722 }
f41cf03… mistachkin 1723
f41cf03… mistachkin 1724 /*
f41cf03… mistachkin 1725 ** This function sets the saved database encryption key to one that gets
f41cf03… mistachkin 1726 ** read from the specified Fossil parent process, if applicable. This is
f41cf03… mistachkin 1727 ** only necessary (or functional) on Windows or Linux.
f41cf03… mistachkin 1728 */
f41cf03… mistachkin 1729 PID_T db_maybe_handle_saved_encryption_key_for_process(int eType){
f41cf03… mistachkin 1730 PID_T processId = 0;
f41cf03… mistachkin 1731 g.zPidKey = find_option("usepidkey",0,1);
f41cf03… mistachkin 1732 if( !g.zPidKey ){
f41cf03… mistachkin 1733 g.zPidKey = fossil_getenv("FOSSIL_SEE_PID_KEY");
f41cf03… mistachkin 1734 }
f41cf03… mistachkin 1735 if( g.zPidKey ){
f41cf03… mistachkin 1736 LPVOID pAddress = NULL;
f41cf03… mistachkin 1737 SIZE_T nSize = 0;
f41cf03… mistachkin 1738 parse_pid_key_value(g.zPidKey, &processId, &pAddress, &nSize);
f41cf03… mistachkin 1739 if( eType==SEE_KEY_READ ){
f41cf03… mistachkin 1740 db_read_saved_encryption_key_from_process(processId, pAddress, nSize);
f41cf03… mistachkin 1741 }else if( eType==SEE_KEY_WRITE ){
f41cf03… mistachkin 1742 db_write_saved_encryption_key_to_process(processId, pAddress, nSize);
f41cf03… mistachkin 1743 }else if( eType==SEE_KEY_ZERO ){
f41cf03… mistachkin 1744 db_zero_saved_encryption_key_in_process(processId, pAddress, nSize);
f41cf03… mistachkin 1745 }else{
f41cf03… mistachkin 1746 fossil_panic("unsupported SEE key operation %d", eType);
f41cf03… mistachkin 1747 }
f41cf03… mistachkin 1748 }else{
f41cf03… mistachkin 1749 const char *zSeeDbConfig = find_option("seedbcfg",0,1);
f41cf03… mistachkin 1750 if( !zSeeDbConfig ){
f41cf03… mistachkin 1751 zSeeDbConfig = fossil_getenv("FOSSIL_SEE_DB_CONFIG");
f41cf03… mistachkin 1752 }
f41cf03… mistachkin 1753 if( zSeeDbConfig ){
f41cf03… mistachkin 1754 processId = db_handle_saved_encryption_key_for_process_via_th1(
f41cf03… mistachkin 1755 zSeeDbConfig, eType
f41cf03… mistachkin 1756 );
f41cf03… mistachkin 1757 }
f41cf03… mistachkin 1758 }
f41cf03… mistachkin 1759 return processId;
f41cf03… mistachkin 1760 }
a8484dc… mistachkin 1761 #endif /* USE_SEE */
ed871fb… drh 1762
ed871fb… drh 1763 /*
ed871fb… drh 1764 ** If the database file zDbFile has a name that suggests that it is
a8484dc… mistachkin 1765 ** encrypted, then prompt for the database encryption key and return it
a8484dc… mistachkin 1766 ** in the blob *pKey. Or, if the encryption key has previously been
a8484dc… mistachkin 1767 ** requested, just return a copy of the previous result. The blob in
a8484dc… mistachkin 1768 ** *pKey must be initialized.
ed871fb… drh 1769 */
a8484dc… mistachkin 1770 static void db_maybe_obtain_encryption_key(
ed871fb… drh 1771 const char *zDbFile, /* Name of the database file */
ed871fb… drh 1772 Blob *pKey /* Put the encryption key here */
ed871fb… drh 1773 ){
60026ba… drh 1774 #if USE_SEE
980fdda… mistachkin 1775 if( sqlite3_strglob("*.efossil", zDbFile)==0 ){
a8484dc… mistachkin 1776 char *zKey = db_get_saved_encryption_key();
a8484dc… mistachkin 1777 if( zKey ){
a8484dc… mistachkin 1778 blob_set(pKey, zKey);
ed871fb… drh 1779 }else{
ed871fb… drh 1780 char *zPrompt = mprintf("\rencryption key for '%s': ", zDbFile);
ed871fb… drh 1781 prompt_for_password(zPrompt, pKey, 0);
ed871fb… drh 1782 fossil_free(zPrompt);
a8484dc… mistachkin 1783 db_set_saved_encryption_key(pKey);
ed871fb… drh 1784 }
ed871fb… drh 1785 }
a8484dc… mistachkin 1786 #endif
a8484dc… mistachkin 1787 }
a8484dc… mistachkin 1788
a8484dc… mistachkin 1789
a8484dc… mistachkin 1790 /*
95f14fa… mistachkin 1791 ** Sets the encryption key for the database, if necessary.
95f14fa… mistachkin 1792 */
d6422ab… mistachkin 1793 void db_maybe_set_encryption_key(sqlite3 *db, const char *zDbName){
95f14fa… mistachkin 1794 Blob key;
95f14fa… mistachkin 1795 blob_init(&key, 0, 0);
95f14fa… mistachkin 1796 db_maybe_obtain_encryption_key(zDbName, &key);
95f14fa… mistachkin 1797 if( blob_size(&key)>0 ){
00dfbdb… mistachkin 1798 if( fossil_getenv("FOSSIL_USE_SEE_TEXTKEY")==0 ){
00dfbdb… mistachkin 1799 char *zCmd = sqlite3_mprintf("PRAGMA key(%Q)", blob_str(&key));
00dfbdb… mistachkin 1800 sqlite3_exec(db, zCmd, 0, 0, 0);
00dfbdb… mistachkin 1801 fossil_secure_zero(zCmd, strlen(zCmd));
00dfbdb… mistachkin 1802 sqlite3_free(zCmd);
00dfbdb… mistachkin 1803 #if USE_SEE
00dfbdb… mistachkin 1804 }else{
00dfbdb… mistachkin 1805 sqlite3_key(db, blob_str(&key), -1);
00dfbdb… mistachkin 1806 #endif
00dfbdb… mistachkin 1807 }
95f14fa… mistachkin 1808 }
95f14fa… mistachkin 1809 blob_reset(&key);
95f14fa… mistachkin 1810 }
95f14fa… mistachkin 1811
95f14fa… mistachkin 1812 /*
00ac794… drh 1813 ** Open a database file. Return a pointer to the new database
00ac794… drh 1814 ** connection. An error results in process abort.
00ac794… drh 1815 */
f97e1cf… drh 1816 LOCAL sqlite3 *db_open(const char *zDbName){
00ac794… drh 1817 int rc;
00ac794… drh 1818 sqlite3 *db;
ab7ad23… stephan 1819 Blob bNameCheck = BLOB_INITIALIZER;
5af289e… jan.nijtmans 1820
baa1ebb… drh 1821 if( g.fSqlTrace ) fossil_trace("-- sqlite3_open: [%s]\n", zDbName);
ab7ad23… stephan 1822 file_canonical_name(zDbName, &bNameCheck, 0)
ab7ad23… stephan 1823 /* For purposes of the apndvfs check, g.nameOfExe and zDbName must
ab7ad23… stephan 1824 ** both be canonicalized, else chances are very good that they
ab7ad23… stephan 1825 ** will not match even if they're the same file. Details:
ab7ad23… stephan 1826 ** https://fossil-scm.org/forum/forumpost/16880a28aad1a868 */;
ab7ad23… stephan 1827 if( strcmp(blob_str(&bNameCheck), g.nameOfExe)==0 ){
a7056e6… mistachkin 1828 extern int sqlite3_appendvfs_init(
a7056e6… mistachkin 1829 sqlite3 *, char **, const sqlite3_api_routines *
a7056e6… mistachkin 1830 );
37b2eb9… drh 1831 sqlite3_appendvfs_init(0,0,0);
37b2eb9… drh 1832 g.zVfsName = "apndvfs";
37b2eb9… drh 1833 }
37ae94b… drh 1834 blob_reset(&bNameCheck);
19de4b5… mistachkin 1835 rc = sqlite3_open_v2(
19de4b5… mistachkin 1836 zDbName, &db,
19de4b5… mistachkin 1837 SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
19de4b5… mistachkin 1838 g.zVfsName
19de4b5… mistachkin 1839 );
00ac794… drh 1840 if( rc!=SQLITE_OK ){
baa1ebb… drh 1841 db_err("[%s]: %s", zDbName, sqlite3_errmsg(db));
00ac794… drh 1842 }
d6422ab… mistachkin 1843 db_maybe_set_encryption_key(db, zDbName);
994a7c7… drh 1844 sqlite3_db_config(db, SQLITE_DBCONFIG_ENABLE_FKEY, 0, &rc);
8a3dc1a… drh 1845 sqlite3_db_config(db, SQLITE_DBCONFIG_ENABLE_TRIGGER, 0, &rc);
994a7c7… drh 1846 sqlite3_db_config(db, SQLITE_DBCONFIG_TRUSTED_SCHEMA, 0, &rc);
994a7c7… drh 1847 sqlite3_db_config(db, SQLITE_DBCONFIG_DQS_DDL, 0, &rc);
994a7c7… drh 1848 sqlite3_db_config(db, SQLITE_DBCONFIG_DQS_DML, 0, &rc);
994a7c7… drh 1849 sqlite3_db_config(db, SQLITE_DBCONFIG_DEFENSIVE, 1, &rc);
d9543f4… drh 1850 sqlite3_busy_timeout(db, 15000);
932825b… drh 1851 sqlite3_wal_autocheckpoint(db, 1); /* Set to checkpoint frequently */
2c95802… jan.nijtmans 1852 sqlite3_create_function(db, "user", 0, SQLITE_UTF8, 0, db_sql_user, 0, 0);
2c95802… jan.nijtmans 1853 sqlite3_create_function(db, "cgi", 1, SQLITE_UTF8, 0, db_sql_cgi, 0, 0);
2c95802… jan.nijtmans 1854 sqlite3_create_function(db, "cgi", 2, SQLITE_UTF8, 0, db_sql_cgi, 0, 0);
f97e1cf… drh 1855 sqlite3_create_function(db, "print", -1, SQLITE_UTF8, 0,db_sql_print,0,0);
f97e1cf… drh 1856 sqlite3_create_function(
f97e1cf… drh 1857 db, "is_selected", 1, SQLITE_UTF8, 0, file_is_selected,0,0
f97e1cf… drh 1858 );
f97e1cf… drh 1859 sqlite3_create_function(
f97e1cf… drh 1860 db, "if_selected", 3, SQLITE_UTF8, 0, file_is_selected,0,0
f97e1cf… drh 1861 );
ada305f… drh 1862 if( g.fSqlTrace ) sqlite3_trace_v2(db, SQLITE_TRACE_PROFILE, db_sql_trace, 0);
9e318f6… jan.nijtmans 1863 db_add_aux_functions(db);
ba1429c… drh 1864 re_add_sql_func(db); /* The REGEXP operator */
ba1429c… drh 1865 foci_register(db); /* The "files_of_checkin" virtual table */
f741baa… drh 1866 sqlite3_set_authorizer(db, db_top_authorizer, db);
c3c4ef1… stephan 1867 db_register_fts5(db) /* in search.c */;
00ac794… drh 1868 return db;
00ac794… drh 1869 }
00ac794… drh 1870
75bcb48… mistachkin 1871
75bcb48… mistachkin 1872 /*
75bcb48… mistachkin 1873 ** Detaches the zLabel database.
75bcb48… mistachkin 1874 */
75bcb48… mistachkin 1875 void db_detach(const char *zLabel){
49b0ff1… drh 1876 db_multi_exec("DETACH DATABASE %Q", zLabel);
75bcb48… mistachkin 1877 }
75bcb48… mistachkin 1878
75bcb48… mistachkin 1879 /*
75bcb48… mistachkin 1880 ** zDbName is the name of a database file. Attach zDbName using
75bcb48… mistachkin 1881 ** the name zLabel.
75bcb48… mistachkin 1882 */
75bcb48… mistachkin 1883 void db_attach(const char *zDbName, const char *zLabel){
ed871fb… drh 1884 Blob key;
22645e1… drh 1885 if( db_table_exists(zLabel,"sqlite_schema") ) return;
a8484dc… mistachkin 1886 blob_init(&key, 0, 0);
a8484dc… mistachkin 1887 db_maybe_obtain_encryption_key(zDbName, &key);
00dfbdb… mistachkin 1888 if( fossil_getenv("FOSSIL_USE_SEE_TEXTKEY")==0 ){
00dfbdb… mistachkin 1889 char *zCmd = sqlite3_mprintf("ATTACH DATABASE %Q AS %Q KEY %Q",
00dfbdb… mistachkin 1890 zDbName, zLabel, blob_str(&key));
0ea56bb… drh 1891 db_exec_sql(zCmd);
00dfbdb… mistachkin 1892 fossil_secure_zero(zCmd, strlen(zCmd));
00dfbdb… mistachkin 1893 sqlite3_free(zCmd);
00dfbdb… mistachkin 1894 }else{
00dfbdb… mistachkin 1895 char *zCmd = sqlite3_mprintf("ATTACH DATABASE %Q AS %Q KEY ''",
00dfbdb… mistachkin 1896 zDbName, zLabel);
0ea56bb… drh 1897 db_exec_sql(zCmd);
00dfbdb… mistachkin 1898 sqlite3_free(zCmd);
00dfbdb… mistachkin 1899 #if USE_SEE
00dfbdb… mistachkin 1900 if( blob_size(&key)>0 ){
00dfbdb… mistachkin 1901 sqlite3_key_v2(g.db, zLabel, blob_str(&key), -1);
00dfbdb… mistachkin 1902 }
00dfbdb… mistachkin 1903 #endif
00dfbdb… mistachkin 1904 }
ed871fb… drh 1905 blob_reset(&key);
06aec61… drh 1906 }
06aec61… drh 1907
06aec61… drh 1908 /*
06aec61… drh 1909 ** Change the schema name of the "main" database to zLabel.
06aec61… drh 1910 ** zLabel must be a static string that is unchanged for the life of
06aec61… drh 1911 ** the database connection.
06aec61… drh 1912 **
06aec61… drh 1913 ** After calling this routine, db_database_slot(zLabel) should
06aec61… drh 1914 ** return 0.
06aec61… drh 1915 */
06aec61… drh 1916 void db_set_main_schemaname(sqlite3 *db, const char *zLabel){
06aec61… drh 1917 if( sqlite3_db_config(db, SQLITE_DBCONFIG_MAINDBNAME, zLabel) ){
3f5ab71… drh 1918 fossil_panic("Fossil requires a version of SQLite that supports the "
06aec61… drh 1919 "SQLITE_DBCONFIG_MAINDBNAME interface.");
06aec61… drh 1920 }
06aec61… drh 1921 }
06aec61… drh 1922
06aec61… drh 1923 /*
06aec61… drh 1924 ** Return the slot number for database zLabel. The first database
06aec61… drh 1925 ** opened is slot 0. The "temp" database is slot 1. Attached databases
06aec61… drh 1926 ** are slots 2 and higher.
06aec61… drh 1927 **
06aec61… drh 1928 ** Return -1 if zLabel does not match any open database.
06aec61… drh 1929 */
06aec61… drh 1930 int db_database_slot(const char *zLabel){
06aec61… drh 1931 int iSlot = -1;
02fa325… mistachkin 1932 int rc;
06aec61… drh 1933 Stmt q;
06aec61… drh 1934 if( g.db==0 ) return iSlot;
02fa325… mistachkin 1935 rc = db_prepare_ignore_error(&q, "PRAGMA database_list");
eddfa8d… drh 1936 if( rc==SQLITE_OK ){
eddfa8d… drh 1937 while( db_step(&q)==SQLITE_ROW ){
eddfa8d… drh 1938 if( fossil_strcmp(db_column_text(&q,1),zLabel)==0 ){
eddfa8d… drh 1939 iSlot = db_column_int(&q, 0);
eddfa8d… drh 1940 break;
eddfa8d… drh 1941 }
06aec61… drh 1942 }
06aec61… drh 1943 }
06aec61… drh 1944 db_finalize(&q);
06aec61… drh 1945 return iSlot;
75bcb48… mistachkin 1946 }
06aec61… drh 1947 void db_open_or_attach(const char *zDbName, const char *zLabel){
00ac794… drh 1948 if( !g.db ){
f97e1cf… drh 1949 g.db = db_open(zDbName);
06aec61… drh 1950 db_set_main_schemaname(g.db, zLabel);
75bcb48… mistachkin 1951 }else{
75bcb48… mistachkin 1952 db_attach(zDbName, zLabel);
75bcb48… mistachkin 1953 }
75bcb48… mistachkin 1954 }
75bcb48… mistachkin 1955
75bcb48… mistachkin 1956 /*
4645e9b… drh 1957 ** Close the per-user configuration database file
1c528d3… mistachkin 1958 */
1c528d3… mistachkin 1959 void db_close_config(){
06aec61… drh 1960 int iSlot = db_database_slot("configdb");
06aec61… drh 1961 if( iSlot>0 ){
1c528d3… mistachkin 1962 db_detach("configdb");
1c528d3… mistachkin 1963 }else if( g.dbConfig ){
b0d61b0… mistachkin 1964 sqlite3_wal_checkpoint(g.dbConfig, 0);
1c528d3… mistachkin 1965 sqlite3_close(g.dbConfig);
1c528d3… mistachkin 1966 g.dbConfig = 0;
06aec61… drh 1967 }else if( g.db && 0==iSlot ){
1e79991… drh 1968 int rc;
b0d61b0… mistachkin 1969 sqlite3_wal_checkpoint(g.db, 0);
1e79991… drh 1970 rc = sqlite3_close(g.db);
1e79991… drh 1971 if( g.fSqlTrace ) fossil_trace("-- db_close_config(%d)\n", rc);
1c528d3… mistachkin 1972 g.db = 0;
57d8a71… mistachkin 1973 g.repositoryOpen = 0;
aefbd20… mistachkin 1974 g.localOpen = 0;
79988f9… drh 1975 }else{
79988f9… drh 1976 return;
1c528d3… mistachkin 1977 }
79988f9… drh 1978 fossil_free(g.zConfigDbName);
79988f9… drh 1979 g.zConfigDbName = 0;
1c528d3… mistachkin 1980 }
1c528d3… mistachkin 1981
1c528d3… mistachkin 1982 /*
4645e9b… drh 1983 ** Compute the name of the configuration database. If unable to find the
4645e9b… drh 1984 ** database, return 0 if isOptional is true, or panic if isOptional is false.
c97a085… mistachkin 1985 **
4645e9b… drh 1986 ** Space to hold the result comes from fossil_malloc().
c97a085… mistachkin 1987 */
4645e9b… drh 1988 static char *db_configdb_name(int isOptional){
4645e9b… drh 1989 char *zHome; /* Home directory */
4645e9b… drh 1990 char *zDbName; /* Name of the database file */
4645e9b… drh 1991
4645e9b… drh 1992
4645e9b… drh 1993 /* On Windows, look for these directories, in order:
4645e9b… drh 1994 **
4645e9b… drh 1995 ** FOSSIL_HOME
4645e9b… drh 1996 ** LOCALAPPDATA
4645e9b… drh 1997 ** APPDATA
4645e9b… drh 1998 ** USERPROFILE
4645e9b… drh 1999 ** HOMEDRIVE HOMEPATH
4645e9b… drh 2000 */
c97a085… mistachkin 2001 #if defined(_WIN32) || defined(__CYGWIN__)
4645e9b… drh 2002 zHome = fossil_getenv("FOSSIL_HOME");
c97a085… mistachkin 2003 if( zHome==0 ){
c97a085… mistachkin 2004 zHome = fossil_getenv("LOCALAPPDATA");
c97a085… mistachkin 2005 if( zHome==0 ){
c97a085… mistachkin 2006 zHome = fossil_getenv("APPDATA");
c97a085… mistachkin 2007 if( zHome==0 ){
006afac… drh 2008 zHome = fossil_getenv("USERPROFILE");
006afac… drh 2009 if( zHome==0 ){
006afac… drh 2010 char *zDrive = fossil_getenv("HOMEDRIVE");
006afac… drh 2011 char *zPath = fossil_getenv("HOMEPATH");
006afac… drh 2012 if( zDrive && zPath ) zHome = mprintf("%s%s", zDrive, zPath);
006afac… drh 2013 }
006afac… drh 2014 }
006afac… drh 2015 }
006afac… drh 2016 }
4645e9b… drh 2017 zDbName = mprintf("%//_fossil", zHome);
4645e9b… drh 2018 fossil_free(zHome);
4645e9b… drh 2019 return zDbName;
4645e9b… drh 2020
4645e9b… drh 2021 #else /* if unix */
4645e9b… drh 2022 char *zXdgHome;
4645e9b… drh 2023
4645e9b… drh 2024 /* For unix. a 5-step algorithm is used.
4645e9b… drh 2025 ** See ../www/tech_overview.wiki for discussion.
4645e9b… drh 2026 **
4645e9b… drh 2027 ** Step 1: If FOSSIL_HOME exists -> $FOSSIL_HOME/.fossil
4645e9b… drh 2028 */
4645e9b… drh 2029 zHome = fossil_getenv("FOSSIL_HOME");
4645e9b… drh 2030 if( zHome!=0 ) return mprintf("%s/.fossil", zHome);
4645e9b… drh 2031
4645e9b… drh 2032 /* Step 2: If HOME exists and file $HOME/.fossil exists -> $HOME/.fossil
4645e9b… drh 2033 */
4645e9b… drh 2034 zHome = fossil_getenv("HOME");
4645e9b… drh 2035 if( zHome ){
4645e9b… drh 2036 zDbName = mprintf("%s/.fossil", zHome);
4645e9b… drh 2037 if( file_size(zDbName, ExtFILE)>1024*3 ){
4645e9b… drh 2038 return zDbName;
4645e9b… drh 2039 }
4645e9b… drh 2040 fossil_free(zDbName);
4645e9b… drh 2041 }
4645e9b… drh 2042
4645e9b… drh 2043 /* Step 3: if XDG_CONFIG_HOME exists -> $XDG_CONFIG_HOME/fossil.db
4645e9b… drh 2044 */
4645e9b… drh 2045 zXdgHome = fossil_getenv("XDG_CONFIG_HOME");
4645e9b… drh 2046 if( zXdgHome!=0 ){
4645e9b… drh 2047 return mprintf("%s/fossil.db", zXdgHome);
4645e9b… drh 2048 }
4645e9b… drh 2049
9cb8194… drh 2050 /* The HOME variable is required in order to continue.
4645e9b… drh 2051 */
4645e9b… drh 2052 if( zHome==0 ){
4645e9b… drh 2053 if( isOptional ) return 0;
31c7bdb… larrybr 2054 fossil_fatal("cannot locate home directory - please set one of the "
4645e9b… drh 2055 "FOSSIL_HOME, XDG_CONFIG_HOME, or HOME environment "
4645e9b… drh 2056 "variables");
4645e9b… drh 2057 }
4645e9b… drh 2058
9cb8194… drh 2059 /* Step 4: If $HOME/.config is a directory -> $HOME/.config/fossil.db
8388a46… drh 2060 */
8388a46… drh 2061 zXdgHome = mprintf("%s/.config", zHome);
8388a46… drh 2062 if( file_isdir(zXdgHome, ExtFILE)==1 ){
8388a46… drh 2063 fossil_free(zXdgHome);
8388a46… drh 2064 return mprintf("%s/.config/fossil.db", zHome);
8388a46… drh 2065 }
8388a46… drh 2066
9cb8194… drh 2067 /* Step 5: Otherwise -> $HOME/.fossil
4645e9b… drh 2068 */
8388a46… drh 2069 return mprintf("%s/.fossil", zHome);
4645e9b… drh 2070 #endif /* unix */
4645e9b… drh 2071 }
4645e9b… drh 2072
4645e9b… drh 2073 /*
4645e9b… drh 2074 ** Open the configuration database. Create the database anew if
4645e9b… drh 2075 ** it does not already exist.
4645e9b… drh 2076 **
4645e9b… drh 2077 ** If the useAttach flag is 0 (the usual case) then the configuration
4645e9b… drh 2078 ** database is opened on a separate database connection g.dbConfig.
4645e9b… drh 2079 ** This prevents the database from becoming locked on long check-in or sync
4645e9b… drh 2080 ** operations which hold an exclusive transaction. In a few cases, though,
4645e9b… drh 2081 ** it is convenient for the database to be attached to the main database
4645e9b… drh 2082 ** connection so that we can join between the various databases. In that
4645e9b… drh 2083 ** case, invoke this routine with useAttach as 1.
4645e9b… drh 2084 */
4645e9b… drh 2085 int db_open_config(int useAttach, int isOptional){
4645e9b… drh 2086 char *zDbName;
4645e9b… drh 2087 if( g.zConfigDbName ){
4645e9b… drh 2088 int alreadyAttached = db_database_slot("configdb")>0;
4645e9b… drh 2089 if( useAttach==alreadyAttached ) return 1; /* Already open. */
4645e9b… drh 2090 db_close_config();
4645e9b… drh 2091 }
4645e9b… drh 2092 zDbName = db_configdb_name(isOptional);
4645e9b… drh 2093 if( zDbName==0 ) return 0;
4645e9b… drh 2094 if( file_size(zDbName, ExtFILE)<1024*3 ){
4645e9b… drh 2095 char *zHome = file_dirname(zDbName);
4645e9b… drh 2096 int rc;
4645e9b… drh 2097 if( file_isdir(zHome, ExtFILE)==0 ){
4645e9b… drh 2098 file_mkdir(zHome, ExtFILE, 0);
4645e9b… drh 2099 }
4645e9b… drh 2100 rc = file_access(zHome, W_OK);
4645e9b… drh 2101 if( rc ){
4645e9b… drh 2102 if( isOptional ) return 0;
31c7bdb… larrybr 2103 fossil_fatal("home directory \"%s\" must be writeable", zHome);
4645e9b… drh 2104 }
4645e9b… drh 2105 db_init_database(zDbName, zConfigSchema, (char*)0);
aad4b78… drh 2106 fossil_free(zHome);
473e08a… jan.nijtmans 2107 }
473e08a… jan.nijtmans 2108 if( file_access(zDbName, W_OK) ){
b06cd63… mistachkin 2109 if( isOptional ) return 0;
31c7bdb… larrybr 2110 fossil_fatal("configuration file %s must be writeable", zDbName);
fe453a4… drh 2111 }
00ac794… drh 2112 if( useAttach ){
06aec61… drh 2113 db_open_or_attach(zDbName, "configdb");
00ac794… drh 2114 g.dbConfig = 0;
00ac794… drh 2115 }else{
f97e1cf… drh 2116 g.dbConfig = db_open(zDbName);
06aec61… drh 2117 db_set_main_schemaname(g.dbConfig, "configdb");
21f8161… drh 2118 }
a5dc533… jan.nijtmans 2119 g.zConfigDbName = zDbName;
b06cd63… mistachkin 2120 return 1;
21f8161… drh 2121 }
21f8161… drh 2122
1aa8067… drh 2123 /*
1aa8067… drh 2124 ** Return TRUE if zTable exists.
1aa8067… drh 2125 */
1aa8067… drh 2126 int db_table_exists(
1aa8067… drh 2127 const char *zDb, /* One of: NULL, "configdb", "localdb", "repository" */
1aa8067… drh 2128 const char *zTable /* Name of table */
1aa8067… drh 2129 ){
06aec61… drh 2130 return sqlite3_table_column_metadata(g.db, zDb, zTable, 0,
06aec61… drh 2131 0, 0, 0, 0, 0)==SQLITE_OK;
1aa8067… drh 2132 }
1aa8067… drh 2133
1aa8067… drh 2134 /*
1aa8067… drh 2135 ** Return TRUE if zTable exists and contains column zColumn.
1aa8067… drh 2136 ** Return FALSE if zTable does not exist or if zTable exists
1aa8067… drh 2137 ** but lacks zColumn.
1aa8067… drh 2138 */
1aa8067… drh 2139 int db_table_has_column(
1aa8067… drh 2140 const char *zDb, /* One of: NULL, "config", "localdb", "repository" */
1aa8067… drh 2141 const char *zTable, /* Name of table */
1aa8067… drh 2142 const char *zColumn /* Name of column in table */
1aa8067… drh 2143 ){
06aec61… drh 2144 return sqlite3_table_column_metadata(g.db, zDb, zTable, zColumn,
06aec61… drh 2145 0, 0, 0, 0, 0)==SQLITE_OK;
1aa8067… drh 2146 }
21f8161… drh 2147
21f8161… drh 2148 /*
21f8161… drh 2149 ** Returns TRUE if zTable exists in the local database but lacks column
21f8161… drh 2150 ** zColumn
21f8161… drh 2151 */
21f8161… drh 2152 static int db_local_table_exists_but_lacks_column(
21f8161… drh 2153 const char *zTable,
21f8161… drh 2154 const char *zColumn
21f8161… drh 2155 ){
06aec61… drh 2156 return db_table_exists("localdb", zTable)
06aec61… drh 2157 && !db_table_has_column("localdb", zTable, zColumn);
e146d80… drh 2158
d8ec765… drh 2159 if( file_access(zDbName, F_OK) ) return 0;
1772357… drh 2160 lsize = file_size(zDbName, ExtFILE);
06aec61… drh 2161 db_open_or_attach(zDbName, "localdb");
fff37e6… drh 2162
e2bdc10… danield 2163 /* Check to see if the check-out database has the latest schema changes.
fff37e6… drh 2164 ** The most recent schema change (2019-01-19) is the addition of the
fff37e6… drh 2165 ** vmerge.mhash and vfile.mhash fields. If the schema has the vmerge.mhash
918bcfc… jan.nijtmans 2166 ** column, assume everything else is up-to-date.
fff37e6… drh 2167 */
fff37e6… drh 2168 if( db_table_has_column("localdb","vmerge","mhash") ){
bc36fdc… danield 2169 return 1; /* This is a check-out database with the latest schema */
fff37e6… drh 2170 }
fff37e6… drh 2171
fff37e6… drh 2172 /* If there is no vfile table, then assume we have picked up something
bc36fdc… danield 2173 ** that is not even close to being a valid check-out database */
fff37e6… drh 2174 if( !db_table_exists("localdb","vfile") ){
fff37e6… drh 2175 return 0; /* Not a DB */
fff37e6… drh 2176 }
dacc694… jan.nijtmans 2177
7c2577b… drh 2178 /* If the "isexe" column is missing from the vfile table, then
7c2577b… drh 2179 ** add it now. This code added on 2010-03-06. After all users have
dacc694… jan.nijtmans 2180 ** upgraded, this code can be safely deleted.
e4f1c1f… drh 2181 */
fff37e6… drh 2182 if( !db_table_has_column("localdb","vfile","isexe") ){
e4f1c1f… drh 2183 db_multi_exec("ALTER TABLE vfile ADD COLUMN isexe BOOLEAN DEFAULT 0");
a7248d8… drh 2184 }
e4f1c1f… drh 2185
e4f1c1f… drh 2186 /* If "islink"/"isLink" columns are missing from tables, then
e4f1c1f… drh 2187 ** add them now. This code added on 2011-01-17 and 2011-08-27.
dacc694… jan.nijtmans 2188 ** After all users have upgraded, this code can be safely deleted.
e4f1c1f… drh 2189 */
fff37e6… drh 2190 if( !db_table_has_column("localdb","vfile","isLink") ){
e4f1c1f… drh 2191 db_multi_exec("ALTER TABLE vfile ADD COLUMN islink BOOLEAN DEFAULT 0");
21f8161… drh 2192 if( db_local_table_exists_but_lacks_column("stashfile", "isLink") ){
21f8161… drh 2193 db_multi_exec("ALTER TABLE stashfile ADD COLUMN isLink BOOL DEFAULT 0");
21f8161… drh 2194 }
21f8161… drh 2195 if( db_local_table_exists_but_lacks_column("undo", "isLink") ){
21f8161… drh 2196 db_multi_exec("ALTER TABLE undo ADD COLUMN isLink BOOLEAN DEFAULT 0");
21f8161… drh 2197 }
21f8161… drh 2198 if( db_local_table_exists_but_lacks_column("undo_vfile", "islink") ){
21f8161… drh 2199 db_multi_exec("ALTER TABLE undo_vfile ADD COLUMN islink BOOL DEFAULT 0");
21f8161… drh 2200 }
21f8161… drh 2201 }
fff37e6… drh 2202
275da70… danield 2203 /* The design of the check-out database changed on 2019-01-19 adding the mhash
fff37e6… drh 2204 ** column to vfile and vmerge and changing the UNIQUE index on vmerge into
fff37e6… drh 2205 ** a PRIMARY KEY that includes the new mhash column. However, we must have
fff37e6… drh 2206 ** the repository database at hand in order to do the migration, so that
fff37e6… drh 2207 ** step is deferred. */
f9a200f… drh 2208 ** directory is found by searching for a file named "_FOSSIL_" or ".fslckout"
8bdea95… drh 2209 ** that contains a valid repository database.
8bdea95… drh 2210 **
f9a200f… drh 2211 ** For legacy, also look for ".fos". The use of ".fos" is deprecated
f9a200f… drh 2212 ** since "fos" has negative connotations in Hungarian, we are told.
f9a200f… drh 2213 **
beb91c9… jan.nijtmans 2214 ** If no valid _FOSSIL_ or .fslckout file is found, we move up one level and
8bdea95… drh 2215 ** try again. Once the file is found, the g.zLocalRoot variable is set
8bdea95… drh 2216 ** to the root of the repository tree and this routine returns 1. If
8bdea95… drh 2217 ** no database is found, then this routine return 0.
8bdea95… drh 2218 **
15a7b1f… drh 2219 ** In db_open_local_v2(), if the bRootOnly flag is true, then only
bc36fdc… danield 2220 ** look in the CWD for the check-out database. Do not scan upwards in
15a7b1f… drh 2221 ** the file hierarchy.
15a7b1f… drh 2222 **
8bdea95… drh 2223 ** This routine always opens the user database regardless of whether or
beb91c9… jan.nijtmans 2224 ** not the repository database is found. If the _FOSSIL_ or .fslckout file
8bdea95… drh 2225 ** is found, it is attached to the open database connection too.
8bdea95… drh 2226 */
15a7b1f… drh 2227 int db_open_local_v2(const char *zDbName, int bRootOnly){
8bdea95… drh 2228 int i, n;
8bdea95… drh 2229 char zPwd[2000];
f922f4e… drh 2230 static const char *(aDbName[]) = { "_FOSSIL_", ".fslckout", ".fos" };
dacc694… jan.nijtmans 2231
c7f5541… mistachkin 2232 if( g.localOpen ) return 1;
4b34254… drh 2233 file_getcwd(zPwd, sizeof(zPwd)-20);
8bdea95… drh 2234 n = strlen(zPwd);
8bdea95… drh 2235 while( n>0 ){
beb91c9… jan.nijtmans 2236 for(i=0; i<count(aDbName); i++){
beb91c9… jan.nijtmans 2237 sqlite3_snprintf(sizeof(zPwd)-n, &zPwd[n], "/%s", aDbName[i]);
8bdea95… drh 2238 if( isValidLocalDb(zPwd) ){
b06cd63… mistachkin 2239 if( db_open_config(0, 1)==0 ){
b06cd63… mistachkin 2240 return 0; /* Configuration could not be opened */
b06cd63… mistachkin 2241 }
bc36fdc… danield 2242 /* Found a valid check-out database file */
0c9dff6… drh 2243 g.zLocalDbName = fossil_strdup(zPwd);
8bdea95… drh 2244 zPwd[n] = 0;
cbcd705… drh 2245 while( n>0 && zPwd[n-1]=='/' ){
1d280a8… drh 2246 n--;
1d280a8… drh 2247 zPwd[n] = 0;
1d280a8… drh 2248 }
8bdea95… drh 2249 g.zLocalRoot = mprintf("%s/", zPwd);
1ef6499… stephan 2250 g.localOpen = 1;
e590547… jan.nijtmans 2251 db_open_repository(zDbName);
8bdea95… drh 2252 return 1;
8bdea95… drh 2253 }
8bdea95… drh 2254 }
15a7b1f… drh 2255 if( bRootOnly ) break;
8bdea95… drh 2256 n--;
cbcd705… drh 2257 while( n>1 && zPwd[n]!='/' ){ n--; }
cbcd705… drh 2258 while( n>1 && zPwd[n-1]=='/' ){ n--; }
8bdea95… drh 2259 zPwd[n] = 0;
8bdea95… drh 2260 }
8bdea95… drh 2261
bc36fdc… danield 2262 /* A check-out database file could not be found */
8bdea95… drh 2263 return 0;
15a7b1f… drh 2264 }
15a7b1f… drh 2265 int db_open_local(const char *zDbName){
15a7b1f… drh 2266 return db_open_local_v2(zDbName, 0);
a7248d8… drh 2267 }
a7248d8… drh 2268
a7248d8… drh 2269 /*
a7248d8… drh 2270 ** Get the full pathname to the repository database file. The
f9a200f… drh 2271 ** local database (the _FOSSIL_ or .fslckout database) must have already
a7248d8… drh 2272 ** been opened before this routine is called.
a7248d8… drh 2273 */
a7248d8… drh 2274 const char *db_repository_filename(void){
a7248d8… drh 2275 static char *zRepo = 0;
a7248d8… drh 2276 assert( g.localOpen );
a7248d8… drh 2277 assert( g.zLocalRoot );
a7248d8… drh 2278 if( zRepo==0 ){
a7248d8… drh 2279 zRepo = db_lget("repository", 0);
a7248d8… drh 2280 if( zRepo && !file_is_absolute_path(zRepo) ){
4cf8dbe… stephan 2281 char * zFree = zRepo;
a7248d8… drh 2282 zRepo = mprintf("%s%s", g.zLocalRoot, zRepo);
4cf8dbe… stephan 2283 fossil_free(zFree);
f304c56… drh 2284 zFree = zRepo;
f304c56… drh 2285 zRepo = file_canonical_name_dup(zFree);
f304c56… drh 2286 fossil_free(zFree);
a7248d8… drh 2287 }
a7248d8… drh 2288 }
a7248d8… drh 2289 return zRepo;
a7248d8… drh 2290 }
81d7ce3… mistachkin 2291
81d7ce3… mistachkin 2292 /*
8e659df… mistachkin 2293 ** Returns non-zero if support for symlinks is currently enabled.
81d7ce3… mistachkin 2294 */
b727218… drh 2295 int db_allow_symlinks(void){
c840617… andygoth 2296 return g.allowSymlinks;
b727218… drh 2297 }
f741baa… drh 2298
f741baa… drh 2299 /*
bd7f272… danield 2300 ** Return TRUE if the file in the argument seems like it might be an
bd7f272… danield 2301 ** SQLite database file that contains a Fossil repository schema.
bd7f272… danield 2302 */
bd7f272… danield 2303 int db_looks_like_a_repository(const char *zDbName){
bd7f272… danield 2304 sqlite3 *db = 0;
bd7f272… danield 2305 i64 sz;
bd7f272… danield 2306 int rc;
bd7f272… danield 2307 int res = 0;
bd7f272… danield 2308 sqlite3_stmt *pStmt = 0;
bd7f272… danield 2309
bd7f272… danield 2310 sz = file_size(zDbName, ExtFILE);
bd7f272… danield 2311 if( sz<16834 ) return 0;
69145d9… wyoung 2312 db = db_open(zDbName);
69145d9… wyoung 2313 if( !db ) return 0;
69145d9… wyoung 2314 if( !g.zVfsName && sz%512 ) return 0;
275da70… danield 2315 rc = sqlite3_prepare_v2(db,
bd7f272… danield 2316 "SELECT count(*) FROM sqlite_schema"
bd7f272… danield 2317 " WHERE name COLLATE nocase IN"
bd7f272… danield 2318 "('blob','delta','rcvfrom','user','config','mlink','plink');",
bd7f272… danield 2319 -1, &pStmt, 0);
bd7f272… danield 2320 if( rc ) goto is_repo_end;
bd7f272… danield 2321 rc = sqlite3_step(pStmt);
bd7f272… danield 2322 if( rc!=SQLITE_ROW ) goto is_repo_end;
bd7f272… danield 2323 if( sqlite3_column_int(pStmt, 0)!=7 ) goto is_repo_end;
bd7f272… danield 2324 res = 1;
bd7f272… danield 2325
bd7f272… danield 2326 is_repo_end:
bd7f272… danield 2327 sqlite3_finalize(pStmt);
bd7f272… danield 2328 sqlite3_close(db);
bd7f272… danield 2329 return res;
bd7f272… danield 2330 }
bd7f272… danield 2331
bd7f272… danield 2332 /*
bd7f272… danield 2333 ** COMMAND: test-is-repo
bd5f1d6… florian 2334 ** Usage: %fossil test-is-repo FILENAME...
bd5f1d6… florian 2335 **
bd5f1d6… florian 2336 ** Test whether the specified files look like a SQLite database
bd5f1d6… florian 2337 ** containing a Fossil repository schema.
bd7f272… danield 2338 */
bd7f272… danield 2339 void test_is_repo(void){
bd7f272… danield 2340 int i;
bd5f1d6… florian 2341 verify_all_options();
bd7f272… danield 2342 for(i=2; i<g.argc; i++){
bd7f272… danield 2343 fossil_print("%s: %s\n",
bd7f272… danield 2344 db_looks_like_a_repository(g.argv[i]) ? "yes" : " no",
bd7f272… danield 2345 g.argv[i]
bd7f272… danield 2346 );
bd7f272… danield 2347 }
bd7f272… danield 2348 }
bd7f272… danield 2349
bd7f272… danield 2350
bd7f272… danield 2351 /*
a7248d8… drh 2352 zDbName = db_repository_filename();
69145d9… wyoung 2353 if( !db_looks_like_a_repository(zDbName) ){
9df71fe… jan.nijtmans 2354 if( file_access(zDbName, F_OK) ){
796dcfe… drh 2355 #ifdef FOSSIL_ENABLE_JSON
796dcfe… drh 2356 g.json.resultCode = FSL_JSON_E_DB_NOT_FOUND;
796dcfe… drh 2357 #endif
99fcc43… drh 2358 fossil_fatal("repository does not exist or"
69650f2… drh 2359 " is in an unreadable directory: %s", zDbName);
d8ec765… drh 2360 }else if( file_access(zDbName, R_OK) ){
796dcfe… drh 2361 #ifdef FOSSIL_ENABLE_JSON
796dcfe… drh 2362 g.json.resultCode = FSL_JSON_E_DENIED;
796dcfe… drh 2363 #endif
99fcc43… drh 2364 fossil_fatal("read permission denied for repository %s", zDbName);
69650f2… drh 2365 }else{
796dcfe… drh 2366 #ifdef FOSSIL_ENABLE_JSON
796dcfe… drh 2367 g.json.resultCode = FSL_JSON_E_DB_NOT_VALID;
796dcfe… drh 2368 #endif
99fcc43… drh 2369 fossil_fatal("not a valid repository: %s", zDbName);
06aec61… drh 2370 }
06aec61… drh 2371 }
0c9dff6… drh 2372 g.zRepositoryName = fossil_strdup(zDbName);
06aec61… drh 2373 db_open_or_attach(g.zRepositoryName, "repository");
06aec61… drh 2374 g.repositoryOpen = 1;
752ea43… drh 2375 sqlite3_file_control(g.db, "repository", SQLITE_FCNTL_DATA_VERSION,
752ea43… drh 2376 &g.iRepoDataVers);
f741baa… drh 2377
fd9b7bd… drh 2378 /* Cache "allow-symlinks" option, because we'll need it on every stat call */
f741baa… drh 2379 g.allowSymlinks = db_get_boolean("allow-symlinks",0);
f741baa… drh 2380
fd9b7bd… drh 2381 g.zAuxSchema = db_get("aux-schema","");
e92133a… drh 2382 g.eHashPolicy = db_get_int("hash-policy",-1);
e92133a… drh 2383 if( g.eHashPolicy<0 ){
e92133a… drh 2384 g.eHashPolicy = hname_default_policy();
e92133a… drh 2385 db_set_int("hash-policy", g.eHashPolicy, 0);
e92133a… drh 2386 }
fd9b7bd… drh 2387
f8363db… drh 2388 #if 0 /* No longer automatic. Need to run "fossil rebuild" to migrate */
66807d3… drh 2389 /* Make a change to the CHECK constraint on the BLOB table for
66807d3… drh 2390 ** version 2.0 and later.
fd9b7bd… drh 2391 */
fd9b7bd… drh 2392 rebuild_schema_update_2_0(); /* Do the Fossil-2.0 schema updates */
f8363db… drh 2393 #endif
fff37e6… drh 2394
bc36fdc… danield 2395 /* Additional checks that occur when opening the check-out database */
fff37e6… drh 2396 if( g.localOpen ){
fff37e6… drh 2397
fff37e6… drh 2398 /* If the repository database that was just opened has been
e2bdc10… danield 2399 ** replaced by a clone of the same project, with different RID
fff37e6… drh 2400 ** values, then renumber the RID values stored in various tables
bc36fdc… danield 2401 ** of the check-out database, so that the repository and check-out
fff37e6… drh 2402 ** databases align.
fff37e6… drh 2403 */
fff37e6… drh 2404 if( !db_fingerprint_ok() ){
fff37e6… drh 2405 if( find_option("no-rid-adjust",0,0)!=0 ){
fff37e6… drh 2406 /* The --no-rid-adjust command-line option bypasses the RID value
fff37e6… drh 2407 ** updates. Intended for use during debugging, especially to be
fff37e6… drh 2408 ** able to run "fossil sql" after a database swap. */
fff37e6… drh 2409 fossil_print(
fff37e6… drh 2410 "WARNING: repository change detected, but no adjust made.\n"
fff37e6… drh 2411 );
fff37e6… drh 2412 }else if( find_option("rid-renumber-dryrun",0,0)!=0 ){
fff37e6… drh 2413 /* the --rid-renumber-dryrun option shows how RID values would be
fff37e6… drh 2414 ** renumbered, but does not actually perform the renumbering.
fff37e6… drh 2415 ** This is a debugging-only option. */
fff37e6… drh 2416 vfile_rid_renumbering_event(1);
fff37e6… drh 2417 exit(0);
fff37e6… drh 2418 }else{
fff37e6… drh 2419 char *z;
fff37e6… drh 2420 stash_rid_renumbering_event();
fff37e6… drh 2421 vfile_rid_renumbering_event(0);
fff37e6… drh 2422 undo_reset();
fff37e6… drh 2423 bisect_reset();
36d3685… drh 2424 z = db_fingerprint(0, 1);
fff37e6… drh 2425 db_lset("fingerprint", z);
fff37e6… drh 2426 fossil_free(z);
fff37e6… drh 2427 fossil_print(
fff37e6… drh 2428 "WARNING: The repository database has been replaced by a clone.\n"
fff37e6… drh 2429 "Bisect history and undo have been lost.\n"
fff37e6… drh 2430 );
fff37e6… drh 2431 }
fff37e6… drh 2432 }
fff37e6… drh 2433
bc36fdc… danield 2434 /* Make sure the check-out database schema migration of 2019-01-20
fff37e6… drh 2435 ** has occurred.
fff37e6… drh 2436 **
fff37e6… drh 2437 ** The 2019-01-19 migration is the addition of the vmerge.mhash and
fff37e6… drh 2438 ** vfile.mhash columns and making the vmerge.mhash column part of the
fff37e6… drh 2439 ** PRIMARY KEY for vmerge.
fff37e6… drh 2440 */
fff37e6… drh 2441 if( !db_table_has_column("localdb", "vfile", "mhash") ){
fff37e6… drh 2442 db_multi_exec("ALTER TABLE vfile ADD COLUMN mhash;");
fff37e6… drh 2443 db_multi_exec(
fff37e6… drh 2444 "UPDATE vfile"
fff37e6… drh 2445 " SET mhash=(SELECT uuid FROM blob WHERE blob.rid=vfile.mrid)"
fff37e6… drh 2446 " WHERE mrid!=rid;"
fff37e6… drh 2447 );
fff37e6… drh 2448 if( !db_table_has_column("localdb", "vmerge", "mhash") ){
0ea56bb… drh 2449 db_exec_sql("ALTER TABLE vmerge RENAME TO old_vmerge;");
0ea56bb… drh 2450 db_exec_sql(zLocalSchemaVmerge);
918bcfc… jan.nijtmans 2451 db_exec_sql(
fff37e6… drh 2452 "INSERT OR IGNORE INTO vmerge(id,merge,mhash)"
fff37e6… drh 2453 " SELECT id, merge, blob.uuid FROM old_vmerge, blob"
fff37e6… drh 2454 " WHERE old_vmerge.merge=blob.rid;"
fff37e6… drh 2455 "DROP TABLE old_vmerge;"
fff37e6… drh 2456 );
fff37e6… drh 2457 }
fff37e6… drh 2458 }
1b114d2… drh 2459 }
752ea43… drh 2460 }
752ea43… drh 2461
752ea43… drh 2462 /*
752ea43… drh 2463 ** Return true if there have been any changes to the repository
752ea43… drh 2464 ** database since it was opened.
752ea43… drh 2465 **
752ea43… drh 2466 ** Changes to "config" and "localdb" and "temp" do not count.
752ea43… drh 2467 ** This routine only returns true if there have been changes
752ea43… drh 2468 ** to "repository".
752ea43… drh 2469 */
752ea43… drh 2470 int db_repository_has_changed(void){
752ea43… drh 2471 unsigned int v;
752ea43… drh 2472 if( !g.repositoryOpen ) return 0;
752ea43… drh 2473 sqlite3_file_control(g.db, "repository", SQLITE_FCNTL_DATA_VERSION, &v);
918bcfc… jan.nijtmans 2474 return g.iRepoDataVers != v;
c0c3d92… drh 2475
c0c3d92… drh 2476 /*
c0c3d92… drh 2477 ** Flags for the db_find_and_open_repository() function.
c0c3d92… drh 2478 */
c0c3d92… drh 2479 #if INTERFACE
0ac64da… drh 2480 #define OPEN_OK_NOT_FOUND 0x001 /* Do not error out if not found */
0ac64da… drh 2481 #define OPEN_ANY_SCHEMA 0x002 /* Do not error if schema is wrong */
0ac64da… drh 2482 #define OPEN_SUBSTITUTE 0x004 /* Fake in-memory repo if not found */
c0c3d92… drh 2483 #endif
bc36fdc… danield 2484 ** use the repository of the open check-out if there is one.
1b53667… drh 2485 void db_find_and_open_repository(int bFlags, int nArgUsed){
912fce2… mistachkin 2486 const char *zRep = find_repository_option();
1772357… drh 2487 if( zRep && file_isdir(zRep, ExtFILE)==1 ){
36c4551… mistachkin 2488 goto rep_not_found;
36c4551… mistachkin 2489 }
1b53667… drh 2490 if( zRep==0 && nArgUsed && g.argc==nArgUsed+1 ){
1b53667… drh 2491 zRep = g.argv[nArgUsed];
c0c3d92… drh 2492 }
e590547… jan.nijtmans 2493 if( db_open_local(0)==0 ){
a7248d8… drh 2494 zRep = db_repository_filename();
c0c3d92… drh 2495 if( (bFlags & OPEN_ANY_SCHEMA)==0 ) db_verify_schema();
0ac64da… drh 2496 if( bFlags & OPEN_OK_NOT_FOUND ){
0ac64da… drh 2497 /* No errors if the database is not found */
0ac64da… drh 2498 if( bFlags & OPEN_SUBSTITUTE ){
0ac64da… drh 2499 db_create_repository(0);
0ac64da… drh 2500 }
0ac64da… drh 2501 }else{
796dcfe… drh 2502 #ifdef FOSSIL_ENABLE_JSON
796dcfe… drh 2503 g.json.resultCode = FSL_JSON_E_DB_NOT_FOUND;
796dcfe… drh 2504 #endif
e25f55d… drh 2505 if( nArgUsed==0 ){
99fcc43… drh 2506 fossil_fatal("use --repository or -R to specify the repository database");
e25f55d… drh 2507 }else{
99fcc43… drh 2508 fossil_fatal("specify the repository name as a command-line argument");
e25f55d… drh 2509 }
e25f55d… drh 2510 }
0ca3644… drh 2511 }
0ca3644… drh 2512
0ca3644… drh 2513 /*
0ca3644… drh 2514 ** Return TRUE if the schema is out-of-date
0ca3644… drh 2515 */
0ca3644… drh 2516 int db_schema_is_outofdate(void){
5dd8b2d… drh 2517 return strcmp(g.zAuxSchema,AUX_SCHEMA_MIN)<0
5dd8b2d… drh 2518 || strcmp(g.zAuxSchema,AUX_SCHEMA_MAX)>0;
0205148… drh 2519 }
0205148… drh 2520
0205148… drh 2521 /*
0205148… drh 2522 ** Return true if the database is writeable
0205148… drh 2523 */
0205148… drh 2524 int db_is_writeable(const char *zName){
06aec61… drh 2525 return g.db!=0 && !sqlite3_db_readonly(g.db, zName);
c0c3d92… drh 2526 }
c0c3d92… drh 2527
c0c3d92… drh 2528 /*
c0c3d92… drh 2529 ** Verify that the repository schema is correct. If it is not correct,
c0c3d92… drh 2530 ** issue a fatal error and die.
c0c3d92… drh 2531 */
c0c3d92… drh 2532 void db_verify_schema(void){
0ca3644… drh 2533 if( db_schema_is_outofdate() ){
796dcfe… drh 2534 #ifdef FOSSIL_ENABLE_JSON
796dcfe… drh 2535 g.json.resultCode = FSL_JSON_E_DB_NEEDS_REBUILD;
796dcfe… drh 2536 #endif
5dd8b2d… drh 2537 fossil_warning("incorrect repository schema version: "
5dd8b2d… drh 2538 "current repository schema version is \"%s\" "
5dd8b2d… drh 2539 "but need versions between \"%s\" and \"%s\".",
5dd8b2d… drh 2540 g.zAuxSchema, AUX_SCHEMA_MIN, AUX_SCHEMA_MAX);
c0c3d92… drh 2541 fossil_fatal("run \"fossil rebuild\" to fix this problem");
4a19864… drh 2542 }
4a19864… drh 2543 }
0ca3644… drh 2544
4a19864… drh 2545
4a19864… drh 2546 /*
aa3f554… drh 2547 ** COMMAND: test-move-repository
4a19864… drh 2548 **
aa3f554… drh 2549 ** Usage: %fossil test-move-repository PATHNAME
4a19864… drh 2550 **
4a19864… drh 2551 ** Change the location of the repository database on a local check-out.
bc36fdc… danield 2552 ** Use this command to avoid having to close and reopen a check-out
4a19864… drh 2553 ** when relocating the repository database.
4a19864… drh 2554 */
4a19864… drh 2555 void move_repo_cmd(void){
4a19864… drh 2556 Blob repo;
4a19864… drh 2557 char *zRepo;
4a19864… drh 2558 if( g.argc!=3 ){
4a19864… drh 2559 usage("PATHNAME");
4a19864… drh 2560 }
135ed93… drh 2561 file_canonical_name(g.argv[2], &repo, 0);
4a19864… drh 2562 zRepo = blob_str(&repo);
9df71fe… jan.nijtmans 2563 if( file_access(zRepo, F_OK) ){
4a19864… drh 2564 fossil_fatal("no such file: %s", zRepo);
fe453a4… drh 2565 }
e590547… jan.nijtmans 2566 if( db_open_local(zRepo)==0 ){
bc36fdc… danield 2567 fossil_fatal("not in a local check-out");
e590547… jan.nijtmans 2568 return;
e590547… jan.nijtmans 2569 }
06aec61… drh 2570 db_open_or_attach(zRepo, "test_repo");
4a19864… drh 2571 db_lset("repository", blob_str(&repo));
592ee15… drh 2572 db_record_repository_filename(blob_str(&repo));
c2f5dbe… drh 2573 db_close(1);
4a19864… drh 2574
8c22e1b… drh 2575 if( find_repository_option() ){
55e2487… andybradford 2576 fossil_fatal("the \"%s\" command only works from within an open check-out",
8c22e1b… drh 2577 g.argv[1]);
8c22e1b… drh 2578 }
e590547… jan.nijtmans 2579 if( db_open_local(0)==0 ){
bc36fdc… danield 2580 fossil_fatal("current directory is not within an open check-out");
c0c3d92… drh 2581 db_verify_schema();
c2f5dbe… drh 2582 **
c2f5dbe… drh 2583 ** Check for unfinalized statements and report errors if the reportErrors
c2f5dbe… drh 2584 ** argument is true. Ignore unfinalized statements when false.
c2f5dbe… drh 2585 void db_close(int reportErrors){
94d8e9c… drh 2586 sqlite3_stmt *pStmt;
f741baa… drh 2587 sqlite3_set_authorizer(g.db, 0, 0);
b811123… drh 2588 if( g.fSqlStats ){
cb745cc… drh 2589 int cur, hiwtr;
cb745cc… drh 2590 sqlite3_db_status(g.db, SQLITE_DBSTATUS_LOOKASIDE_USED, &cur, &hiwtr, 0);
b811123… drh 2591 fprintf(stderr, "-- LOOKASIDE_USED %10d %10d\n", cur, hiwtr);
b41d0f6… drh 2592 sqlite3_db_status(g.db, SQLITE_DBSTATUS_LOOKASIDE_HIT, &cur, &hiwtr, 0);
b811123… drh 2593 fprintf(stderr, "-- LOOKASIDE_HIT %10d\n", hiwtr);
b41d0f6… drh 2594 sqlite3_db_status(g.db, SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE, &cur,&hiwtr,0);
b811123… drh 2595 fprintf(stderr, "-- LOOKASIDE_MISS_SIZE %10d\n", hiwtr);
b41d0f6… drh 2596 sqlite3_db_status(g.db, SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL, &cur,&hiwtr,0);
b811123… drh 2597 fprintf(stderr, "-- LOOKASIDE_MISS_FULL %10d\n", hiwtr);
cb745cc… drh 2598 sqlite3_db_status(g.db, SQLITE_DBSTATUS_CACHE_USED, &cur, &hiwtr, 0);
b811123… drh 2599 fprintf(stderr, "-- CACHE_USED %10d\n", cur);
cb745cc… drh 2600 sqlite3_db_status(g.db, SQLITE_DBSTATUS_SCHEMA_USED, &cur, &hiwtr, 0);
b811123… drh 2601 fprintf(stderr, "-- SCHEMA_USED %10d\n", cur);
cb745cc… drh 2602 sqlite3_db_status(g.db, SQLITE_DBSTATUS_STMT_USED, &cur, &hiwtr, 0);
b811123… drh 2603 fprintf(stderr, "-- STMT_USED %10d\n", cur);
cb745cc… drh 2604 sqlite3_status(SQLITE_STATUS_MEMORY_USED, &cur, &hiwtr, 0);
b811123… drh 2605 fprintf(stderr, "-- MEMORY_USED %10d %10d\n", cur, hiwtr);
cb745cc… drh 2606 sqlite3_status(SQLITE_STATUS_MALLOC_SIZE, &cur, &hiwtr, 0);
b811123… drh 2607 fprintf(stderr, "-- MALLOC_SIZE %10d\n", hiwtr);
cb745cc… drh 2608 sqlite3_status(SQLITE_STATUS_MALLOC_COUNT, &cur, &hiwtr, 0);
b811123… drh 2609 fprintf(stderr, "-- MALLOC_COUNT %10d %10d\n", cur, hiwtr);
cb745cc… drh 2610 sqlite3_status(SQLITE_STATUS_PAGECACHE_OVERFLOW, &cur, &hiwtr, 0);
b811123… drh 2611 fprintf(stderr, "-- PCACHE_OVFLOW %10d %10d\n", cur, hiwtr);
a537c99… drh 2612 fprintf(stderr, "-- prepared statements %10d\n", db.nPrepare);
791a513… drh 2613 }
a537c99… drh 2614 while( db.pAllStmt ){
a537c99… drh 2615 db_finalize(db.pAllStmt);
cb745cc… drh 2616 }
f741baa… drh 2617 if( db.nBegin ){
f741baa… drh 2618 if( reportErrors ){
f741baa… drh 2619 fossil_warning("Transaction started at %s:%d never commits",
f741baa… drh 2620 db.zStartFile, db.iStartLine);
f741baa… drh 2621 }
f87fb02… drh 2622 db_end_transaction(1);
f87fb02… drh 2623 }
94d8e9c… drh 2624 pStmt = 0;
f741baa… drh 2625 sqlite3_busy_timeout(g.db, 0);
f741baa… drh 2626 g.dbIgnoreErrors++; /* Stop "database locked" warnings */
7815d01… drh 2627 sqlite3_exec(g.db, "PRAGMA optimize", 0, 0, 0);
41956e7… drh 2628 g.dbIgnoreErrors--;
b0d61b0… mistachkin 2629 db_close_config();
bf68103… drh 2630
06aec61… drh 2631 /* If the localdb has a lot of unused free space,
06aec61… drh 2632 ** then VACUUM it as we shut down.
bf68103… drh 2633 */
06aec61… drh 2634 if( db_database_slot("localdb")>=0 ){
06aec61… drh 2635 int nFree = db_int(0, "PRAGMA localdb.freelist_count");
06aec61… drh 2636 int nTotal = db_int(0, "PRAGMA localdb.page_count");
bf68103… drh 2637 if( nFree>nTotal/4 ){
f741baa… drh 2638 db_unprotect(PROTECT_ALL);
06aec61… drh 2639 db_multi_exec("VACUUM localdb;");
f741baa… drh 2640 db_protect_pop();
bf68103… drh 2641 }
bf68103… drh 2642 }
bf68103… drh 2643
b0d61b0… mistachkin 2644 if( g.db ){
a00a140… drh 2645 int rc;
b0d61b0… mistachkin 2646 sqlite3_wal_checkpoint(g.db, 0);
a00a140… drh 2647 rc = sqlite3_close(g.db);
1e79991… drh 2648 if( g.fSqlTrace ) fossil_trace("-- sqlite3_close(%d)\n", rc);
a00a140… drh 2649 if( rc==SQLITE_BUSY && reportErrors ){
a00a140… drh 2650 while( (pStmt = sqlite3_next_stmt(g.db, pStmt))!=0 ){
a00a140… drh 2651 fossil_warning("unfinalized SQL statement: [%s]", sqlite3_sql(pStmt));
a00a140… drh 2652 }
a00a140… drh 2653 }
b0d61b0… mistachkin 2654 g.db = 0;
c2f5dbe… drh 2655 }
06aec61… drh 2656 g.repositoryOpen = 0;
06aec61… drh 2657 g.localOpen = 0;
f741baa… drh 2658 db.bProtectTriggers = 0;
06aec61… drh 2659 assert( g.dbConfig==0 );
06aec61… drh 2660 assert( g.zConfigDbName==0 );
4180dc6… drh 2661 backoffice_run_if_needed();
06aec61… drh 2662 }
06aec61… drh 2663
f525b6d… drh 2664 /*
f525b6d… drh 2665 ** Close the database as quickly as possible without unnecessary processing.
f525b6d… drh 2666 */
f525b6d… drh 2667 void db_panic_close(void){
f525b6d… drh 2668 if( g.db ){
f525b6d… drh 2669 int rc;
f525b6d… drh 2670 sqlite3_wal_checkpoint(g.db, 0);
f525b6d… drh 2671 rc = sqlite3_close(g.db);
f525b6d… drh 2672 if( g.fSqlTrace ) fossil_trace("-- sqlite3_close(%d)\n", rc);
f741baa… drh 2673 db_clear_authorizer();
f525b6d… drh 2674 }
f525b6d… drh 2675 g.db = 0;
f525b6d… drh 2676 g.repositoryOpen = 0;
f525b6d… drh 2677 g.localOpen = 0;
f525b6d… drh 2678 }
f2b6459… stephan 2679 zRepositorySchemaDefaultReports,
0aee050… drh 2680 db_delete_on_failure(zFilename);
bf75ea9… drh 2681 }
bf75ea9… drh 2682
bf75ea9… drh 2683 /*
bf75ea9… drh 2684 ** Create the default user accounts in the USER table.
bf75ea9… drh 2685 */
14c19fb… drh 2686 void db_create_default_users(int setupUserOnly, const char *zDefaultUser){
d86201d… drh 2687 const char *zUser = zDefaultUser;
d86201d… drh 2688 if( zUser==0 ){
d86201d… drh 2689 zUser = db_get("default-user", 0);
d86201d… drh 2690 }
d86201d… drh 2691 if( zUser==0 ){
a0dd51e… mistachkin 2692 zUser = fossil_getenv("FOSSIL_USER");
a0dd51e… mistachkin 2693 }
a0dd51e… mistachkin 2694 if( zUser==0 ){
5715208… drh 2695 zUser = fossil_getenv("USER");
1ad4ae2… rberteig 2696 }
1ad4ae2… rberteig 2697 if( zUser==0 ){
1ad4ae2… rberteig 2698 zUser = fossil_getenv("LOGNAME");
1ad4ae2… rberteig 2699 }
1ad4ae2… rberteig 2700 if( zUser==0 ){
1ad4ae2… rberteig 2701 zUser = fossil_getenv("USERNAME");
bf75ea9… drh 2702 }
bf75ea9… drh 2703 if( zUser==0 ){
bf75ea9… drh 2704 zUser = "root";
bf75ea9… drh 2705 }
f741baa… drh 2706 db_unprotect(PROTECT_USER);
d86201d… drh 2707 db_multi_exec(
d86201d… drh 2708 "INSERT OR IGNORE INTO user(login, info) VALUES(%Q,'')", zUser
d86201d… drh 2709 );
bf75ea9… drh 2710 db_multi_exec(
23a9f9b… drh 2711 "UPDATE user SET cap='s', pw=%Q"
23a9f9b… drh 2712 " WHERE login=%Q", fossil_random_password(10), zUser
bf75ea9… drh 2713 );
0c6ea0d… drh 2714 if( !setupUserOnly ){
0c6ea0d… drh 2715 db_multi_exec(
646c4a6… drh 2716 "INSERT OR IGNORE INTO user(login,pw,cap,info)"
ddabf09… stephan 2717 " VALUES('anonymous',hex(randomblob(8)),'hz','Anon');"
646c4a6… drh 2718 "INSERT OR IGNORE INTO user(login,pw,cap,info)"
68ce0bc… drh 2719 " VALUES('nobody','','gjorz','Nobody');"
646c4a6… drh 2720 "INSERT OR IGNORE INTO user(login,pw,cap,info)"
1274054… drh 2721 " VALUES('developer','','ei','Dev');"
646c4a6… drh 2722 "INSERT OR IGNORE INTO user(login,pw,cap,info)"
623a912… drh 2723 " VALUES('reader','','kptw','Reader');"
0c6ea0d… drh 2724 );
0c6ea0d… drh 2725 }
f741baa… drh 2726 db_protect_pop();
75bcb48… mistachkin 2727 }
75bcb48… mistachkin 2728
75bcb48… mistachkin 2729 /*
75bcb48… mistachkin 2730 ** Return a pointer to a string that contains the RHS of an IN operator
75bcb48… mistachkin 2731 ** that will select CONFIG table names that are in the list of control
75bcb48… mistachkin 2732 ** settings.
75bcb48… mistachkin 2733 */
75bcb48… mistachkin 2734 const char *db_setting_inop_rhs(){
75bcb48… mistachkin 2735 Blob x;
75bcb48… mistachkin 2736 int i;
f74f701… drh 2737 int nSetting;
f74f701… drh 2738 const Setting *aSetting = setting_info(&nSetting);
75bcb48… mistachkin 2739 const char *zSep = "";
75bcb48… mistachkin 2740
75bcb48… mistachkin 2741 blob_zero(&x);
49b0ff1… drh 2742 blob_append_sql(&x, "(");
f74f701… drh 2743 for(i=0; i<nSetting; i++){
32f8da0… drh 2744 blob_append_sql(&x, "%s%Q", zSep/*safe-for-%s*/, aSetting[i].name);
75bcb48… mistachkin 2745 zSep = ",";
75bcb48… mistachkin 2746 }
49b0ff1… drh 2747 blob_append_sql(&x, ")");
49b0ff1… drh 2748 return blob_sql_text(&x);
75bcb48… mistachkin 2749 ** The zTemplate parameter determines if the settings for the repository
75bcb48… mistachkin 2750 ** should be copied from another repository. If zTemplate is 0 then the
75bcb48… mistachkin 2751 ** settings will have their normal default values. If zTemplate is
75bcb48… mistachkin 2752 ** non-zero, it is assumed that the caller of this function has already
75bcb48… mistachkin 2753 ** attached a database using the label "settingSrc". If not, the call to
75bcb48… mistachkin 2754 ** this function will fail.
75bcb48… mistachkin 2755 **
8b630bb… drh 2756 ** The zInitialDate parameter determines the date of the initial check-in
8b630bb… drh 2757 ** that is automatically created. If zInitialDate is 0 then no initial
8b630bb… drh 2758 ** check-in is created. The makeServerCodes flag determines whether or
7c2577b… drh 2759 void db_initial_setup(
75bcb48… mistachkin 2760 const char *zTemplate, /* Repository from which to copy settings. */
7c2577b… drh 2761 const char *zInitialDate, /* Initial date of repository. (ex: "now") */
a6e2ceb… drh 2762 const char *zDefaultUser /* Default user for the repository */
7c2577b… drh 2763 ){
f741baa… drh 2764 db_unprotect(PROTECT_ALL);
5dd8b2d… drh 2765 db_set("aux-schema", AUX_SCHEMA_MAX, 0);
c0242ad… mistachkin 2766 db_set("rebuilt", get_version(), 0);
a6e2ceb… drh 2767 db_multi_exec(
a6e2ceb… drh 2768 "INSERT INTO config(name,value,mtime)"
a6e2ceb… drh 2769 " VALUES('server-code', lower(hex(randomblob(20))),now());"
a6e2ceb… drh 2770 "INSERT INTO config(name,value,mtime)"
a6e2ceb… drh 2771 " VALUES('project-code', lower(hex(randomblob(20))),now());"
a6e2ceb… drh 2772 );
14c19fb… drh 2773 db_create_default_users(0, zDefaultUser);
4eb637f… drh 2774 if( zDefaultUser ) g.zLogin = zDefaultUser;
75bcb48… mistachkin 2775
75bcb48… mistachkin 2776 if( zTemplate ){
75bcb48… mistachkin 2777 /*
75bcb48… mistachkin 2778 ** Copy all settings from the supplied template repository.
75bcb48… mistachkin 2779 */
75bcb48… mistachkin 2780 db_multi_exec(
75bcb48… mistachkin 2781 "INSERT OR REPLACE INTO config"
75bcb48… mistachkin 2782 " SELECT name,value,mtime FROM settingSrc.config"
3d36a37… drh 2783 " WHERE (name IN %s OR name IN %s OR name GLOB 'walias:/*')"
8444369… mistachkin 2784 " AND name NOT GLOB 'project-*'"
8444369… mistachkin 2785 " AND name NOT GLOB 'short-project-*';",
75bcb48… mistachkin 2786 configure_inop_rhs(CONFIGSET_ALL),
75bcb48… mistachkin 2787 db_setting_inop_rhs()
75bcb48… mistachkin 2788 );
e92133a… drh 2789 g.eHashPolicy = db_get_int("hash-policy", g.eHashPolicy);
75bcb48… mistachkin 2790 db_multi_exec(
75bcb48… mistachkin 2791 "REPLACE INTO reportfmt SELECT * FROM settingSrc.reportfmt;"
75bcb48… mistachkin 2792 );
75bcb48… mistachkin 2793
75bcb48… mistachkin 2794 /*
75bcb48… mistachkin 2795 ** Copy the user permissions, contact information, last modified
75bcb48… mistachkin 2796 ** time, and photo for all the "system" users from the supplied
75bcb48… mistachkin 2797 ** template repository into the one being setup. The other columns
75bcb48… mistachkin 2798 ** are not copied because they contain security information or other
75bcb48… mistachkin 2799 ** data specific to the other repository. The list of columns copied
75bcb48… mistachkin 2800 ** by this SQL statement may need to be revised in the future.
75bcb48… mistachkin 2801 */
75bcb48… mistachkin 2802 db_multi_exec("UPDATE user SET"
75bcb48… mistachkin 2803 " cap = (SELECT u2.cap FROM settingSrc.user u2"
75bcb48… mistachkin 2804 " WHERE u2.login = user.login),"
75bcb48… mistachkin 2805 " info = (SELECT u2.info FROM settingSrc.user u2"
75bcb48… mistachkin 2806 " WHERE u2.login = user.login),"
75bcb48… mistachkin 2807 " mtime = (SELECT u2.mtime FROM settingSrc.user u2"
75bcb48… mistachkin 2808 " WHERE u2.login = user.login),"
75bcb48… mistachkin 2809 " photo = (SELECT u2.photo FROM settingSrc.user u2"
75bcb48… mistachkin 2810 " WHERE u2.login = user.login)"
75bcb48… mistachkin 2811 " WHERE user.login IN ('anonymous','nobody','developer','reader');"
75bcb48… mistachkin 2812 );
75bcb48… mistachkin 2813 }
f741baa… drh 2814 db_protect_pop();
8b630bb… drh 2815
8b630bb… drh 2816 if( zInitialDate ){
54e7410… drh 2817 int rid;
0139767… drh 2818 blob_appendf(&manifest, "C initial\\sempty\\scheck-in\n");
e024877… drh 2819 zDate = date_in_standard_format(zInitialDate);
4bf2708… jan.nijtmans 2820 /* The R-card is necessary here because without it
4bf2708… jan.nijtmans 2821 * fossil versions earlier than versions 1.27 would
4bf2708… jan.nijtmans 2822 * interpret this artifact as a "control". */
42c2a18… drh 2823 blob_appendf(&manifest, "T *branch * trunk\n");
0139767… drh 2824 blob_appendf(&manifest, "T *sym-trunk *\n");
1b45161… drh 2825 rid = content_put(&manifest);
1311841… jan.nijtmans 2826 manifest_crosslink(rid, &manifest, MC_NONE);
db70849… danield 2827 ** COMMAND: new#
21880ca… drh 2828 ** COMMAND: init
14c19fb… drh 2829 ** Usage: %fossil new ?OPTIONS? FILENAME
8bfd995… rberteig 2830 ** or: %fossil init ?OPTIONS? FILENAME
6b85fd1… drh 2831 **
14c19fb… drh 2832 **
14c19fb… drh 2833 ** By default, your current login name is used to create the default
14c19fb… drh 2834 ** admin user. This can be overridden using the -A|--admin-user
14c19fb… drh 2835 ** parameter.
14c19fb… drh 2836 **
75bcb48… mistachkin 2837 ** By default, all settings will be initialized to their default values.
75bcb48… mistachkin 2838 ** This can be overridden using the --template parameter to specify a
75bcb48… mistachkin 2839 ** repository file from which to copy the initial settings. When a template
75bcb48… mistachkin 2840 ** repository is used, almost all of the settings accessible from the setup
75bcb48… mistachkin 2841 ** page, either directly or indirectly, will be copied. Normal users and
75bcb48… mistachkin 2842 ** their associated permissions will not be copied; however, the system
75bcb48… mistachkin 2843 ** default users "anonymous", "nobody", "reader", "developer", and their
cb3421c… tsbg 2844 ** associated permissions will be copied. In case of SQL errors, rebuild the
cb3421c… tsbg 2845 ** template repository and try again.
75bcb48… mistachkin 2846 **
14c19fb… drh 2847 ** Options:
e92133a… drh 2848 ** --template FILE Copy settings from repository file
7f3c728… jamsek 2849 ** -A|--admin-user USERNAME Select given USERNAME as admin user
e92133a… drh 2850 ** --date-override DATETIME Use DATETIME as time of the initial check-in
161309b… andygoth 2851 ** --sha1 Use an initial hash policy of "sha1"
f3adbd8… drh 2852 ** --project-name STRING The name of the project "project name in
f3adbd8… drh 2853 ** quotes"
f3adbd8… drh 2854 ** --project-desc STRING The description of the project "project
f3adbd8… drh 2855 ** description in quotes"
14c19fb… drh 2856 **
cd77e06… rberteig 2857 ** DATETIME may be "now" or "YYYY-MM-DDTHH:MM:SS.SSS". If in
cd77e06… rberteig 2858 ** year-month-day form, it may be truncated, the "T" may be replaced by
cd77e06… rberteig 2859 ** a space, and it may also name a timezone offset from UTC as "-HH:MM"
cd77e06… rberteig 2860 ** (westward) or "+HH:MM" (eastward). Either no timezone suffix or "Z"
cd77e06… rberteig 2861 ** means UTC.
cd77e06… rberteig 2862 **
71992d0… drh 2863 ** See also: [[clone]]
0c6ea0d… drh 2864 char *zPassword;
75bcb48… mistachkin 2865 const char *zTemplate; /* Repository from which to copy settings */
8b630bb… drh 2866 const char *zDate; /* Date of the initial check-in */
14c19fb… drh 2867 const char *zDefaultUser; /* Optional name of the default user */
9d98d86… rdb 2868 const char *zProjectName; /* Optional project name of the repo */
f3adbd8… drh 2869 const char *zProjectDesc; /* Optional project description "description
f3adbd8… drh 2870 ** of project in quotes" */
e92133a… drh 2871 int bUseSha1 = 0; /* True to set the hash-policy to sha1 */
9a6256c… jan.nijtmans 2872
75bcb48… mistachkin 2873
75bcb48… mistachkin 2874 zTemplate = find_option("template",0,1);
8b630bb… drh 2875 zDate = find_option("date-override",0,1);
14c19fb… drh 2876 zDefaultUser = find_option("admin-user","A",1);
e92133a… drh 2877 bUseSha1 = find_option("sha1",0,0)!=0;
9d98d86… rdb 2878 zProjectName = find_option("project-name", 0, 1);
9d98d86… rdb 2879 zProjectDesc = find_option("project-desc", 0, 1);
74ac0c9… drh 2880 /* We should be done with options.. */
74ac0c9… drh 2881 verify_all_options();
74ac0c9… drh 2882
e1ee31a… jan.nijtmans 2883
1772357… drh 2884 if( -1 != file_size(g.argv[2], ExtFILE) ){
89c17a8… bch 2885 fossil_fatal("file already exists: %s", g.argv[2]);
89c17a8… bch 2886 }
89c17a8… bch 2887
b06cd63… mistachkin 2888 db_open_config(0, 0);
75bcb48… mistachkin 2889 if( zTemplate ) db_attach(zTemplate, "settingSrc");
e1962ef… drh 2890 db_begin_transaction();
e92133a… drh 2891 if( bUseSha1 ){
e92133a… drh 2892 g.eHashPolicy = HPOLICY_SHA1;
e92133a… drh 2893 db_set_int("hash-policy", HPOLICY_SHA1, 0);
9a6256c… jan.nijtmans 2894 }
9769c4f… rdb 2895 if( zProjectName ) db_set("project-name", zProjectName, 0);
9769c4f… rdb 2896 if( zProjectDesc ) db_set("project-description", zProjectDesc, 0);
620783c… drh 2897 if( zDate==0 ) zDate = "now";
a6e2ceb… drh 2898 db_initial_setup(zTemplate, zDate, zDefaultUser);
75bcb48… mistachkin 2899 if( zTemplate ) db_detach("settingSrc");
9d98d86… rdb 2900 if( zProjectName ) fossil_print("project-name: %s\n", zProjectName);
9d98d86… rdb 2901 if( zProjectDesc ) fossil_print("project-description: %s\n", zProjectDesc);
620783c… drh 2902 fossil_print("project-id: %s\n", db_get("project-code", 0));
620783c… drh 2903 fossil_print("server-id: %s\n", db_get("server-code", 0));
0c6ea0d… drh 2904 zPassword = db_text(0, "SELECT pw FROM user WHERE login=%Q", g.zLogin);
57276a5… stephan 2905 fossil_print("admin-user: %s (initial remote-access password is \"%s\")\n",
0aee050… drh 2906 g.zLogin, zPassword);
593e801… preben 2907 hash_user_password(g.zLogin);
ae1dac8… drh 2908 void db_sql_print(
0aee050… drh 2909 fossil_print("%s%c", sqlite3_value_text(argv[i]), c);
a2c2e3b… jeremy_c 2910 }
a2c2e3b… jeremy_c 2911 }
a2c2e3b… jeremy_c 2912 }
fbb15cc… drh 2913
fbb15cc… drh 2914 /*
fbb15cc… drh 2915 ** Callback for sqlite3_trace_v2();
fbb15cc… drh 2916 */
fbb15cc… drh 2917 int db_sql_trace(unsigned m, void *notUsed, void *pP, void *pX){
f0d8483… drh 2918 sqlite3_stmt *pStmt = (sqlite3_stmt*)pP;
f0d8483… drh 2919 char *zSql;
f0d8483… drh 2920 int n;
f0d8483… drh 2921 const char *zArg = (const char*)pX;
bf9d65b… drh 2922 char zEnd[100];
fbb15cc… drh 2923 if( m & SQLITE_TRACE_CLOSE ){
fbb15cc… drh 2924 /* If we are tracking closes, that means we want to clean up static
fbb15cc… drh 2925 ** prepared statements. */
fbb15cc… drh 2926 while( db.pAllStmt ){
fbb15cc… drh 2927 db_finalize(db.pAllStmt);
fbb15cc… drh 2928 }
fbb15cc… drh 2929 return 0;
fbb15cc… drh 2930 }
f0d8483… drh 2931 if( zArg[0]=='-' ) return 0;
ada305f… drh 2932 if( m & SQLITE_TRACE_PROFILE ){
ada305f… drh 2933 sqlite3_int64 nNano = *(sqlite3_int64*)pX;
ada305f… drh 2934 double rMillisec = 0.000001 * nNano;
f044cf2… drh 2935 int nRun = sqlite3_stmt_status(pStmt, SQLITE_STMTSTATUS_RUN, 0);
f044cf2… drh 2936 int nVmStep = sqlite3_stmt_status(pStmt, SQLITE_STMTSTATUS_VM_STEP, 1);
f044cf2… drh 2937 sqlite3_snprintf(sizeof(zEnd),zEnd," /* %.3fms, %r run, %d vm-steps */\n",
f044cf2… drh 2938 rMillisec, nRun, nVmStep);
ada305f… drh 2939 }else{
ada305f… drh 2940 zEnd[0] = '\n';
ada305f… drh 2941 zEnd[1] = 0;
ada305f… drh 2942 }
f0d8483… drh 2943 zSql = sqlite3_expanded_sql(pStmt);
f0d8483… drh 2944 n = (int)strlen(zSql);
ada305f… drh 2945 fossil_trace("%s%s%s", zSql, (n>0 && zSql[n-1]==';') ? "" : ";", zEnd);
f0d8483… drh 2946 sqlite3_free(zSql);
fd35e33… drh 2947 return 0;
a2c2e3b… jeremy_c 2948 }
a2c2e3b… jeremy_c 2949
a2c2e3b… jeremy_c 2950 /*
a2c2e3b… jeremy_c 2951 ** Implement the user() SQL function. user() takes no arguments and
a2c2e3b… jeremy_c 2952 ** returns the user ID of the current user.
a2c2e3b… jeremy_c 2953 */
f97e1cf… drh 2954 LOCAL void db_sql_user(
a2c2e3b… jeremy_c 2955 sqlite3_context *context,
a2c2e3b… jeremy_c 2956 int argc,
a2c2e3b… jeremy_c 2957 sqlite3_value **argv
a2c2e3b… jeremy_c 2958 ){
a2c2e3b… jeremy_c 2959 if( g.zLogin!=0 ){
a2c2e3b… jeremy_c 2960 sqlite3_result_text(context, g.zLogin, -1, SQLITE_STATIC);
a2c2e3b… jeremy_c 2961
a2c2e3b… jeremy_c 2962 /*
db0c512… drh 2963 ** Implement the cgi() SQL function. cgi() takes an argument which is
dacc694… jan.nijtmans 2964 ** a name of CGI query parameter. The value of that parameter is returned,
db0c512… drh 2965 ** if available. Optional second argument will be returned if the first
19f5527… jeremy_c 2966 ** doesn't exist as a CGI parameter.
19f5527… jeremy_c 2967 */
f97e1cf… drh 2968 LOCAL void db_sql_cgi(sqlite3_context *context, int argc, sqlite3_value **argv){
19f5527… jeremy_c 2969 const char* zP;
19f5527… jeremy_c 2970 if( argc!=1 && argc!=2 ) return;
19f5527… jeremy_c 2971 zP = P((const char*)sqlite3_value_text(argv[0]));
19f5527… jeremy_c 2972 if( zP ){
19f5527… jeremy_c 2973 sqlite3_result_text(context, zP, -1, SQLITE_STATIC);
19f5527… jeremy_c 2974 }else if( argc==2 ){
19f5527… jeremy_c 2975 zP = (const char*)sqlite3_value_text(argv[1]);
19f5527… jeremy_c 2976 if( zP ) sqlite3_result_text(context, zP, -1, SQLITE_TRANSIENT);
19f5527… jeremy_c 2977 }
19f5527… jeremy_c 2978 }
19f5527… jeremy_c 2979
19f5527… jeremy_c 2980 /*
01e4de6… drh 2981 ** SQL function:
01e4de6… drh 2982 **
01e4de6… drh 2983 ** is_selected(id)
01e4de6… drh 2984 ** if_selected(id, X, Y)
01e4de6… drh 2985 **
01e4de6… drh 2986 ** On the commit command, when filenames are specified (in order to do
01e4de6… drh 2987 ** a partial commit) the vfile.id values for the named files are loaded
01e4de6… drh 2988 ** into the g.aCommitFile[] array. This function looks at that array
01e4de6… drh 2989 ** to see if a file is named on the command-line.
01e4de6… drh 2990 **
01e4de6… drh 2991 ** In the first form (1 argument) return TRUE if either no files are
01e4de6… drh 2992 ** named on the command line (g.aCommitFile is NULL meaning that all
01e4de6… drh 2993 ** changes are to be committed) or if id is found in g.aCommitFile[]
01e4de6… drh 2994 ** (meaning that id was named on the command-line).
01e4de6… drh 2995 **
01e4de6… drh 2996 ** In the second form (3 arguments) return argument X if true and Y
5f3a068… drh 2997 ** if false. Except if Y is NULL then always return X.
01e4de6… drh 2998 */
f97e1cf… drh 2999 LOCAL void file_is_selected(
01e4de6… drh 3000 int rc = 0;
01e4de6… drh 3001
01e4de6… drh 3002 assert(argc==1 || argc==3);
01e4de6… drh 3003 rc = 1;
01e4de6… drh 3004 break;
f46fe42… drh 3005 }
f46fe42… drh 3006 }
01e4de6… drh 3007 }else{
01e4de6… drh 3008 rc = 1;
01e4de6… drh 3009 }
01e4de6… drh 3010 if( argc==1 ){
01e4de6… drh 3011 sqlite3_result_int(context, rc);
f46fe42… drh 3012 }else{
01e4de6… drh 3013 assert( argc==3 );
01e4de6… drh 3014 assert( rc==0 || rc==1 );
5f3a068… drh 3015 if( sqlite3_value_type(argv[2-rc])==SQLITE_NULL ) rc = 1-rc;
01e4de6… drh 3016 sqlite3_result_value(context, argv[2-rc]);
01e4de6… drh 3017 }
01e4de6… drh 3018 }
01e4de6… drh 3019
01e4de6… drh 3020 /*
d0a8582… stephan 3021 ** Implementation of the "win_reserved(X)" SQL function, a wrapper
d0a8582… stephan 3022 ** for file_is_win_reserved(X) which returns true if X is
d0a8582… stephan 3023 ** a Windows-reserved filename.
d0a8582… stephan 3024 */
d0a8582… stephan 3025 LOCAL void db_win_reserved_func(
d0a8582… stephan 3026 sqlite3_context *context,
d0a8582… stephan 3027 int argc,
d0a8582… stephan 3028 sqlite3_value **argv
d0a8582… stephan 3029 ){
d0a8582… stephan 3030 const char * zName = (const char *)sqlite3_value_text(argv[0]);
d0a8582… stephan 3031 if( zName!=0 ){
d0a8582… stephan 3032 sqlite3_result_int(context, file_is_win_reserved(zName)!=0);
d0a8582… stephan 3033 }
d0a8582… stephan 3034 }
d0a8582… stephan 3035
d0a8582… stephan 3036 /*
99a319b… wyoung 3037 ** Convert the input string into an artifact hash. Make a notation in the
f46fe42… drh 3038 ** CONCEALED table so that the hash can be undo using the db_reveal()
f46fe42… drh 3039 ** function at some later time.
f46fe42… drh 3040 **
f46fe42… drh 3041 ** The value returned is stored in static space and will be overwritten
f46fe42… drh 3042 ** on subsequent calls.
6b0b57a… drh 3043 **
fd9b7bd… drh 3044 ** If zContent is already a well-formed artifact hash, then return a copy
6b0b57a… drh 3045 ** of that hash, not a hash of the hash.
6b0b57a… drh 3046 **
6b0b57a… drh 3047 ** The CONCEALED table is meant to obscure email addresses. Every valid
6b0b57a… drh 3048 ** email address will contain a "@" character and "@" is not valid within
fd9b7bd… drh 3049 ** a SHA1 hash so there is no chance that a valid email address will go
6b0b57a… drh 3050 ** unconcealed.
f46fe42… drh 3051 */
f46fe42… drh 3052 char *db_conceal(const char *zContent, int n){
fd9b7bd… drh 3053 static char zHash[HNAME_MAX+1];
f46fe42… drh 3054 Blob out;
fd9b7bd… drh 3055 if( hname_validate(zContent, n) ){
6b0b57a… drh 3056 memcpy(zHash, zContent, n);
6b0b57a… drh 3057 zHash[n] = 0;
6b0b57a… drh 3058 }else{
6b0b57a… drh 3059 sha1sum_step_text(zContent, n);
6b0b57a… drh 3060 sha1sum_finish(&out);
bbbb35a… drh 3061 sqlite3_snprintf(sizeof(zHash), zHash, "%s", blob_str(&out));
6b0b57a… drh 3062 blob_reset(&out);
6b0b57a… drh 3063 db_multi_exec(
1654456… drh 3064 "INSERT OR IGNORE INTO concealed(hash,content,mtime)"
1654456… drh 3065 " VALUES(%Q,%#Q,now())",
6b0b57a… drh 3066 zHash, n, zContent
6b0b57a… drh 3067 );
6b0b57a… drh 3068 }
f46fe42… drh 3069 return zHash;
f46fe42… drh 3070 }
f46fe42… drh 3071
f46fe42… drh 3072 /*
f46fe42… drh 3073 ** Attempt to look up the input in the CONCEALED table. If found,
f46fe42… drh 3074 ** and if the okRdAddr permission is enabled then return the
f46fe42… drh 3075 ** original value for which the input is a hash. If okRdAddr is
6b0b57a… drh 3076 ** false or if the lookup fails, return the original string content.
f46fe42… drh 3077 **
f46fe42… drh 3078 ** In either case, the string returned is stored in space obtained
f46fe42… drh 3079 ** from malloc and should be freed by the calling function.
f46fe42… drh 3080 */
f46fe42… drh 3081 char *db_reveal(const char *zKey){
f46fe42… drh 3082 char *zOut;
b344d3c… drh 3083 if( g.perm.RdAddr ){
f46fe42… drh 3084 zOut = db_text(0, "SELECT content FROM concealed WHERE hash=%Q", zKey);
f46fe42… drh 3085 }else{
f46fe42… drh 3086 zOut = 0;
f46fe42… drh 3087 }
f46fe42… drh 3088 if( zOut==0 ){
0c9dff6… drh 3089 zOut = fossil_strdup_nn(zKey);
f46fe42… drh 3090 }
f46fe42… drh 3091 return zOut;
676fdd0… drh 3092 }
676fdd0… drh 3093
676fdd0… drh 3094 /*
676fdd0… drh 3095 ** Return true if the string zVal represents "true" (or "false").
676fdd0… drh 3096 */
676fdd0… drh 3097 int is_truth(const char *zVal){
9cc36d2… drh 3098 static const char *const azOn[] = { "on", "yes", "true" };
676fdd0… drh 3099 int i;
beb91c9… jan.nijtmans 3100 for(i=0; i<count(azOn); i++){
874d0ca… drh 3101 if( fossil_stricmp(zVal,azOn[i])==0 ) return 1;
676fdd0… drh 3102 }
9cc36d2… drh 3103 return atoi(zVal);
676fdd0… drh 3104 }
676fdd0… drh 3105 int is_false(const char *zVal){
6032dd5… jan.nijtmans 3106 static const char *const azOff[] = { "off", "no", "false", "0" };
676fdd0… drh 3107 int i;
beb91c9… jan.nijtmans 3108 for(i=0; i<count(azOff); i++){
874d0ca… drh 3109 if( fossil_stricmp(zVal,azOff[i])==0 ) return 1;
676fdd0… drh 3110 }
676fdd0… drh 3111 return 0;
676fdd0… drh 3112 }
676fdd0… drh 3113
676fdd0… drh 3114 /*
00ac794… drh 3115 ** Swap the g.db and g.dbConfig connections so that the various db_* routines
4645e9b… drh 3116 ** work on the configuration database instead of on the repository database.
00ac794… drh 3117 ** Be sure to swap them back after doing the operation.
00ac794… drh 3118 **
4645e9b… drh 3119 ** If the configuration database has already been opened as the main database
4645e9b… drh 3120 ** or is attached to the main database, no connection swaps are required so
4645e9b… drh 3121 ** this routine is a no-op.
00ac794… drh 3122 */
00ac794… drh 3123 void db_swap_connections(void){
fe453a4… drh 3124 /*
fe453a4… drh 3125 ** When swapping the main database connection with the config database
fe453a4… drh 3126 ** connection, the config database connection must be open (not simply
fe453a4… drh 3127 ** attached); otherwise, the swap would end up leaving the main database
fe453a4… drh 3128 ** connection invalid, defeating the very purpose of this routine. This
fe453a4… drh 3129 ** same constraint also holds true when restoring the previously swapped
fe453a4… drh 3130 ** database connection; otherwise, it means that no swap was performed
fe453a4… drh 3131 ** because the main database connection was already pointing to the config
fe453a4… drh 3132 ** database.
fe453a4… drh 3133 */
fe453a4… drh 3134 if( g.dbConfig ){
00ac794… drh 3135 sqlite3 *dbTemp = g.db;
00ac794… drh 3136 g.db = g.dbConfig;
00ac794… drh 3137 g.dbConfig = dbTemp;
00ac794… drh 3138 }
00ac794… drh 3139 }
00ac794… drh 3140
00ac794… drh 3141 /*
32f8da0… drh 3142 ** Try to read a versioned setting string from .fossil-settings/<name>.
32f8da0… drh 3143 **
32f8da0… drh 3144 ** Return the text of the string if it is found. Return NULL if not
32f8da0… drh 3145 ** found.
32f8da0… drh 3146 **
32f8da0… drh 3147 ** If the zNonVersionedSetting parameter is not NULL then it holds the
281c67c… andybradford 3148 ** non-versioned value for this setting. If both a versioned and a
32f8da0… drh 3149 ** non-versioned value exist and are not equal, then a warning message
32f8da0… drh 3150 ** might be generated.
dfc0f1b… drh 3151 **
dfc0f1b… drh 3152 ** zCkin is normally NULL. In that case, the versioned setting is
dfc0f1b… drh 3153 ** take from the local check-out, if a local checkout exists, or from
e2bdc10… danield 3154 ** check-in named by the g.zOpenRevision global variable. If zCkin is
e2bdc10… danield 3155 ** not NULL, then zCkin is the name of the specific check-in from which
dfc0f1b… drh 3156 ** versioned setting value is taken. When zCkin is not NULL, the cache
dfc0f1b… drh 3157 ** is bypassed.
9a0c995… drh 3158 */
dfc0f1b… drh 3159 char *db_get_versioned(
dfc0f1b… drh 3160 const char *zName,
dfc0f1b… drh 3161 char *zNonVersionedSetting,
dfc0f1b… drh 3162 const char *zCkin
dfc0f1b… drh 3163 ){
9a0c995… drh 3164 char *zVersionedSetting = 0;
9a0c995… drh 3165 int noWarn = 0;
010451e… andygoth 3166 int found = 0;
ea51d12… drh 3167 struct _cacheEntry {
ea51d12… drh 3168 struct _cacheEntry *next;
ea51d12… drh 3169 const char *zName, *zValue;
ea51d12… drh 3170 } *cacheEntry = 0;
ea51d12… drh 3171 static struct _cacheEntry *cache = 0;
dacc694… jan.nijtmans 3172
dfc0f1b… drh 3173 if( !g.localOpen && g.zOpenRevision==0 && zCkin==0 ){
dfc0f1b… drh 3174 return zNonVersionedSetting;
dfc0f1b… drh 3175 }
dfc0f1b… drh 3176
ea51d12… drh 3177 /* Look up name in cache */
dfc0f1b… drh 3178 if( zCkin==0 ){
dfc0f1b… drh 3179 cacheEntry = cache;
dfc0f1b… drh 3180 while( cacheEntry!=0 ){
dfc0f1b… drh 3181 if( fossil_strcmp(cacheEntry->zName, zName)==0 ){
dfc0f1b… drh 3182 zVersionedSetting = fossil_strdup(cacheEntry->zValue);
dfc0f1b… drh 3183 break;
dfc0f1b… drh 3184 }
dfc0f1b… drh 3185 cacheEntry = cacheEntry->next;
dfc0f1b… drh 3186 }
dfc0f1b… drh 3187 }
dfc0f1b… drh 3188
bc36fdc… danield 3189 /* Attempt to read value from file in check-out if there wasn't a cache hit.*/
a2cc6bc… drh 3190 if( cacheEntry==0 ){
9a0c995… drh 3191 Blob versionedPathname;
010451e… andygoth 3192 Blob setting;
dfc0f1b… drh 3193 blob_init(&versionedPathname, 0, 0);
dfc0f1b… drh 3194 blob_init(&setting, 0, 0);
dfc0f1b… drh 3195 if( !g.localOpen || zCkin!=0 ){
dfc0f1b… drh 3196 /* Repository is in the process of being opened, but files have not been
dfc0f1b… drh 3197 * written to disk. Load from the database. */
dfc0f1b… drh 3198 blob_appendf(&versionedPathname, ".fossil-settings/%s", zName);
dfc0f1b… drh 3199 if( historical_blob(zCkin ? zCkin : g.zOpenRevision,
dfc0f1b… drh 3200 blob_str(&versionedPathname),
dfc0f1b… drh 3201 &setting, 0)
dfc0f1b… drh 3202 ){
dfc0f1b… drh 3203 found = 1;
dfc0f1b… drh 3204 }
dfc0f1b… drh 3205 }else{
dfc0f1b… drh 3206 blob_appendf(&versionedPathname, "%s.fossil-settings/%s",
dfc0f1b… drh 3207 g.zLocalRoot, zName);
dfc0f1b… drh 3208 if( file_size(blob_str(&versionedPathname), ExtFILE)>=0 ){
dfc0f1b… drh 3209 /* File exists, and contains the value for this setting. Load from
dfc0f1b… drh 3210 ** the file. */
dfc0f1b… drh 3211 const char *zFile = blob_str(&versionedPathname);
dfc0f1b… drh 3212 if( blob_read_from_file(&setting, zFile, ExtFILE)>=0 ){
dfc0f1b… drh 3213 found = 1;
dfc0f1b… drh 3214 }
dfc0f1b… drh 3215 /* See if there's a no-warn flag */
dfc0f1b… drh 3216 blob_append(&versionedPathname, ".no-warn", -1);
dfc0f1b… drh 3217 if( file_size(blob_str(&versionedPathname), ExtFILE)>=0 ){
dfc0f1b… drh 3218 noWarn = 1;
dfc0f1b… drh 3219 }
9a0c995… drh 3220 }
9a0c995… drh 3221 }
9a0c995… drh 3222 blob_reset(&versionedPathname);
010451e… andygoth 3223 if( found ){
ef633d4… danield 3224 blob_strip_comment_lines(&setting, &setting);
010451e… andygoth 3225 blob_trim(&setting); /* Avoid non-obvious problems with line endings
010451e… andygoth 3226 ** on boolean properties */
010451e… andygoth 3227 zVersionedSetting = fossil_strdup(blob_str(&setting));
010451e… andygoth 3228 }
010451e… andygoth 3229 blob_reset(&setting);
dfc0f1b… drh 3230
ea51d12… drh 3231 /* Store result in cache, which can be the value or 0 if not found */
dfc0f1b… drh 3232 if( zCkin==0 ){
dfc0f1b… drh 3233 cacheEntry = (struct _cacheEntry*)fossil_malloc(sizeof(*cacheEntry));
dfc0f1b… drh 3234 cacheEntry->next = cache;
dfc0f1b… drh 3235 cacheEntry->zName = zName;
dfc0f1b… drh 3236 cacheEntry->zValue = fossil_strdup(zVersionedSetting);
dfc0f1b… drh 3237 cache = cacheEntry;
dfc0f1b… drh 3238 }
9a0c995… drh 3239 }
dfc0f1b… drh 3240
9a0c995… drh 3241 /* Display a warning? */
dfc0f1b… drh 3242 if( zVersionedSetting!=0
dfc0f1b… drh 3243 && zNonVersionedSetting!=0
dfc0f1b… drh 3244 && zNonVersionedSetting[0]!='\0'
dfc0f1b… drh 3245 && zCkin==0
dfc0f1b… drh 3246 && !noWarn
9a0c995… drh 3247 ){
9a0c995… drh 3248 /* There's a versioned setting, and a non-versioned setting. Tell
9a0c995… drh 3249 ** the user about the conflict */
9a0c995… drh 3250 fossil_warning(
9a0c995… drh 3251 "setting %s has both versioned and non-versioned values: using "
83fd738… mistachkin 3252 "versioned value from file \"%/.fossil-settings/%s\" (to silence "
83fd738… mistachkin 3253 "this warning, either create an empty file named "
83fd738… mistachkin 3254 "\"%/.fossil-settings/%s.no-warn\" in the check-out root, or delete "
83fd738… mistachkin 3255 "the non-versioned setting with \"fossil unset %s\")", zName,
83fd738… mistachkin 3256 g.zLocalRoot, zName, g.zLocalRoot, zName, zName
9a0c995… drh 3257 );
9a0c995… drh 3258 }
dfc0f1b… drh 3259
9a0c995… drh 3260 /* Prefer the versioned setting */
9a0c995… drh 3261 return ( zVersionedSetting!=0 ) ? zVersionedSetting : zNonVersionedSetting;
9a0c995… drh 3262 }
9a0c995… drh 3263
9a0c995… drh 3264
9a0c995… drh 3265 /*
32f8da0… drh 3266 **
32f8da0… drh 3267 ** If no such variable exists, return zDefault. Or, if zName is the name
32f8da0… drh 3268 ** of a setting, then the zDefault is ignored and the default value of the
32f8da0… drh 3269 ** setting is returned instead. If zName is a versioned setting, then
32f8da0… drh 3270 ** versioned value takes priority.
a6fd491… jan.nijtmans 3271 char *db_get(const char *zName, const char *zDefault){
9a0c995… drh 3272 char *z = 0;
32f8da0… drh 3273 const Setting *pSetting = db_find_setting(zName, 0);
8c35f07… drh 3274 if( g.repositoryOpen ){
f044cf2… drh 3275 static Stmt q1;
f044cf2… drh 3276 const char *zRes;
f044cf2… drh 3277 db_static_prepare(&q1, "SELECT value FROM config WHERE name=$n");
f044cf2… drh 3278 db_bind_text(&q1, "$n", zName);
f044cf2… drh 3279 if( db_step(&q1)==SQLITE_ROW && (zRes = db_column_text(&q1,0))!=0 ){
f044cf2… drh 3280 z = fossil_strdup(zRes);
f044cf2… drh 3281 }
f044cf2… drh 3282 db_reset(&q1);
a5dc533… jan.nijtmans 3283 }
a5dc533… jan.nijtmans 3284 if( z==0 && g.zConfigDbName ){
f044cf2… drh 3285 static Stmt q2;
f044cf2… drh 3286 const char *zRes;
a5dc533… jan.nijtmans 3287 db_swap_connections();
f044cf2… drh 3288 db_static_prepare(&q2, "SELECT value FROM global_config WHERE name=$n");
a5dc533… jan.nijtmans 3289 db_swap_connections();
f044cf2… drh 3290 db_bind_text(&q2, "$n", zName);
f044cf2… drh 3291 if( db_step(&q2)==SQLITE_ROW && (zRes = db_column_text(&q2,0))!=0 ){
f044cf2… drh 3292 z = fossil_strdup(zRes);
f044cf2… drh 3293 }
f044cf2… drh 3294 db_reset(&q2);
a5dc533… jan.nijtmans 3295 }
32f8da0… drh 3296 if( pSetting!=0 && pSetting->versionable ){
8c35f07… drh 3297 /* This is a versionable setting, try and get the info from a
bc36fdc… danield 3298 ** checked-out file */
4cf8dbe… stephan 3299 char * zZ = z;
dfc0f1b… drh 3300 z = db_get_versioned(zName, z, 0);
4cf8dbe… stephan 3301 if(zZ != z){
4cf8dbe… stephan 3302 fossil_free(zZ);
4cf8dbe… stephan 3303 }
c0242ad… mistachkin 3304 }
c0242ad… mistachkin 3305 if( z==0 ){
32f8da0… drh 3306 if( zDefault==0 && pSetting && pSetting->def[0] ){
32f8da0… drh 3307 z = fossil_strdup(pSetting->def);
32f8da0… drh 3308 }else{
868404c… drh 3309 z = fossil_strdup(zDefault);
32f8da0… drh 3310 }
c0242ad… mistachkin 3311 }
c0242ad… mistachkin 3312 return z;
c0242ad… mistachkin 3313 }
275da70… danield 3314 char *db_get_mtime(const char *zName, const char *zFormat,
275da70… danield 3315 const char *zDefault){
5b456cf… jan.nijtmans 3316 char *z = 0;
c0242ad… mistachkin 3317 if( g.repositoryOpen ){
c0242ad… mistachkin 3318 z = db_text(0, "SELECT mtime FROM config WHERE name=%Q", zName);
5b456cf… jan.nijtmans 3319 z = fossil_strdup(zDefault);
c0242ad… mistachkin 3320 }else if( zFormat!=0 ){
c0242ad… mistachkin 3321 z = db_text(0, "SELECT strftime(%Q,%Q,'unixepoch');", zFormat, z);
1f6ae1e… drh 3322 const CmdOrPage *pCmd = 0;
f741baa… drh 3323 db_assert_protection_off_or_not_sensitive(zName);
1f6ae1e… drh 3324 if( zValue!=0 && zValue[0]==0
1f6ae1e… drh 3325 && dispatch_name_search(zName, CMDFLAG_SETTING, &pCmd)==0
1f6ae1e… drh 3326 && (pCmd->eCmdFlags & CMDFLAG_KEEPEMPTY)==0
1f6ae1e… drh 3327 ){
1f6ae1e… drh 3328 /* Changing a setting to an empty string is the same as unsetting it,
1f6ae1e… drh 3329 ** unless that setting has the keep-empty flag. */
1f6ae1e… drh 3330 db_unset(zName/*works-like:"x"*/, globalFlag);
1f6ae1e… drh 3331 return;
1f6ae1e… drh 3332 }
f741baa… drh 3333 db_unprotect(PROTECT_CONFIG);
e1962ef… drh 3334 db_begin_transaction();
00ac794… drh 3335 if( globalFlag ){
00ac794… drh 3336 db_swap_connections();
00ac794… drh 3337 db_multi_exec("REPLACE INTO global_config(name,value) VALUES(%Q,%Q)",
00ac794… drh 3338 zName, zValue);
00ac794… drh 3339 db_swap_connections();
00ac794… drh 3340 }else{
1654456… drh 3341 db_multi_exec("REPLACE INTO config(name,value,mtime) VALUES(%Q,%Q,now())",
00ac794… drh 3342 zName, zValue);
00ac794… drh 3343 }
4182079… drh 3344 if( globalFlag && g.repositoryOpen ){
4182079… drh 3345 db_multi_exec("DELETE FROM config WHERE name=%Q", zName);
4182079… drh 3346 }
4182079… drh 3347 db_end_transaction(0);
f741baa… drh 3348 db_protect_pop();
4182079… drh 3349 }
4182079… drh 3350 void db_unset(const char *zName, int globalFlag){
e1962ef… drh 3351 db_begin_transaction();
f741baa… drh 3352 db_unprotect(PROTECT_CONFIG);
00ac794… drh 3353 if( globalFlag ){
00ac794… drh 3354 db_swap_connections();
74f7f6e… drh 3355 db_multi_exec("DELETE FROM global_config WHERE name=%Q", zName);
00ac794… drh 3356 db_swap_connections();
00ac794… drh 3357 }else{
74f7f6e… drh 3358 db_multi_exec("DELETE FROM config WHERE name=%Q", zName);
00ac794… drh 3359 }
f741baa… drh 3360 db_protect_pop();
00ac794… drh 3361 int rc = 0;
a5dc533… jan.nijtmans 3362 if( g.zConfigDbName ){
00ac794… drh 3363 db_swap_connections();
00ac794… drh 3364 rc = db_exists("SELECT 1 FROM global_config WHERE name=%Q", zName);
00ac794… drh 3365 db_swap_connections();
00ac794… drh 3366 return rc;
f044cf2… drh 3367 static Stmt q;
f044cf2… drh 3368 db_static_prepare(&q, "SELECT value FROM config WHERE name=$n");
f044cf2… drh 3369 db_bind_text(&q, "$n", zName);
f044cf2… drh 3370 db_reset(&q);
a5dc533… jan.nijtmans 3371 if( rc==SQLITE_DONE && g.zConfigDbName ){
f044cf2… drh 3372 static Stmt q2;
00ac794… drh 3373 db_swap_connections();
f044cf2… drh 3374 db_static_prepare(&q2, "SELECT value FROM global_config WHERE name=$n");
00ac794… drh 3375 db_swap_connections();
f044cf2… drh 3376 db_bind_text(&q2, "$n", zName);
f044cf2… drh 3377 if( db_step(&q2)==SQLITE_ROW ){
f044cf2… drh 3378 v = db_column_int(&q2, 0);
f044cf2… drh 3379 }
f044cf2… drh 3380 db_reset(&q2);
f044cf2… drh 3381 }
3ffe893… drh 3382 i64 db_large_file_size(void){
3ffe893… drh 3383 /* Return size of the largest file that is not considered oversized */
3ffe893… drh 3384 return strtoll(db_get("large-file-size","20000000"),0,0);
3ffe893… drh 3385 }
f741baa… drh 3386 db_assert_protection_off_or_not_sensitive(zName);
f741baa… drh 3387 db_unprotect(PROTECT_CONFIG);
00ac794… drh 3388 if( globalFlag ){
00ac794… drh 3389 db_swap_connections();
00ac794… drh 3390 db_multi_exec("REPLACE INTO global_config(name,value) VALUES(%Q,%d)",
00ac794… drh 3391 zName, value);
00ac794… drh 3392 db_swap_connections();
00ac794… drh 3393 }else{
1654456… drh 3394 db_multi_exec("REPLACE INTO config(name,value,mtime) VALUES(%Q,%d,now())",
00ac794… drh 3395 zName, value);
00ac794… drh 3396 }
f741baa… drh 3397 db_protect_pop();
79988f9… drh 3398 if( is_truth(zVal) ){
79988f9… drh 3399 dflt = 1;
79988f9… drh 3400 }else if( is_false(zVal) ){
79988f9… drh 3401 dflt = 0;
79988f9… drh 3402 }
79988f9… drh 3403 fossil_free(zVal);
81d7ce3… mistachkin 3404 return dflt;
81d7ce3… mistachkin 3405 }
81d7ce3… mistachkin 3406 int db_get_versioned_boolean(const char *zName, int dflt){
dfc0f1b… drh 3407 char *zVal = db_get_versioned(zName, 0, 0);
81d7ce3… mistachkin 3408 if( zVal==0 ) return dflt;
676fdd0… drh 3409 if( is_truth(zVal) ) return 1;
676fdd0… drh 3410 if( is_false(zVal) ) return 0;
a6fd491… jan.nijtmans 3411 char *db_lget(const char *zName, const char *zDefault){
a6fd491… jan.nijtmans 3412 return db_text(zDefault,
42f61b6… drh 3413 }
42f61b6… drh 3414 int db_lget_boolean(const char *zName, int dflt){
42f61b6… drh 3415 char *zVal = db_lget(zName, dflt ? "on" : "off");
42f61b6… drh 3416 if( is_truth(zVal) ){
42f61b6… drh 3417 dflt = 1;
42f61b6… drh 3418 }else if( is_false(zVal) ){
42f61b6… drh 3419 dflt = 0;
42f61b6… drh 3420 }
42f61b6… drh 3421 fossil_free(zVal);
42f61b6… drh 3422 return dflt;
0a5d0e1… drh 3423 }
106fe61… drh 3424 }
106fe61… drh 3425
106fe61… drh 3426 /* Va-args versions of db_get(), db_set(), and db_unset()
0a5d0e1… drh 3427 **
0a5d0e1… drh 3428 ** codecheck1.c verifies that the format string for db_set_mprintf()
0a5d0e1… drh 3429 ** and db_unset_mprintf() begins with an ASCII character prefix. We
0a5d0e1… drh 3430 ** don't want that format string to begin with %s or %d as that might
0a5d0e1… drh 3431 ** allow an injection attack to set or overwrite arbitrary settings.
106fe61… drh 3432 */
2fac7df… drh 3433 char *db_get_mprintf(const char *zDefault, const char *zFormat, ...){
106fe61… drh 3434 va_list ap;
106fe61… drh 3435 char *zName;
106fe61… drh 3436 char *zResult;
ad984a2… drh 3437 va_start(ap, zFormat);
106fe61… drh 3438 zName = vmprintf(zFormat, ap);
106fe61… drh 3439 va_end(ap);
106fe61… drh 3440 zResult = db_get(zName, zDefault);
106fe61… drh 3441 fossil_free(zName);
106fe61… drh 3442 return zResult;
106fe61… drh 3443 }
2fac7df… drh 3444 void db_set_mprintf(const char *zNew, int iGlobal, const char *zFormat, ...){
106fe61… drh 3445 va_list ap;
106fe61… drh 3446 char *zName;
ad984a2… drh 3447 va_start(ap, zFormat);
106fe61… drh 3448 zName = vmprintf(zFormat, ap);
106fe61… drh 3449 va_end(ap);
0a5d0e1… drh 3450 db_set(zName/*works-like:"x"*/, zNew, iGlobal);
106fe61… drh 3451 fossil_free(zName);
106fe61… drh 3452 }
2fac7df… drh 3453 void db_unset_mprintf(int iGlobal, const char *zFormat, ...){
106fe61… drh 3454 va_list ap;
106fe61… drh 3455 char *zName;
ad984a2… drh 3456 va_start(ap, zFormat);
106fe61… drh 3457 zName = vmprintf(zFormat, ap);
106fe61… drh 3458 va_end(ap);
0a5d0e1… drh 3459 db_unset(zName/*works-like:"x"*/, iGlobal);
106fe61… drh 3460 fossil_free(zName);
106fe61… drh 3461 }
106fe61… drh 3462
346e457… drh 3463 /*
53754ff… andybradford 3464 ** Get a setting that is tailored to subsystem. The return value is
346e457… drh 3465 ** NULL if the setting does not exist, or a string obtained from mprintf()
346e457… drh 3466 ** if the setting is available.
346e457… drh 3467 **
53754ff… andybradford 3468 ** The actual setting can be a comma-separated list of values of the form:
346e457… drh 3469 **
346e457… drh 3470 ** * VALUE
346e457… drh 3471 ** * SUBSYSTEM=VALUE
346e457… drh 3472 **
346e457… drh 3473 ** A VALUE without the SUBSYSTEM= prefix is the default. This routine
346e457… drh 3474 ** returns the VALUE that with the matching SUBSYSTEM, or the default
346e457… drh 3475 ** VALUE if there is no match.
346e457… drh 3476 */
346e457… drh 3477 char *db_get_for_subsystem(const char *zName, const char *zSubsys){
346e457… drh 3478 int nSubsys;
346e457… drh 3479 char *zToFree = 0;
346e457… drh 3480 char *zCopy;
346e457… drh 3481 char *zNext;
346e457… drh 3482 char *zResult = 0;
346e457… drh 3483 const char *zSetting = db_get(zName, 0);
346e457… drh 3484 if( zSetting==0 ) return 0;
346e457… drh 3485 zCopy = zToFree = fossil_strdup(zSetting);
346e457… drh 3486 if( zSubsys==0 ) zSubsys = "";
346e457… drh 3487 nSubsys = (int)strlen(zSubsys);
346e457… drh 3488 while( zCopy ){
346e457… drh 3489 zNext = strchr(zCopy, ',');
346e457… drh 3490 if( zNext ){
346e457… drh 3491 zNext[0] = 0;
346e457… drh 3492 do{ zNext++; }while( fossil_isspace(zNext[0]) );
346e457… drh 3493 if( zNext[0]==0 ) zNext = 0;
346e457… drh 3494 }
346e457… drh 3495 if( strchr(zCopy,'=')==0 ){
346e457… drh 3496 if( zResult==0 ) zResult = zCopy;
346e457… drh 3497 }else
346e457… drh 3498 if( nSubsys
346e457… drh 3499 && strncmp(zCopy, zSubsys, nSubsys)==0
346e457… drh 3500 && zCopy[nSubsys]=='='
346e457… drh 3501 ){
346e457… drh 3502 zResult = &zCopy[nSubsys+1];
346e457… drh 3503 break;
346e457… drh 3504 }
346e457… drh 3505 zCopy = zNext;
346e457… drh 3506 }
346e457… drh 3507 if( zResult ) zResult = fossil_strdup(zResult);
346e457… drh 3508 fossil_free(zToFree);
346e457… drh 3509 return zResult;
346e457… drh 3510 }
189bfc2… jan 3511
189bfc2… jan 3512 #if INTERFACE
189bfc2… jan 3513 /* Manifest generation flags */
189bfc2… jan 3514 #define MFESTFLG_RAW 0x01
189bfc2… jan 3515 #define MFESTFLG_UUID 0x02
189bfc2… jan 3516 #define MFESTFLG_TAGS 0x04
189bfc2… jan 3517 #endif /* INTERFACE */
189bfc2… jan 3518
189bfc2… jan 3519 /*
189bfc2… jan 3520 ** Get the manifest setting. For backwards compatibility first check if the
189bfc2… jan 3521 ** value is a boolean. If it's not a boolean, treat each character as a flag
189bfc2… jan 3522 ** to enable a manifest type. This system puts certain boundary conditions on
189bfc2… jan 3523 ** which letters can be used to represent flags (any permutation of flags must
189bfc2… jan 3524 ** not be able to fully form one of the boolean values).
dfc0f1b… drh 3525 **
dfc0f1b… drh 3526 ** "manifest" is a versionable setting. But we do not issue a warning
dfc0f1b… drh 3527 ** if there is a conflict. Instead, the value returned is the value for
dfc0f1b… drh 3528 ** the versioned setting if the versioned setting exists, or the ordinary
dfc0f1b… drh 3529 ** setting otherwise.
dfc0f1b… drh 3530 **
dfc0f1b… drh 3531 ** The argument zCkin is the specific check-in for which we want the
dfc0f1b… drh 3532 ** manifest setting.
189bfc2… jan 3533 */
dfc0f1b… drh 3534 int db_get_manifest_setting(const char *zCkin){
189bfc2… jan 3535 int flg;
dfc0f1b… drh 3536 char *zVal;
dfc0f1b… drh 3537
dfc0f1b… drh 3538 /* Look for the versioned setting first */
dfc0f1b… drh 3539 zVal = db_get_versioned("manifest", 0, zCkin);
dfc0f1b… drh 3540
dfc0f1b… drh 3541 if( zVal==0 && g.repositoryOpen ){
dfc0f1b… drh 3542 /* No versioned setting, look for the repository setting second */
dfc0f1b… drh 3543 zVal = db_text(0, "SELECT value FROM config WHERE name='manifest'");
dfc0f1b… drh 3544 }
3604802… mistachkin 3545 if( zVal==0 || is_false(zVal) ){
189bfc2… jan 3546 return 0;
3604802… mistachkin 3547 }else if( is_truth(zVal) ){
189bfc2… jan 3548 return MFESTFLG_RAW|MFESTFLG_UUID;
189bfc2… jan 3549 }
189bfc2… jan 3550 flg = 0;
189bfc2… jan 3551 while( *zVal ){
3604802… mistachkin 3552 switch( *zVal ){
3604802… mistachkin 3553 case 'r': flg |= MFESTFLG_RAW; break;
3604802… mistachkin 3554 case 'u': flg |= MFESTFLG_UUID; break;
3604802… mistachkin 3555 case 't': flg |= MFESTFLG_TAGS; break;
189bfc2… jan 3556 }
189bfc2… jan 3557 zVal++;
189bfc2… jan 3558 }
189bfc2… jan 3559 return flg;
dfc0f1b… drh 3560 }
dfc0f1b… drh 3561
dfc0f1b… drh 3562 /*
dfc0f1b… drh 3563 ** COMMAND: test-manifest-setting
dfc0f1b… drh 3564 **
dfc0f1b… drh 3565 ** Usage: %fossil test-manifest-setting VERSION VERSION ...
dfc0f1b… drh 3566 **
dfc0f1b… drh 3567 ** Display the value for the "manifest" setting for various versions
dfc0f1b… drh 3568 ** of the repository.
dfc0f1b… drh 3569 */
dfc0f1b… drh 3570 void test_manfest_setting_cmd(void){
dfc0f1b… drh 3571 int i;
dfc0f1b… drh 3572 db_find_and_open_repository(0, 0);
dfc0f1b… drh 3573 for(i=2; i<g.argc; i++){
dfc0f1b… drh 3574 int m = db_get_manifest_setting(g.argv[i]);
dfc0f1b… drh 3575 fossil_print("%s:\n", g.argv[i]);
dfc0f1b… drh 3576 fossil_print(" flags = 0x%02x\n", m);
dfc0f1b… drh 3577 if( m & MFESTFLG_RAW ){
dfc0f1b… drh 3578 fossil_print(" manifest\n");
dfc0f1b… drh 3579 }
dfc0f1b… drh 3580 if( m & MFESTFLG_UUID ){
dfc0f1b… drh 3581 fossil_print(" manifest.uuid\n");
dfc0f1b… drh 3582 }
dfc0f1b… drh 3583 if( m & MFESTFLG_TAGS ){
dfc0f1b… drh 3584 fossil_print(" manifest.tags\n");
dfc0f1b… drh 3585 }
dfc0f1b… drh 3586 }
bc36fdc… danield 3587 }
bc36fdc… danield 3588
da52ff9… drh 3589
9346f22… drh 3590 /*
9346f22… drh 3591 ** Record the name of a local repository in the global_config() database.
da52ff9… drh 3592 ** The repository filename %s is recorded as an entry with a "name" field
9346f22… drh 3593 ** of the following form:
9346f22… drh 3594 **
9346f22… drh 3595 ** repo:%s
9346f22… drh 3596 **
9346f22… drh 3597 ** The value field is set to 1.
272e304… drh 3598 **
bc36fdc… danield 3599 ** If running from a local check-out, also record the root of the check-out
272e304… drh 3600 ** as follows:
272e304… drh 3601 **
272e304… drh 3602 ** ckout:%s
272e304… drh 3603 **
bc36fdc… danield 3604 ** Where %s is the check-out root. The value is the repository file.
9346f22… drh 3605 */
9346f22… drh 3606 void db_record_repository_filename(const char *zName){
c6079d1… drh 3607 char *zRepoSetting;
c6079d1… drh 3608 char *zCkoutSetting;
2bd0690… drh 3609 Blob full;
9346f22… drh 3610 if( zName==0 ){
9346f22… drh 3611 if( !g.localOpen ) return;
a7248d8… drh 3612 zName = db_repository_filename();
9346f22… drh 3613 }
135ed93… drh 3614 file_canonical_name(zName, &full, 0);
49b0ff1… drh 3615 (void)filename_collation(); /* Initialize before connection swap */
00ac794… drh 3616 db_swap_connections();
c6079d1… drh 3617 zRepoSetting = mprintf("repo:%q", blob_str(&full));
275da70… danield 3618
f741baa… drh 3619 db_unprotect(PROTECT_CONFIG);
c6079d1… drh 3620 db_multi_exec(
49b0ff1… drh 3621 "DELETE FROM global_config WHERE name %s = %Q;",
49b0ff1… drh 3622 filename_collation(), zRepoSetting
c6079d1… drh 3623 );
9346f22… drh 3624 db_multi_exec(
9346f22… drh 3625 "INSERT OR IGNORE INTO global_config(name,value)"
49b0ff1… drh 3626 "VALUES(%Q,1);",
c6079d1… drh 3627 zRepoSetting
9346f22… drh 3628 );
f741baa… drh 3629 db_protect_pop();
c6079d1… drh 3630 fossil_free(zRepoSetting);
272e304… drh 3631 if( g.localOpen && g.zLocalRoot && g.zLocalRoot[0] ){
135ed93… drh 3632 Blob localRoot;
135ed93… drh 3633 file_canonical_name(g.zLocalRoot, &localRoot, 1);
c6079d1… drh 3634 zCkoutSetting = mprintf("ckout:%q", blob_str(&localRoot));
f741baa… drh 3635 db_unprotect(PROTECT_CONFIG);
c6079d1… drh 3636 db_multi_exec(
49b0ff1… drh 3637 "DELETE FROM global_config WHERE name %s = %Q;",
49b0ff1… drh 3638 filename_collation(), zCkoutSetting
c6079d1… drh 3639 );
272e304… drh 3640 db_multi_exec(
272e304… drh 3641 "REPLACE INTO global_config(name, value)"
49b0ff1… drh 3642 "VALUES(%Q,%Q);",
c6079d1… drh 3643 zCkoutSetting, blob_str(&full)
0205148… drh 3644 );
0205148… drh 3645 db_swap_connections();
dacc694… jan.nijtmans 3646 db_optional_sql("repository",
49b0ff1… drh 3647 "DELETE FROM config WHERE name %s = %Q;",
49b0ff1… drh 3648 filename_collation(), zCkoutSetting
c6079d1… drh 3649 );
c6079d1… drh 3650 db_optional_sql("repository",
0205148… drh 3651 "REPLACE INTO config(name,value,mtime)"
49b0ff1… drh 3652 "VALUES(%Q,1,now());",
c6079d1… drh 3653 zCkoutSetting
272e304… drh 3654 );
f741baa… drh 3655 db_protect_pop();
c6079d1… drh 3656 fossil_free(zCkoutSetting);
135ed93… drh 3657 blob_reset(&localRoot);
0205148… drh 3658 }else{
0205148… drh 3659 db_swap_connections();
272e304… drh 3660 }
2bd0690… drh 3661 blob_reset(&full);
dfc5cee… drh 3662 ** Usage: %fossil open REPOSITORY ?VERSION? ?OPTIONS?
2210be1… drh 3663 **
bc36fdc… danield 3664 ** Open a new connection to the repository name REPOSITORY. A check-out
dfc5cee… drh 3665 ** for the repository is created with its root at the current working
99ab111… drh 3666 ** directory, or in DIR if the "--workdir DIR" is used. If VERSION is
99ab111… drh 3667 ** specified then that version is checked out. Otherwise the most recent
99ab111… drh 3668 ** check-in on the main branch (usually "trunk") is used.
0825520… drh 3669 **
0825520… drh 3670 ** REPOSITORY can be the filename for a repository that already exists on the
0825520… drh 3671 ** local machine or it can be a URI for a remote repository. If REPOSITORY
75c45fd… drh 3672 ** is a URI in one of the formats recognized by the [[clone]] command, the
c965636… drh 3673 ** remote repo is first cloned, then the clone is opened. The clone will be
a31a717… drh 3674 ** stored in the current directory, or in DIR if the "--repodir DIR" option
c965636… drh 3675 ** is used. The name of the clone will be taken from the last term of the URI.
a31a717… drh 3676 ** For "http:" and "https:" URIs, you can append an extra term to the end of
a31a717… drh 3677 ** the URI to get any repository name you like. For example:
0825520… drh 3678 **
0825520… drh 3679 ** fossil open https://fossil-scm.org/home/new-name
0825520… drh 3680 **
c965636… drh 3681 ** The base URI for cloning is "https://fossil-scm.org/home". The extra
c965636… drh 3682 ** "new-name" term means that the cloned repository will be called
c965636… drh 3683 ** "new-name.fossil".
2210be1… drh 3684 **
2210be1… drh 3685 ** Options:
bc36fdc… danield 3686 ** --empty Initialize check-out as being empty, but still connected
bc36fdc… danield 3687 ** with the local repository. If you commit this check-out,
941ead2… andybradford 3688 ** it will become a new "initial" commit in the repository.
17c244d… drh 3689 ** -f|--force Continue with the open even if the working directory is
b2ab66e… florian 3690 ** not empty, or if auto-sync fails.
941ead2… andybradford 3691 ** --force-missing Force opening a repository with missing content
ca2248e… stephan 3692 ** -k|--keep Only modify the manifest file(s)
bc36fdc… danield 3693 ** --nested Allow opening a repository inside an opened check-out
346e457… drh 3694 ** --nosync Do not auto-sync the repository prior to opening even
346e457… drh 3695 ** if the autosync setting is on.
6d4cd32… mgagnon 3696 ** --proxy PROXY Use PROXY as http proxy during sync operation
6f566c8… drh 3697 ** --reopen REPO "Reopen" an existing checkout to use a different
6f566c8… drh 3698 ** repository file REPO. Useful to reconnect an existing
6f566c8… drh 3699 ** checkout to a repository after the original repository
6f566c8… drh 3700 ** file is moved. CAUTION: may lose stash and bisect
6f566c8… drh 3701 ** history.
dfc5cee… drh 3702 ** --repodir DIR If REPOSITORY is a URI that will be cloned, store
dfc5cee… drh 3703 ** the clone in DIR rather than in "."
a7e86f5… drh 3704 ** --setmtime Set timestamps of all files to match their SCM-side
bc36fdc… danield 3705 ** times (the timestamp of the last check-in which modified
a7e86f5… drh 3706 ** them).
bd7f272… danield 3707 ** --verbose If passed a URI then this flag is passed on to the clone
2e56ef4… km 3708 ** operation, otherwise it has no effect
dfc5cee… drh 3709 ** --workdir DIR Use DIR as the working directory instead of ".". The DIR
c965636… drh 3710 ** directory is created if it does not exist.
2210be1… drh 3711 **
71992d0… drh 3712 ** See also: [[close]], [[clone]]
640626f… jan.nijtmans 3713 int emptyFlag;
915bfd9… drh 3714 int keepFlag;
941ead2… andybradford 3715 int forceMissingFlag;
f15943f… drh 3716 int allowNested;
a7e86f5… drh 3717 int setmtimeFlag; /* --setmtime. Set mtimes on files */
99ab111… drh 3718 int bForce = 0; /* --force. Open even if non-empty dir */
bae2e57… andybradford 3719 static char *azNewArgv[] = { 0, "checkout", "--prompt", 0, 0, 0, 0 };
0629d2a… drh 3720 const char *zWorkDir; /* --workdir value */
0629d2a… drh 3721 const char *zRepo = 0; /* Name of the repository file */
dfc5cee… drh 3722 const char *zRepoDir = 0; /* --repodir value */
ae8a45c… stephan 3723 const char *zReopen = 0; /* --reopen REPOFILE */
19677d7… drh 3724 char *zPwd; /* Initial working directory */
19677d7… drh 3725 int isUri = 0; /* True if REPOSITORY is a URI */
61132ce… drh 3726 int nLocal; /* Number of preexisting files in cwd */
bd7f272… danield 3727 int bVerbose = 0; /* --verbose option for clone */
ae8a45c… stephan 3728
ae8a45c… stephan 3729 zReopen = find_option("reopen",0,1);
ae8a45c… stephan 3730 if( 0!=zReopen ){
ae8a45c… stephan 3731 g.argc = 3;
ae8a45c… stephan 3732 g.argv[2] = (char*)zReopen;
ae8a45c… stephan 3733 move_repo_cmd();
ae8a45c… stephan 3734 return;
ae8a45c… stephan 3735 }
f15943f… drh 3736
ec82a32… drh 3737 url_proxy_options();
640626f… jan.nijtmans 3738 emptyFlag = find_option("empty",0,0)!=0;
bbd3d25… danield 3739 keepFlag = find_option("keep","k",0)!=0;
941ead2… andybradford 3740 forceMissingFlag = find_option("force-missing",0,0)!=0;
f15943f… drh 3741 allowNested = find_option("nested",0,0)!=0;
a7e86f5… drh 3742 setmtimeFlag = find_option("setmtime",0,0)!=0;
0629d2a… drh 3743 zWorkDir = find_option("workdir",0,1);
dfc5cee… drh 3744 zRepoDir = find_option("repodir",0,1);
346e457… drh 3745 bForce = find_option("force","f",0)!=0;
346e457… drh 3746 if( find_option("nosync",0,0) ) g.fNoSync = 1;
bd7f272… danield 3747 bVerbose = find_option("verbose",0,0)!=0;
19677d7… drh 3748 zPwd = file_getcwd(0,0);
4e18dba… jan.nijtmans 3749
74ac0c9… drh 3750 /* We should be done with options.. */
74ac0c9… drh 3751 verify_all_options();
74ac0c9… drh 3752
915bfd9… drh 3753 if( g.argc!=3 && g.argc!=4 ){
915bfd9… drh 3754 usage("REPOSITORY-FILENAME ?VERSION?");
915bfd9… drh 3755 }
0629d2a… drh 3756 zRepo = g.argv[2];
dfc5cee… drh 3757 if( sqlite3_strglob("http://*", zRepo)==0
dfc5cee… drh 3758 || sqlite3_strglob("https://*", zRepo)==0
dfc5cee… drh 3759 || sqlite3_strglob("ssh:*", zRepo)==0
dfc5cee… drh 3760 || sqlite3_strglob("file:*", zRepo)==0
dfc5cee… drh 3761 ){
19677d7… drh 3762 isUri = 1;
dfc5cee… drh 3763 }
dfc5cee… drh 3764
dfc5cee… drh 3765 /* If --workdir is specified, change to the requested working directory */
0629d2a… drh 3766 if( zWorkDir ){
19677d7… drh 3767 if( !isUri ){
15a7b1f… drh 3768 zRepo = file_canonical_name_dup(zRepo);
15a7b1f… drh 3769 }
15a7b1f… drh 3770 if( zRepoDir ){
15a7b1f… drh 3771 zRepoDir = file_canonical_name_dup(zRepoDir);
19677d7… drh 3772 }
0629d2a… drh 3773 if( file_isdir(zWorkDir, ExtFILE)!=1 ){
0629d2a… drh 3774 file_mkfolder(zWorkDir, ExtFILE, 0, 0);
0629d2a… drh 3775 if( file_mkdir(zWorkDir, ExtFILE, 0) ){
dfc5cee… drh 3776 fossil_fatal("cannot create directory %s", zWorkDir);
0629d2a… drh 3777 }
0629d2a… drh 3778 }
0629d2a… drh 3779 if( file_chdir(zWorkDir, 0) ){
dfc5cee… drh 3780 fossil_fatal("unable to make %s the working directory", zWorkDir);
dfc5cee… drh 3781 }
99ab111… drh 3782 }
61132ce… drh 3783 if( keepFlag==0
61132ce… drh 3784 && bForce==0
ff7fe39… drh 3785 && (nLocal = file_directory_list(".", 0, 1, 2, 0))>0
61132ce… drh 3786 && (nLocal>1 || isUri || !file_in_cwd(zRepo))
61132ce… drh 3787 ){
99ab111… drh 3788 fossil_fatal("directory %s is not empty\n"
d70ea01… mgagnon 3789 "use the -f (--force) option to override\n"
275da70… danield 3790 "or the -k (--keep) option to keep local files unchanged",
d70ea01… mgagnon 3791 file_getcwd(0,0));
f080538… drh 3792 }
19677d7… drh 3793
15a7b1f… drh 3794 if( db_open_local_v2(0, allowNested) ){
15a7b1f… drh 3795 fossil_fatal("there is already an open tree at %s", g.zLocalRoot);
19677d7… drh 3796 }
19677d7… drh 3797
19677d7… drh 3798 /* If REPOSITORY looks like a URI, then try to clone it first */
19677d7… drh 3799 if( isUri ){
19677d7… drh 3800 char *zNewBase; /* Base name of the cloned repository file */
19677d7… drh 3801 const char *zUri; /* URI to clone */
19677d7… drh 3802 int rc; /* Result code from fossil_system() */
19677d7… drh 3803 Blob cmd; /* Clone command to be run */
19677d7… drh 3804 char *zCmd; /* String version of the clone command */
dfc5cee… drh 3805
19677d7… drh 3806 zUri = zRepo;
f978fcd… drh 3807 zNewBase = url_to_repo_basename(zUri);
f978fcd… drh 3808 if( zNewBase==0 ){
f978fcd… drh 3809 fossil_fatal("unable to deduce a repository name from the url \"%s\"",
f978fcd… drh 3810 zUri);
f978fcd… drh 3811 }
6e04d9c… drh 3812 if( zRepoDir==0 ){
6e04d9c… drh 3813 zRepo = mprintf("%s.fossil", zNewBase);
6e04d9c… drh 3814 }else{
6e04d9c… drh 3815 zRepo = mprintf("%s/%s.fossil", zRepoDir, zNewBase);
6e04d9c… drh 3816 }
19677d7… drh 3817 fossil_free(zNewBase);
19677d7… drh 3818 blob_init(&cmd, 0, 0);
4f83d06… drh 3819 blob_append_escaped_arg(&cmd, g.nameOfExe, 1);
19677d7… drh 3820 blob_append(&cmd, " clone", -1);
bd7f272… danield 3821 if(0!=bVerbose){
bd7f272… danield 3822 blob_append(&cmd, " --verbose", -1);
bd7f272… danield 3823 }
4f83d06… drh 3824 blob_append_escaped_arg(&cmd, zUri, 1);
4f83d06… drh 3825 blob_append_escaped_arg(&cmd, zRepo, 1);
19677d7… drh 3826 zCmd = blob_str(&cmd);
19677d7… drh 3827 fossil_print("%s\n", zCmd);
19677d7… drh 3828 if( zWorkDir ) file_chdir(zPwd, 0);
19677d7… drh 3829 rc = fossil_system(zCmd);
19677d7… drh 3830 if( rc ){
19677d7… drh 3831 fossil_fatal("clone of %s failed", zUri);
19677d7… drh 3832 }
19677d7… drh 3833 blob_reset(&cmd);
19677d7… drh 3834 if( zWorkDir ) file_chdir(zWorkDir, 0);
19677d7… drh 3835 }else if( zRepoDir ){
19677d7… drh 3836 fossil_fatal("the --repodir option only makes sense if the REPOSITORY "
19677d7… drh 3837 "argument is a URI that begins with http:, https:, ssh:, "
19677d7… drh 3838 "or file:");
010451e… andygoth 3839 }
19677d7… drh 3840
24420b4… drh 3841 db_open_config(0,0);
0629d2a… drh 3842 db_open_repository(zRepo);
010451e… andygoth 3843
010451e… andygoth 3844 /* Figure out which revision to open. */
010451e… andygoth 3845 if( !emptyFlag ){
010451e… andygoth 3846 if( g.argc==4 ){
010451e… andygoth 3847 g.zOpenRevision = g.argv[3];
010451e… andygoth 3848 }else if( db_exists("SELECT 1 FROM event WHERE type='ci'") ){
ca0d66b… danield 3849 g.zOpenRevision = fossil_strdup(db_main_branch());
dc97155… drh 3850 }
346e457… drh 3851 if( autosync_loop(SYNC_PULL, !bForce, "open") && !bForce ){
dc97155… drh 3852 fossil_fatal("unable to auto-sync the repository");
010451e… andygoth 3853 }
432306b… drh 3854 }
81d7ce3… mistachkin 3855
010451e… andygoth 3856
8cc71be… jan.nijtmans 3857 #if defined(_WIN32) || defined(__CYGWIN__)
432306b… drh 3858 # define LOCALDB_NAME "./_FOSSIL_"
432306b… drh 3859 #else
432306b… drh 3860 # define LOCALDB_NAME "./.fslckout"
432306b… drh 3861 #endif
fff37e6… drh 3862 db_init_database(LOCALDB_NAME, zLocalSchema, zLocalSchemaVmerge,
8c35f07… drh 3863 #ifdef FOSSIL_LOCAL_WAL
8c35f07… drh 3864 "COMMIT; PRAGMA journal_mode=WAL; BEGIN;",
8c35f07… drh 3865 #endif
8c35f07… drh 3866 (char*)0);
432306b… drh 3867 db_delete_on_failure(LOCALDB_NAME);
e590547… jan.nijtmans 3868 db_open_local(0);
dfc5cee… drh 3869 db_lset("repository", zRepo);
dfc5cee… drh 3870 db_record_repository_filename(zRepo);
f72ef85… andybradford 3871 db_set_checkout(0, 0); /* manifest files handled by checkout_cmd */
6791ad1… jan.nijtmans 3872 azNewArgv[0] = g.argv[0];
6791ad1… jan.nijtmans 3873 g.argv = azNewArgv;
c7f5541… mistachkin 3874 if( !emptyFlag ){
640626f… jan.nijtmans 3875 g.argc = 3;
010451e… andygoth 3876 if( g.zOpenRevision ){
010451e… andygoth 3877 azNewArgv[g.argc-1] = g.zOpenRevision;
640626f… jan.nijtmans 3878 }else{
010451e… andygoth 3879 azNewArgv[g.argc-1] = "--latest";
640626f… jan.nijtmans 3880 }
640626f… jan.nijtmans 3881 if( keepFlag ){
640626f… jan.nijtmans 3882 azNewArgv[g.argc++] = "--keep";
640626f… jan.nijtmans 3883 }
941ead2… andybradford 3884 if( forceMissingFlag ){
941ead2… andybradford 3885 azNewArgv[g.argc++] = "--force-missing";
941ead2… andybradford 3886 }
640626f… jan.nijtmans 3887 checkout_cmd();
941ead2… andybradford 3888 }
a7e86f5… drh 3889 if( setmtimeFlag ){
a7e86f5… drh 3890 int const vid = db_lget_int("checkout", 0);
a7e86f5… drh 3891 if(vid!=0){
a7e86f5… drh 3892 vfile_check_signature(vid, CKSIG_SETMTIME);
a7e86f5… drh 3893 }
a7e86f5… drh 3894 }
6791ad1… jan.nijtmans 3895 g.argc = 2;
6791ad1… jan.nijtmans 3896 info_cmd();
bff93fc… drh 3897 }
bff93fc… drh 3898
bff93fc… drh 3899 /*
bff93fc… drh 3900 ** Return true if pSetting has its default value assuming its
bff93fc… drh 3901 ** current value is zVal.
bff93fc… drh 3902 */
bff93fc… drh 3903 int setting_has_default_value(const Setting *pSetting, const char *zVal){
bff93fc… drh 3904 if( zVal==0 ) return 1;
bff93fc… drh 3905 if( pSetting->def==0 ) return 0;
bff93fc… drh 3906 if( pSetting->width==0 ){
bff93fc… drh 3907 return is_false(pSetting->def)==is_false(zVal);
bff93fc… drh 3908 }
bff93fc… drh 3909 if( fossil_strcmp(pSetting->def, zVal)==0 ) return 1;
bff93fc… drh 3910 if( is_false(zVal) && is_false(pSetting->def) ) return 1;
bff93fc… drh 3911 if( is_truth(zVal) && is_truth(pSetting->def) ) return 1;
bff93fc… drh 3912 return 0;
74a5e10… drh 3913 }
74a5e10… drh 3914
74a5e10… drh 3915 /*
32f8da0… drh 3916 ** Print the current value of a setting identified by the pSetting
32f8da0… drh 3917 ** pointer.
74a5e10… drh 3918 **
74a5e10… drh 3919 ** Only show the value, not the setting name, if valueOnly is true.
74a5e10… drh 3920 **
74a5e10… drh 3921 ** Show nothing if bIfChng is true and the setting is not currently set
74a5e10… drh 3922 ** or is set to its default value.
32f8da0… drh 3923 */
74a5e10… drh 3924 void print_setting(const Setting *pSetting, int valueOnly, int bIfChng){
0d4a31a… preben 3925 int versioned = 0;
0d4a31a… preben 3926 if( pSetting->versionable && g.localOpen ){
0d4a31a… preben 3927 /* Check to see if this is overridden by a versionable settings file */
0d4a31a… preben 3928 Blob versionedPathname;
0d4a31a… preben 3929 blob_zero(&versionedPathname);
0d4a31a… preben 3930 blob_appendf(&versionedPathname, "%s.fossil-settings/%s",
0d4a31a… preben 3931 g.zLocalRoot, pSetting->name);
0d4a31a… preben 3932 if( file_size(blob_str(&versionedPathname), ExtFILE)>=0 ){
0d4a31a… preben 3933 versioned = 1;
0d4a31a… preben 3934 }
0d4a31a… preben 3935 blob_reset(&versionedPathname);
0d4a31a… preben 3936 }
0d4a31a… preben 3937 if( valueOnly && versioned ){
8f4aedc… drh 3938 const char *zVal = db_get_versioned(pSetting->name, NULL, NULL);
74a5e10… drh 3939 if( !bIfChng || (zVal!=0 && fossil_strcmp(zVal, pSetting->def)!=0) ){
8f4aedc… drh 3940 fossil_print("%s\n", db_get_versioned(pSetting->name, NULL, NULL));
8f4aedc… drh 3941 }else{
8f4aedc… drh 3942 versioned = 0;
8f4aedc… drh 3943 }
0d4a31a… preben 3944 return;
0d4a31a… preben 3945 }
4e683ef… drh 3946 if( g.repositoryOpen ){
4e683ef… drh 3947 db_prepare(&q,
4e683ef… drh 3948 "SELECT '(local)', value FROM config WHERE name=%Q"
4e683ef… drh 3949 " UNION ALL "
4e683ef… drh 3950 "SELECT '(global)', value FROM global_config WHERE name=%Q",
32f8da0… drh 3951 pSetting->name, pSetting->name
4e683ef… drh 3952 );
4e683ef… drh 3953 }else{
4e683ef… drh 3954 db_prepare(&q,
4e683ef… drh 3955 "SELECT '(global)', value FROM global_config WHERE name=%Q",
32f8da0… drh 3956 pSetting->name
4e683ef… drh 3957 );
4e683ef… drh 3958 }
8f4aedc… drh 3959 const char *zVal = db_column_text(&q,1);
bff93fc… drh 3960 if( bIfChng && setting_has_default_value(pSetting,zVal) ){
ea37cc8… drh 3961 if( versioned ){
ea37cc8… drh 3962 fossil_print("%-24s (versioned)\n", pSetting->name);
ea37cc8… drh 3963 versioned = 0;
ea37cc8… drh 3964 }
8f4aedc… drh 3965 }else if( valueOnly ){
0d4a31a… preben 3966 fossil_print("%s\n", db_column_text(&q, 1));
0d4a31a… preben 3967 }else{
74a5e10… drh 3968 const char *zVal = (const char*)db_column_text(&q,1);
74a5e10… drh 3969 const char *zName = (const char*)db_column_text(&q,0);
74a5e10… drh 3970 if( zVal==0 ) zVal = "NULL";
74a5e10… drh 3971 if( strchr(zVal,'\n')==0 ){
74a5e10… drh 3972 fossil_print("%-24s %-11s %s\n", pSetting->name, zName, zVal);
74a5e10… drh 3973 }else{
74a5e10… drh 3974 fossil_print("%-24s %-11s\n", pSetting->name, zName);
74a5e10… drh 3975 while( zVal[0] ){
74a5e10… drh 3976 char *zNL = strchr(zVal, '\n');
74a5e10… drh 3977 if( zNL==0 ){
74a5e10… drh 3978 fossil_print(" %s\n", zVal);
74a5e10… drh 3979 break;
74a5e10… drh 3980 }else{
74a5e10… drh 3981 int n = (int)(zNL - zVal);
74a5e10… drh 3982 while( n>0 && fossil_isspace(zVal[n-1]) ){ n--; }
74a5e10… drh 3983 fossil_print(" %.*s\n", n, zVal);
74a5e10… drh 3984 zVal = zNL+1;
74a5e10… drh 3985 }
74a5e10… drh 3986 }
74a5e10… drh 3987 }
0d4a31a… preben 3988 }
74a5e10… drh 3989 }else if( bIfChng ){
8f4aedc… drh 3990 /* Display nothing */
74a5e10… drh 3991 versioned = 0;
0d4a31a… preben 3992 }else if( valueOnly ){
0d4a31a… preben 3993 fossil_print("\n");
74a5e10… drh 3994 fossil_print("%-24s\n", pSetting->name);
0d4a31a… preben 3995 }
0d4a31a… preben 3996 if( versioned ){
0d4a31a… preben 3997 fossil_print(" (overridden by contents of file .fossil-settings/%s)\n",
0d4a31a… preben 3998 pSetting->name);
32f8da0… drh 3999 #if INTERFACE
b48f789… drh 4000 /*
32f8da0… drh 4001 ** Define all settings, which can be controlled via the set/unset
32f8da0… drh 4002 ** command.
32f8da0… drh 4003 **
32f8da0… drh 4004 ** var is the name of the internal configuration name for db_(un)set.
b48f789… drh 4005 ** If var is 0, the settings name is used.
32f8da0… drh 4006 **
45953a4… stephan 4007 ** width is the length for the edit field on the behavior page, 0 is
33d3bf3… km 4008 ** used for on/off checkboxes. A negative value indicates that the
45953a4… stephan 4009 ** page should not render this setting. Such values may be rendered
45953a4… stephan 4010 ** separately/manually on another page, e.g., /setup_access, and are
45953a4… stephan 4011 ** exposed via the CLI settings command.
32f8da0… drh 4012 **
b48f789… drh 4013 ** The behaviour page doesn't use a special layout. It lists all
b48f789… drh 4014 ** set-commands and displays the 'set'-help as info.
b48f789… drh 4015 */
32f8da0… drh 4016 struct Setting {
4e18dba… jan.nijtmans 4017 const char *name; /* Name of the setting */
4e18dba… jan.nijtmans 4018 const char *var; /* Internal variable name used by db_set() */
45953a4… stephan 4019 int width; /* Width of display. 0 for boolean values and
45953a4… stephan 4020 ** negative for values which should not appear
45953a4… stephan 4021 ** on the /setup_settings page. */
f741baa… drh 4022 char versionable; /* Is this setting versionable? */
f741baa… drh 4023 char forceTextArea; /* Force using a text area for display? */
f741baa… drh 4024 char sensitive; /* True if this a security-sensitive setting */
4e18dba… jan.nijtmans 4025 const char *def; /* Default value */
4e18dba… jan.nijtmans 4026 };
8f4aedc… drh 4027
4e18dba… jan.nijtmans 4028 #endif /* INTERFACE */
32f8da0… drh 4029
f74f701… drh 4030 /*
bdf12f4… drh 4031 ** SETTING: access-log boolean default=on
ce1bc4f… jan.nijtmans 4032 **
f74f701… drh 4033 ** When the access-log setting is enabled, all login attempts (successful
f74f701… drh 4034 ** and unsuccessful) on the web interface are recorded in the "access" table
f74f701… drh 4035 ** of the repository.
f74f701… drh 4036 */
f74f701… drh 4037 /*
bdf12f4… drh 4038 ** SETTING: admin-log boolean default=on
f74f701… drh 4039 **
f74f701… drh 4040 ** When the admin-log setting is enabled, configuration changes are recorded
f74f701… drh 4041 ** in the "admin_log" table of the repository.
f74f701… drh 4042 */
f741baa… drh 4043 /*
f741baa… drh 4044 ** SETTING: allow-symlinks boolean default=off sensitive
f741baa… drh 4045 **
275da70… danield 4046 ** When allow-symlinks is OFF, Fossil does not see symbolic links
f741baa… drh 4047 ** (a.k.a "symlinks") on disk as a separate class of object. Instead Fossil
f741baa… drh 4048 ** sees the object that the symlink points to. Fossil will only manage files
f741baa… drh 4049 ** and directories, not symlinks. When a symlink is added to a repository,
f741baa… drh 4050 ** the object that the symlink points to is added, not the symlink itself.
f741baa… drh 4051 **
f741baa… drh 4052 ** When allow-symlinks is ON, Fossil sees symlinks on disk as a separate
f741baa… drh 4053 ** object class that is distinct from files and directories. When a symlink
f741baa… drh 4054 ** is added to a repository, Fossil stores the target filename. In other
f741baa… drh 4055 ** words, Fossil stores the symlink itself, not the object that the symlink
f741baa… drh 4056 ** points to.
f741baa… drh 4057 **
f741baa… drh 4058 ** Symlinks are not cross-platform. They are not available on all
f741baa… drh 4059 ** operating systems and file systems. Hence the allow-symlinks setting is
f741baa… drh 4060 ** OFF by default, for portability.
f741baa… drh 4061 */
f74f701… drh 4062 /*
f74f701… drh 4063 ** SETTING: auto-captcha boolean default=on variable=autocaptcha
f74f701… drh 4064 ** If enabled, the /login page provides a button that will automatically
f74f701… drh 4065 ** fill in the captcha password. This makes things easier for human users,
7989d7c… drh 4066 ** at the expense of also making logins easier for malicious robots.
f74f701… drh 4067 */
f74f701… drh 4068 /*
df337eb… drh 4069 ** SETTING: auto-hyperlink width=16 default=1
df337eb… drh 4070 **
df337eb… drh 4071 ** If non-zero, enable hyperlinks on web pages even for users that lack
df337eb… drh 4072 ** the "h" privilege as long as the UserAgent string in the HTTP request
df337eb… drh 4073 ** (The HTTP_USER_AGENT cgi variable) looks like it comes from a human and
df337eb… drh 4074 ** not a robot. Details depend on the value of the setting.
df337eb… drh 4075 **
df337eb… drh 4076 ** (0) Off: No adjustments are made to the 'h' privilege based on
df337eb… drh 4077 ** the user agent.
df337eb… drh 4078 **
df337eb… drh 4079 ** (1) UserAgent and Javascript: The the href= values of hyperlinks
df337eb… drh 4080 ** initially point to /honeypot and are changed to point to the
df337eb… drh 4081 ** correct target by javascript that runs after the page loads.
df337eb… drh 4082 ** The auto-hyperlink-delay and auto-hyperlink-mouseover settings
df337eb… drh 4083 ** influence that javascript.
df337eb… drh 4084 **
df337eb… drh 4085 ** (2) UserAgent only: If the HTTP_USER_AGENT looks human
df337eb… drh 4086 ** then generate hyperlinks, otherwise do not.
df337eb… drh 4087 **
df337eb… drh 4088 ** Better robot exclusion is obtained when this setting is 1 versus 2.
df337eb… drh 4089 ** However, a value of 1 causes the visited/unvisited colors of hyperlinks
df337eb… drh 4090 ** to stop working on Safari-derived web browsers. When this setting is 2,
df337eb… drh 4091 ** the hyperlinks work better on Safari, but more robots are able to sneak
df337eb… drh 4092 ** in.
df337eb… drh 4093 */
0dd4118… stephan 4094 /*
0dd4118… stephan 4095 ** SETTING: auto-hyperlink-delay width=16 default=0
df337eb… drh 4096 **
df337eb… drh 4097 ** When the auto-hyperlink setting is 1, the javascript that runs to set
df337eb… drh 4098 ** the href= attributes of hyperlinks delays by this many milliseconds
df337eb… drh 4099 ** after the page load. Suggested values: 50 to 200.
df337eb… drh 4100 */
0dd4118… stephan 4101 /*
0dd4118… stephan 4102 ** SETTING: auto-hyperlink-mouseover boolean default=off
df337eb… drh 4103 **
275da70… danield 4104 ** When the auto-hyperlink setting is 1 and this setting is on, the
df337eb… drh 4105 ** javascript that runs to set the href= attributes of hyperlinks waits
df337eb… drh 4106 ** until either a mousedown or mousemove event is seen. This helps
df337eb… drh 4107 ** to distinguish real users from robots. For maximum robot defense,
df337eb… drh 4108 ** the recommended setting is ON.
f74f701… drh 4109 */
f74f701… drh 4110 /*
f74f701… drh 4111 ** SETTING: auto-shun boolean default=on
f74f701… drh 4112 ** If enabled, automatically pull the shunning list
f74f701… drh 4113 ** from a server to which the client autosyncs.
f74f701… drh 4114 */
f74f701… drh 4115 /*
f74f701… drh 4116 ** SETTING: autosync width=16 default=on
346e457… drh 4117 ** This setting determines when autosync occurs. The setting is a
346e457… drh 4118 ** string that provides a lot of flexibility for determining when and
346e457… drh 4119 ** when not to autosync. Examples:
346e457… drh 4120 **
346e457… drh 4121 ** on Always autosync for command where autosync
346e457… drh 4122 ** makes sense ("commit", "merge", "open", "update")
346e457… drh 4123 **
346e457… drh 4124 ** off Never autosync.
346e457… drh 4125 **
346e457… drh 4126 ** pullonly Only to pull autosyncs
346e457… drh 4127 **
4e7b08c… drh 4128 ** all Sync with all remotes
4e7b08c… drh 4129 **
346e457… drh 4130 ** on,open=off Autosync for most commands, but not for "open"
346e457… drh 4131 **
346e457… drh 4132 ** off,commit=pullonly Do not autosync, except do a pull before each
346e457… drh 4133 ** "commit", presumably to avoid undesirable
346e457… drh 4134 ** forks.
346e457… drh 4135 **
346e457… drh 4136 ** The syntax is a comma-separated list of VALUE and COMMAND=VALUE entries.
346e457… drh 4137 ** A plain VALUE entry is the default that is used if no COMMAND matches.
346e457… drh 4138 ** Otherwise, the VALUE of the matching command is used.
808193e… drh 4139 **
808193e… drh 4140 ** The "all" value is special in that it applies to the "sync" command in
808193e… drh 4141 ** addition to "commit", "merge", "open", and "update".
f74f701… drh 4142 */
f74f701… drh 4143 /*
f74f701… drh 4144 ** SETTING: autosync-tries width=16 default=1
f74f701… drh 4145 ** If autosync is enabled setting this to a value greater
f74f701… drh 4146 ** than zero will cause autosync to try no more than this
f74f701… drh 4147 ** number of attempts if there is a sync failure.
f74f701… drh 4148 */
f74f701… drh 4149 /*
c09b251… drh 4150 ** SETTING: backoffice-nodelay boolean default=off
99fcc43… drh 4151 ** If backoffice-nodelay is true, then the backoffice processing
99fcc43… drh 4152 ** will never invoke sleep(). If it has nothing useful to do,
99fcc43… drh 4153 ** it simply exits.
99fcc43… drh 4154 */
99fcc43… drh 4155 /*
2467a35… drh 4156 ** SETTING: backoffice-disable boolean default=off
2467a35… drh 4157 ** If backoffice-disable is true, then the automatic backoffice
2467a35… drh 4158 ** processing is disabled. Automatic backoffice processing is the
2467a35… drh 4159 ** backoffice work that normally runs after each web page is
2467a35… drh 4160 ** rendered. Backoffice processing that is triggered by the
2467a35… drh 4161 ** "fossil backoffice" command is unaffected by this setting.
2467a35… drh 4162 **
2467a35… drh 4163 ** Backoffice processing does things such as delivering
2467a35… drh 4164 ** email notifications. So if this setting is true, and if
2467a35… drh 4165 ** there is no cron job periodically running "fossil backoffice",
2467a35… drh 4166 ** email notifications and other work normally done by the
2467a35… drh 4167 ** backoffice will not occur.
2467a35… drh 4168 */
2467a35… drh 4169 /*
f741baa… drh 4170 ** SETTING: backoffice-logfile width=40 sensitive
2583cae… drh 4171 ** If backoffice-logfile is not an empty string and is a valid
2583cae… drh 4172 ** filename, then a one-line message is appended to that file
2583cae… drh 4173 ** every time the backoffice runs. This can be used for debugging,
2583cae… drh 4174 ** to ensure that backoffice is running appropriately.
2583cae… drh 4175 */
2583cae… drh 4176 /*
f74f701… drh 4177 ** SETTING: binary-glob width=40 versionable block-text
7dc0433… wyoung 4178 ** The VALUE of this setting is a list of GLOB patterns matching files
7dc0433… wyoung 4179 ** that should be treated as "binary" for committing and merging
7dc0433… wyoung 4180 ** purposes. Example: *.jpg,*.png The parsing rules are complex;
7dc0433… wyoung 4181 ** see https://fossil-scm.org/home/doc/trunk/www/globs.md#syntax
f74f701… drh 4182 */
f74f701… drh 4183 #if defined(_WIN32)||defined(__CYGWIN__)||defined(__DARWIN__)
f74f701… drh 4184 /*
f74f701… drh 4185 ** SETTING: case-sensitive boolean default=off
f74f701… drh 4186 ** If TRUE, the files whose names differ only in case
f74f701… drh 4187 ** are considered distinct. If FALSE files whose names
f74f701… drh 4188 ** differ only in case are the same file. Defaults to
f74f701… drh 4189 ** TRUE for unix and FALSE for Cygwin, Mac and Windows.
f74f701… drh 4190 */
f74f701… drh 4191 #endif
f74f701… drh 4192 #if !(defined(_WIN32)||defined(__CYGWIN__)||defined(__DARWIN__))
f74f701… drh 4193 /*
f74f701… drh 4194 ** SETTING: case-sensitive boolean default=on
f74f701… drh 4195 ** If TRUE, the files whose names differ only in case
f74f701… drh 4196 ** are considered distinct. If FALSE files whose names
f74f701… drh 4197 ** differ only in case are the same file. Defaults to
f74f701… drh 4198 ** TRUE for unix and FALSE for Cygwin, Mac and Windows.
f74f701… drh 4199 */
f74f701… drh 4200 #endif
f74f701… drh 4201 /*
ab8ed31… drh 4202 ** SETTING: clean-glob width=40 versionable block-text
7dc0433… wyoung 4203 ** The VALUE of this setting is a list of GLOB patterns matching files
7dc0433… wyoung 4204 ** that the "clean" command will delete without prompting or allowing
7dc0433… wyoung 4205 ** undo. Example: *.a,*.o,*.so The parsing rules are complex;
7dc0433… wyoung 4206 ** see https://fossil-scm.org/home/doc/trunk/www/globs.md#syntax
f74f701… drh 4207 */
f74f701… drh 4208 /*
f74f701… drh 4209 ** SETTING: clearsign boolean default=off
f74f701… drh 4210 ** When enabled, fossil will attempt to sign all commits
91d70b7… danield 4211 ** with gpg or ssh. When disabled, commits will be unsigned.
2476b81… drh 4212 */
2476b81… drh 4213 /*
2476b81… drh 4214 ** SETTING: comment-format width=16 default=1
35302d9… drh 4215 ** Set the algorithm for printing timeline comments to the console.
2476b81… drh 4216 **
2476b81… drh 4217 ** Possible values are:
35302d9… drh 4218 ** 1 Use the original comment printing algorithm:
7438f5b… wyoung 4219 ** * Leading and trailing whitespace is removed
35302d9… drh 4220 ** * Internal whitespace is converted into a single space (0x20)
35302d9… drh 4221 ** * Line breaks occurs at whitespace or hyphens if possible
35302d9… drh 4222 ** This is the recommended value and the default.
2476b81… drh 4223 **
2476b81… drh 4224 ** Or a bitwise combination of the following flags:
2476b81… drh 4225 ** 2 Trim leading and trailing CR and LF characters.
2476b81… drh 4226 ** 4 Trim leading and trailing white space characters.
2476b81… drh 4227 ** 8 Attempt to break lines on word boundaries.
2476b81… drh 4228 ** 16 Break lines before the original comment embedded in other text.
2476b81… drh 4229 **
35302d9… drh 4230 ** Note: To preserve line breaks and/or other whitespace within comment text,
35302d9… drh 4231 ** make this setting some integer value that omits the "1" bit.
f74f701… drh 4232 */
f74f701… drh 4233 /*
f74f701… drh 4234 ** SETTING: crlf-glob width=40 versionable block-text
7dc0433… wyoung 4235 ** The VALUE of this setting is a list of GLOB patterns matching files
7dc0433… wyoung 4236 ** in which it is allowed to have CR, CR+LF or mixed line endings,
7dc0433… wyoung 4237 ** suppressing Fossil's normal warning about this. Set it to "*" to
7dc0433… wyoung 4238 ** disable CR+LF checking entirely. Example: *.md,*.txt
f74f701… drh 4239 ** The crnl-glob setting is a compatibility alias.
c5d287b… wyoung 4240 */
f74f701… drh 4241 /*
f74f701… drh 4242 ** SETTING: crnl-glob width=40 versionable block-text
7989d7c… drh 4243 ** This is an alias for the crlf-glob setting.
f74f701… drh 4244 */
f74f701… drh 4245 /*
1f6ae1e… drh 4246 ** SETTING: default-perms width=16 default=u sensitive keep-empty
f74f701… drh 4247 ** Permissions given automatically to new users. For more
f74f701… drh 4248 ** information on permissions see the Users page in Server
f74f701… drh 4249 ** Administration of the HTTP UI.
f74f701… drh 4250 */
2eea758… drh 4251 /*
2eea758… drh 4252 ** SETTING: diff-binary boolean default=on
f74f701… drh 4253 ** If enabled, permit files that may be binary
f74f701… drh 4254 ** or that match the "binary-glob" setting to be used with
f74f701… drh 4255 ** external diff programs. If disabled, skip these files.
f74f701… drh 4256 */
f74f701… drh 4257 /*
f741baa… drh 4258 ** SETTING: diff-command width=40 sensitive
f74f701… drh 4259 ** The value is an external command to run when performing a diff.
f74f701… drh 4260 ** If undefined, the internal text diff will be used.
f741baa… drh 4261 */
f741baa… drh 4262 /*
884436c… danield 4263 ** SETTING: dont-commit boolean default=off
884436c… danield 4264 ** If enabled, prevent committing to this repository, as an extra precaution
884436c… danield 4265 ** against accidentally checking in to a repository intended to be read-only.
884436c… danield 4266 */
884436c… danield 4267 /*
f74f701… drh 4268 ** SETTING: dont-push boolean default=off
f74f701… drh 4269 ** If enabled, prevent this repository from pushing from client to
f74f701… drh 4270 ** server. This can be used as an extra precaution to prevent
f74f701… drh 4271 ** accidental pushes to a public server from a private clone.
f74f701… drh 4272 */
f74f701… drh 4273 /*
f74f701… drh 4274 ** SETTING: dotfiles boolean versionable default=off
f74f701… drh 4275 ** If enabled, include --dotfiles option for all compatible commands.
f74f701… drh 4276 */
f74f701… drh 4277 /*
f741baa… drh 4278 ** SETTING: editor width=32 sensitive
f74f701… drh 4279 ** The value is an external command that will launch the
f74f701… drh 4280 ** text editor command used for check-in comments.
c94da9d… drh 4281 **
c94da9d… drh 4282 ** If this value is not set, then environment variables VISUAL and
c94da9d… drh 4283 ** EDITOR are consulted, in that order. If neither of those are set,
c94da9d… drh 4284 ** then a search is made for common text editors, including
486f81b… drh 4285 ** "notepad", "nano", "pico", "jove", "edit", "vi", "vim", and "ed".
c94da9d… drh 4286 **
c94da9d… drh 4287 ** If this setting is false ("off", "no", "false", or "0") then no
c94da9d… drh 4288 ** text editor is used.
f74f701… drh 4289 */
f74f701… drh 4290 /*
f74f701… drh 4291 ** SETTING: empty-dirs width=40 versionable block-text
f12609f… wyoung 4292 ** The value is a list of pathnames parsed according to the same rules as
f12609f… wyoung 4293 ** the *-glob settings. On update and checkout commands, if no directory
e2bdc10… danield 4294 ** exists with that name, an empty directory will be created, even if
f12609f… wyoung 4295 ** it must create one or more parent directories.
f74f701… drh 4296 */
f74f701… drh 4297 /*
f74f701… drh 4298 ** SETTING: encoding-glob width=40 versionable block-text
7dc0433… wyoung 4299 ** The VALUE of this setting is a list of GLOB patterns matching files that
7dc0433… wyoung 4300 ** the "commit" command will ignore when issuing warnings about text files
7dc0433… wyoung 4301 ** that may use another encoding than ASCII or UTF-8. Set to "*" to disable
7dc0433… wyoung 4302 ** encoding checking. Example: *.md,*.txt The parsing rules are complex;
7dc0433… wyoung 4303 ** see https://fossil-scm.org/home/doc/trunk/www/globs.md#syntax
f74f701… drh 4304 */
825d78b… mistachkin 4305 #if defined(FOSSIL_ENABLE_EXEC_REL_PATHS)
f74f701… drh 4306 /*
f74f701… drh 4307 ** SETTING: exec-rel-paths boolean default=on
f74f701… drh 4308 ** When executing certain external commands (e.g. diff and
f74f701… drh 4309 ** gdiff), use relative paths.
f74f701… drh 4310 */
f74f701… drh 4311 #endif
f74f701… drh 4312 #if !defined(FOSSIL_ENABLE_EXEC_REL_PATHS)
f74f701… drh 4313 /*
f74f701… drh 4314 ** SETTING: exec-rel-paths boolean default=off
f74f701… drh 4315 ** When executing certain external commands (e.g. diff and
f74f701… drh 4316 ** gdiff), use relative paths.
f74f701… drh 4317 */
f74f701… drh 4318 #endif
1243bf3… stephan 4319
1243bf3… stephan 4320 /*
1243bf3… stephan 4321 ** SETTING: fileedit-glob width=40 block-text
7dc0433… wyoung 4322 ** The VALUE of this setting is a list of GLOB patterns matching files
7dc0433… wyoung 4323 ** which are allowed to be edited using the /fileedit page. An empty list
7dc0433… wyoung 4324 ** suppresses the feature. Example: *.md,*.txt The parsing rules are
7dc0433… wyoung 4325 ** complex; see https://fossil-scm.org/home/doc/trunk/www/globs.md#syntax
7dc0433… wyoung 4326 ** Note that /fileedit cannot edit binary files, so the list should not
1243bf3… stephan 4327 ** contain any globs for, e.g., images or PDFs.
1243bf3… stephan 4328 */
1243bf3… stephan 4329 /*
884436c… danield 4330 ** SETTING: forbid-delta-manifests boolean default=off
884436c… danield 4331 ** If enabled on a client, new delta manifests are prohibited on
884436c… danield 4332 ** commits. If enabled on a server, whenever a client attempts
275da70… danield 4333 ** to obtain a check-in lock during auto-sync, the server will
884436c… danield 4334 ** send the "pragma avoid-delta-manifests" statement in its reply,
884436c… danield 4335 ** which will cause the client to avoid generating a delta
884436c… danield 4336 ** manifest.
884436c… danield 4337 */
884436c… danield 4338 /*
8c4c93b… drh 4339 ** SETTING: gdiff-command width=40 sensitive
f74f701… drh 4340 ** The value is an external command to run when performing a graphical
721c43b… drh 4341 ** diff. If undefined, a --tk diff is done if commands "tclsh" and "wish"
721c43b… drh 4342 ** are on PATH, or a --by diff is done if "tclsh" or "wish" are unavailable.
f74f701… drh 4343 */
f74f701… drh 4344 /*
f741baa… drh 4345 ** SETTING: gmerge-command width=40 sensitive
f74f701… drh 4346 ** The value is a graphical merge conflict resolver command operating
f74f701… drh 4347 ** on four files. Examples:
ce1bc4f… jan.nijtmans 4348 **
f74f701… drh 4349 ** kdiff3 "%baseline" "%original" "%merge" -o "%output"
f74f701… drh 4350 ** xxdiff "%original" "%baseline" "%merge" -M "%output"
375589e… wyoung 4351 ** meld "%baseline" "%original" "%merge" --output "%output"
f74f701… drh 4352 */
f74f701… drh 4353 /*
f74f701… drh 4354 ** SETTING: hash-digits width=5 default=10
f74f701… drh 4355 ** The number of hexadecimal digits of the SHA3 hash to display.
f74f701… drh 4356 */
f74f701… drh 4357 /*
f74f701… drh 4358 ** SETTING: http-port width=16 default=8080
f74f701… drh 4359 ** The default TCP/IP port number to use by the "server"
f74f701… drh 4360 ** and "ui" commands.
f74f701… drh 4361 */
f74f701… drh 4362 /*
f74f701… drh 4363 ** SETTING: https-login boolean default=off
f74f701… drh 4364 ** If true, then the Fossil web server will redirect unencrypted
7989d7c… drh 4365 ** login screen requests to HTTPS.
f74f701… drh 4366 */
f74f701… drh 4367 /*
f74f701… drh 4368 ** SETTING: ignore-glob width=40 versionable block-text
7dc0433… wyoung 4369 ** The VALUE of this setting is a list of GLOB patterns matching files that
7dc0433… wyoung 4370 ** the "add", "addremove", "clean", and "extras" commands will ignore.
7dc0433… wyoung 4371 ** Example: *.log,notes.txt The parsing rules are complex; see
7dc0433… wyoung 4372 ** https://fossil-scm.org/home/doc/trunk/www/globs.md#syntax
f74f701… drh 4373 */
f74f701… drh 4374 /*
f74f701… drh 4375 ** SETTING: keep-glob width=40 versionable block-text
7dc0433… wyoung 4376 ** The VALUE of this setting is a list of GLOB patterns matching files that
7dc0433… wyoung 4377 ** the "clean" command must not delete. Example: build/precious.exe
7dc0433… wyoung 4378 ** The parsing rules are complex; see
7dc0433… wyoung 4379 ** https://fossil-scm.org/home/doc/trunk/www/globs.md#syntax
f74f701… drh 4380 */
f74f701… drh 4381 /*
f74f701… drh 4382 ** SETTING: localauth boolean default=off
883c2ea… drh 4383 ** If enabled, require that HTTP connections from the loopback
883c2ea… drh 4384 ** address (127.0.0.1) be authenticated by password. If false,
883c2ea… drh 4385 ** some HTTP requests might be granted full "Setup" user
883c2ea… drh 4386 ** privileges without having to present login credentials.
883c2ea… drh 4387 ** This mechanism allows the "fossil ui" command to provide
883c2ea… drh 4388 ** full access to the repository without requiring the user to
883c2ea… drh 4389 ** log in first.
883c2ea… drh 4390 **
883c2ea… drh 4391 ** In order for full "Setup" privilege to be granted without a
883c2ea… drh 4392 ** login, the following conditions must be met:
883c2ea… drh 4393 **
883c2ea… drh 4394 ** (1) This setting ("localauth") must be off
883c2ea… drh 4395 ** (2) The HTTP request arrive over the loopback TCP/IP
883c2ea… drh 4396 ** address (127.0.01) or else via SSH.
883c2ea… drh 4397 ** (3) The request must be HTTP, not HTTPS. (This
883c2ea… drh 4398 ** restriction is designed to help prevent accidentally
883c2ea… drh 4399 ** providing "Setup" privileges to requests arriving
883c2ea… drh 4400 ** over a reverse proxy.)
883c2ea… drh 4401 ** (4) The command that launched the fossil server must be
883c2ea… drh 4402 ** one of the following:
883c2ea… drh 4403 ** (a) "fossil ui"
883c2ea… drh 4404 ** (b) "fossil server" with the --localauth option
883c2ea… drh 4405 ** (c) "fossil http" with the --localauth option
883c2ea… drh 4406 ** (d) CGI with the "localauth" setting in the cgi script.
883c2ea… drh 4407 **
883c2ea… drh 4408 ** For maximum security, set "localauth" to 1. However, because
883c2ea… drh 4409 ** of the other restrictions (2) through (4), it should be safe
918bcfc… jan.nijtmans 4410 ** to leave "localauth" set to 0 in most installations, and
883c2ea… drh 4411 ** especially on cloned repositories on workstations. Leaving
883c2ea… drh 4412 ** "localauth" at 0 makes the "fossil ui" command more convenient
883c2ea… drh 4413 ** to use.
6d3daf7… drh 4414 */
6d3daf7… drh 4415 /*
18d5880… drh 4416 ** SETTING: lock-timeout width=25 default=60
6d3daf7… drh 4417 ** This is the number of seconds that a check-in lock will be held on
18d5880… drh 4418 ** the server before the lock expires. The default is a 60-second delay.
6d3daf7… drh 4419 ** Set this value to zero to disable the check-in lock mechanism.
6d3daf7… drh 4420 **
6d3daf7… drh 4421 ** This value should be set on the server to which users auto-sync
2f78b2c… danield 4422 ** their work. This setting has no effect on client repositories. The
6d3daf7… drh 4423 ** check-in lock mechanism is only effective if all users are auto-syncing
6d3daf7… drh 4424 ** to the same server.
6d3daf7… drh 4425 **
6d3daf7… drh 4426 ** Check-in locks are an advisory mechanism designed to help prevent
6d3daf7… drh 4427 ** accidental forks due to a check-in race in installations where many
66c3095… andybradford 4428 ** users are committing to the same branch and auto-sync is enabled.
18d5880… drh 4429 ** As forks are harmless, there is no danger in disabling this mechanism.
6d3daf7… drh 4430 ** However, keeping check-in locks turned on can help prevent unnecessary
6d3daf7… drh 4431 ** confusion.
f74f701… drh 4432 */
f74f701… drh 4433 /*
f74f701… drh 4434 ** SETTING: main-branch width=40 default=trunk
f74f701… drh 4435 ** The value is the primary branch for the project.
f74f701… drh 4436 */
f74f701… drh 4437 /*
e8dc1df… drh 4438 ** SETTING: manifest width=5 versionable
f74f701… drh 4439 ** If enabled, automatically create files "manifest" and "manifest.uuid"
bc36fdc… danield 4440 ** in every check-out.
f74f701… drh 4441 **
f74f701… drh 4442 ** Optionally use combinations of characters 'r' for "manifest",
f74f701… drh 4443 ** 'u' for "manifest.uuid" and 't' for "manifest.tags". The SQLite
f74f701… drh 4444 ** and Fossil repositories both require manifests.
f74f701… drh 4445 */
f74f701… drh 4446 /*
f74f701… drh 4447 ** SETTING: max-loadavg width=25 default=0.0
f74f701… drh 4448 ** Some CPU-intensive web pages (ex: /zip, /tarball, /blame)
f74f701… drh 4449 ** are disallowed if the system load average goes above this
f74f701… drh 4450 ** value. "0.0" means no limit. This only works on unix.
f74f701… drh 4451 ** Only local settings of this value make a difference since
f74f701… drh 4452 ** when running as a web-server, Fossil does not open the
f74f701… drh 4453 ** global configuration database.
f74f701… drh 4454 */
f74f701… drh 4455 /*
f74f701… drh 4456 ** SETTING: max-upload width=25 default=250000
f74f701… drh 4457 ** A limit on the size of uplink HTTP requests.
f74f701… drh 4458 */
f74f701… drh 4459 /*
009a243… stephan 4460 ** SETTING: mimetypes width=40 versionable block-text
009a243… stephan 4461 ** A list of file extension-to-mimetype mappings, one per line. e.g.
009a243… stephan 4462 ** "foo application/x-foo". File extensions are compared
009a243… stephan 4463 ** case-insensitively in the order listed in this setting. A leading
009a243… stephan 4464 ** '.' on file extensions is permitted but not required.
009a243… stephan 4465 */
009a243… stephan 4466 /*
f74f701… drh 4467 ** SETTING: mtime-changes boolean default=on
f74f701… drh 4468 ** Use file modification times (mtimes) to detect when
f74f701… drh 4469 ** files have been modified. If disabled, all managed files
f74f701… drh 4470 ** are hashed to detect changes, which can be slow for large
f74f701… drh 4471 ** projects.
f74f701… drh 4472 */
f74f701… drh 4473 /*
f74f701… drh 4474 ** SETTING: mv-rm-files boolean default=off
f74f701… drh 4475 ** If enabled, the "mv" and "rename" commands will also move
bc36fdc… danield 4476 ** the associated files within the check-out -AND- the "rm"
f74f701… drh 4477 ** and "delete" commands will also remove the associated
bc36fdc… danield 4478 ** files from within the check-out.
f74f701… drh 4479 */
f74f701… drh 4480 /*
f741baa… drh 4481 ** SETTING: pgp-command width=40 sensitive
f74f701… drh 4482 ** Command used to clear-sign manifests at check-in.
91d70b7… danield 4483 ** Default value is "gpg --clearsign -o".
91d70b7… danield 4484 ** For SSH, use e.g. "ssh-keygen -q -Y sign -n fossilscm -f ~/.ssh/id_ed25519"
884436c… danield 4485 */
884436c… danield 4486 /*
8f9f2cb… mgagnon 4487 ** SETTING: proxy width=32 default=system
8f9f2cb… mgagnon 4488 ** URL of the HTTP proxy. If undefined or "system", the "http_proxy"
8f9f2cb… mgagnon 4489 ** environment variable is consulted. If "off", a direct HTTP connection is
8f9f2cb… mgagnon 4490 ** used.
884436c… danield 4491 */
884436c… danield 4492 /*
b087b03… drh 4493 ** SETTING: redirect-to-https default=0 width=2
2789197… drh 4494 ** Specifies whether or not to redirect unencrypted "http://" requests to
2789197… drh 4495 ** encrypted "https://" URIs. A value of 0 (the default) means do not
45953a4… stephan 4496 ** redirect, 1 means to redirect only the /login page, and 2
45953a4… stephan 4497 ** means to always redirect.
2789197… drh 4498 **
2789197… drh 4499 ** For security, a value of 2 is recommended. The default value is 0
2789197… drh 4500 ** because not all sites are TLS-capable. But you should definitely enable
2789197… drh 4501 ** TLS and change this setting to 2 for all public-facing repositories.
f74f701… drh 4502 */
f74f701… drh 4503 /*
f74f701… drh 4504 ** SETTING: relative-paths boolean default=on
f74f701… drh 4505 ** When showing changes and extras, report paths relative
f74f701… drh 4506 ** to the current working directory.
f74f701… drh 4507 */
f74f701… drh 4508 /*
f74f701… drh 4509 ** SETTING: repo-cksum boolean default=on
bc36fdc… danield 4510 ** Compute checksums over all files in each check-out as a double-check
f74f701… drh 4511 ** of correctness. Disable this on large repositories for a performance
f74f701… drh 4512 ** improvement.
f74f701… drh 4513 */
f74f701… drh 4514 /*
8f5399b… drh 4515 ** SETTING: repolist-skin width=2 default=0
02bdcf5… drh 4516 ** If non-zero then use this repository as the skin for a repository list
02bdcf5… drh 4517 ** such as created by the one of:
02bdcf5… drh 4518 **
02bdcf5… drh 4519 ** 1) fossil server DIRECTORY --repolist
02bdcf5… drh 4520 ** 2) fossil ui DIRECTORY --repolist
02bdcf5… drh 4521 ** 3) fossil http DIRECTORY --repolist
02bdcf5… drh 4522 ** 4) (The "repolist" option in a CGI script)
02bdcf5… drh 4523 ** 5) fossil all ui
02bdcf5… drh 4524 ** 6) fossil all server
02bdcf5… drh 4525 **
02bdcf5… drh 4526 ** All repositories are searched (in lexicographical order) and the first
02bdcf5… drh 4527 ** repository with a non-zero "repolist-skin" value is used as the skin
02bdcf5… drh 4528 ** for the repository list page. If none of the repositories on the list
02bdcf5… drh 4529 ** have a non-zero "repolist-skin" setting then the repository list is
c591bbe… florian 4530 ** displayed using unadorned HTML ("skinless"), with the page title taken
c591bbe… florian 4531 ** from the FOSSIL_REPOLIST_TITLE environment variable.
02bdcf5… drh 4532 **
02bdcf5… drh 4533 ** If repolist-skin has a value of 2, then the repository is omitted from
02bdcf5… drh 4534 ** the list in use cases 1 through 4, but not for 5 and 6.
07bfe3f… drh 4535 */
07bfe3f… drh 4536 /*
07bfe3f… drh 4537 ** SETTING: self-pw-reset boolean default=off sensitive
07bfe3f… drh 4538 ** Allow users to request that an email containing a hyperlink
07bfe3f… drh 4539 ** to the /resetpw page be sent to their email address of record,
07bfe3f… drh 4540 ** thus allowing forgetful users to reset their forgotten passwords
07bfe3f… drh 4541 ** without administrator involvement.
f741baa… drh 4542 */
f741baa… drh 4543 /*
f741baa… drh 4544 ** SETTING: self-register boolean default=off sensitive
f74f701… drh 4545 ** Allow users to register themselves through the HTTP UI.
f74f701… drh 4546 ** This is useful if you want to see other names than
f74f701… drh 4547 ** "Anonymous" in e.g. ticketing system. On the other hand
f74f701… drh 4548 ** users can not be deleted.
f74f701… drh 4549 */
f74f701… drh 4550 /*
f741baa… drh 4551 ** SETTING: ssh-command width=40 sensitive
f74f701… drh 4552 ** The command used to talk to a remote machine with the "ssh://" protocol.
f74f701… drh 4553 */
f6263bb… drh 4554
f74f701… drh 4555 /*
f741baa… drh 4556 ** SETTING: ssl-ca-location width=40 sensitive
f74f701… drh 4557 ** The full pathname to a file containing PEM encoded
f74f701… drh 4558 ** CA root certificates, or a directory of certificates
f74f701… drh 4559 ** with filenames formed from the certificate hashes as
f74f701… drh 4560 ** required by OpenSSL.
f74f701… drh 4561 **
f74f701… drh 4562 ** If set, this will override the OS default list of
f74f701… drh 4563 ** OpenSSL CAs. If unset, the default list will be used.
f74f701… drh 4564 ** Some platforms may add additional certificates.
f74f701… drh 4565 ** Checking your platform behaviour is required if the
f74f701… drh 4566 ** exact contents of the CA root is critical for your
f74f701… drh 4567 ** application.
85cfdd1… florian 4568 **
85cfdd1… florian 4569 ** This setting is overridden by environment variables
85cfdd1… florian 4570 ** SSL_CERT_FILE and SSL_CERT_DIR.
f74f701… drh 4571 */
f74f701… drh 4572 /*
f741baa… drh 4573 ** SETTING: ssl-identity width=40 sensitive
f74f701… drh 4574 ** The full pathname to a file containing a certificate
f74f701… drh 4575 ** and private key in PEM format. Create by concatenating
f74f701… drh 4576 ** the certificate and private key files.
f74f701… drh 4577 **
f74f701… drh 4578 ** This identity will be presented to SSL servers to
f74f701… drh 4579 ** authenticate this client, in addition to the normal
f74f701… drh 4580 ** password authentication.
f74f701… drh 4581 */
f74f701… drh 4582 #ifdef FOSSIL_ENABLE_TCL
f74f701… drh 4583 /*
f741baa… drh 4584 ** SETTING: tcl boolean default=off sensitive
f74f701… drh 4585 ** If enabled Tcl integration commands will be added to the TH1
f74f701… drh 4586 ** interpreter, allowing arbitrary Tcl expressions and
f74f701… drh 4587 ** scripts to be evaluated from TH1. Additionally, the Tcl
f74f701… drh 4588 ** interpreter will be able to evaluate arbitrary TH1
f74f701… drh 4589 ** expressions and scripts.
f74f701… drh 4590 */
f74f701… drh 4591 /*
f741baa… drh 4592 ** SETTING: tcl-setup width=40 block-text sensitive
f74f701… drh 4593 ** This is the setup script to be evaluated after creating
f74f701… drh 4594 ** and initializing the Tcl interpreter. By default, this
f74f701… drh 4595 ** is empty and no extra setup is performed.
f74f701… drh 4596 */
f74f701… drh 4597 #endif /* FOSSIL_ENABLE_TCL */
9fc945c… drh 4598 /*
f741baa… drh 4599 ** SETTING: tclsh width=80 default=tclsh sensitive
9fc945c… drh 4600 ** Name of the external TCL interpreter used for such things
9fc945c… drh 4601 ** as running the GUI diff viewer launched by the --tk option
9fc945c… drh 4602 ** of the various "diff" commands.
9fc945c… drh 4603 */
f74f701… drh 4604 #ifdef FOSSIL_ENABLE_TH1_DOCS
f74f701… drh 4605 /*
f741baa… drh 4606 ** SETTING: th1-docs boolean default=off sensitive
f74f701… drh 4607 ** If enabled, this allows embedded documentation files to contain
f74f701… drh 4608 ** arbitrary TH1 scripts that are evaluated on the server. If native
f74f701… drh 4609 ** Tcl integration is also enabled, this setting has the
f74f701… drh 4610 ** potential to allow anybody with check-in privileges to
f74f701… drh 4611 ** do almost anything that the associated operating system
f74f701… drh 4612 ** user account could do. Extreme caution should be used
f74f701… drh 4613 ** when enabling this setting.
f74f701… drh 4614 */
ff8ac8d… mistachkin 4615 #endif
ff8ac8d… mistachkin 4616 #ifdef FOSSIL_ENABLE_TH1_HOOKS
f74f701… drh 4617 /*
f74f701… drh 4618 ** SETTING: th1-hooks boolean default=off
f74f701… drh 4619 ** If enabled, special TH1 commands will be called before and
f74f701… drh 4620 ** after any Fossil command or web page.
f74f701… drh 4621 */
4e18dba… jan.nijtmans 4622 #endif
f74f701… drh 4623 /*
2ff87d4… stephan 4624 ** SETTING: th1-setup width=40 block-text sensitive
f74f701… drh 4625 ** This is the setup script to be evaluated after creating
f74f701… drh 4626 ** and initializing the TH1 interpreter. By default, this
f74f701… drh 4627 ** is empty and no extra setup is performed.
f74f701… drh 4628 */
f74f701… drh 4629 /*
2da704c… drh 4630 ** SETTING: th1-uri-regexp width=40 block-text
f74f701… drh 4631 ** Specify which URI's are allowed in HTTP requests from
f74f701… drh 4632 ** TH1 scripts. If empty, no HTTP requests are allowed
f74f701… drh 4633 ** whatsoever.
2da704c… drh 4634 */
2da704c… drh 4635 /*
1f6ae1e… drh 4636 ** SETTING: default-csp width=40 block-text keep-empty
14c81d9… drh 4637 **
14c81d9… drh 4638 ** The text of the Content Security Policy that is included
14c81d9… drh 4639 ** in the Content-Security-Policy: header field of the HTTP
14c81d9… drh 4640 ** reply and in the default HTML <head> section that is added when the
14c81d9… drh 4641 ** skin header does not specify a <head> section. The text "$nonce"
14c81d9… drh 4642 ** is replaced by the random nonce that is created for each web page.
14c81d9… drh 4643 **
14c81d9… drh 4644 ** If this setting is an empty string or is omitted, then
14c81d9… drh 4645 ** the following default Content Security Policy is used:
14c81d9… drh 4646 **
14c81d9… drh 4647 ** default-src 'self' data:;
14c81d9… drh 4648 ** script-src 'self' 'nonce-$nonce';
14c81d9… drh 4649 ** style-src 'self' 'unsafe-inline';
c184d64… drh 4650 ** img-src * data:;
14c81d9… drh 4651 **
14c81d9… drh 4652 ** The default CSP is recommended. The main reason to change
14c81d9… drh 4653 ** this setting would be to add CDNs from which it is safe to
14c81d9… drh 4654 ** load additional content.
14c81d9… drh 4655 */
14c81d9… drh 4656 /*
f74f701… drh 4657 ** SETTING: uv-sync boolean default=off
f74f701… drh 4658 ** If true, automatically send unversioned files as part
f74f701… drh 4659 ** of a "fossil clone" or "fossil sync" command. The
f74f701… drh 4660 ** default is false, in which case the -u option is
f74f701… drh 4661 ** needed to clone or sync unversioned files.
f74f701… drh 4662 */
f74f701… drh 4663 /*
f741baa… drh 4664 ** SETTING: web-browser width=30 sensitive
f74f701… drh 4665 ** A shell command used to launch your preferred
f74f701… drh 4666 ** web browser when given a URL as an argument.
f74f701… drh 4667 ** Defaults to "start" on windows, "open" on Mac,
f74f701… drh 4668 ** and "firefox" on Unix.
3ffe893… drh 4669 */
3ffe893… drh 4670 /*
3ffe893… drh 4671 ** SETTING: large-file-size width=10 default=200000000
3ffe893… drh 4672 ** Fossil considers any file whose size is greater than this value
3ffe893… drh 4673 ** to be a "large file". Fossil might issue warnings if you try to
275da70… danield 4674 ** "add" or "commit" a "large file". Set this value to 0 or less
3ffe893… drh 4675 ** to disable all such warnings.
f74f701… drh 4676 */
32f8da0… drh 4677
32f8da0… drh 4678 /*
32f8da0… drh 4679 ** Look up a control setting by its name. Return a pointer to the Setting
32f8da0… drh 4680 ** object, or NULL if there is no such setting.
32f8da0… drh 4681 **
32f8da0… drh 4682 ** If allowPrefix is true, then the Setting returned is the first one for
32f8da0… drh 4683 ** which zName is a prefix of the Setting name.
32f8da0… drh 4684 */
f74f701… drh 4685 Setting *db_find_setting(const char *zName, int allowPrefix){
32f8da0… drh 4686 int lwr, mid, upr, c;
32f8da0… drh 4687 int n = (int)strlen(zName) + !allowPrefix;
f74f701… drh 4688 int nSetting;
f74f701… drh 4689 const Setting *aSetting = setting_info(&nSetting);
32f8da0… drh 4690 lwr = 0;
f74f701… drh 4691 upr = nSetting - 1;
32f8da0… drh 4692 while( upr>=lwr ){
32f8da0… drh 4693 mid = (upr+lwr)/2;
32f8da0… drh 4694 c = fossil_strncmp(zName, aSetting[mid].name, n);
32f8da0… drh 4695 if( c<0 ){
32f8da0… drh 4696 upr = mid - 1;
32f8da0… drh 4697 }else if( c>0 ){
32f8da0… drh 4698 lwr = mid + 1;
32f8da0… drh 4699 }else{
32f8da0… drh 4700 if( allowPrefix ){
32f8da0… drh 4701 while( mid>lwr && fossil_strncmp(zName, aSetting[mid-1].name, n)==0 ){
32f8da0… drh 4702 mid--;
32f8da0… drh 4703 }
32f8da0… drh 4704 }
f74f701… drh 4705 return (Setting*)&aSetting[mid];
32f8da0… drh 4706 }
32f8da0… drh 4707 }
32f8da0… drh 4708 return 0;
32f8da0… drh 4709 }
841772c… drh 4710 ** COMMAND: unset*
841772c… drh 4711 **
f74f701… drh 4712 ** Usage: %fossil settings ?SETTING? ?VALUE? ?OPTIONS?
f74f701… drh 4713 ** or: %fossil unset SETTING ?OPTIONS?
841772c… drh 4714 **
f74f701… drh 4715 ** The "settings" command with no arguments lists all settings and their
f74f701… drh 4716 ** values. With just a SETTING name it shows the current value of that setting.
f74f701… drh 4717 ** With a VALUE argument it changes the property for the current repository.
9a0c995… drh 4718 **
9a0c995… drh 4719 ** Settings marked as versionable are overridden by the contents of the
c510b74… michai 4720 ** file named .fossil-settings/PROPERTY in the check-out root, if that
9a0c995… drh 4721 ** file exists.
9a0c995… drh 4722 **
f74f701… drh 4723 ** The "unset" command clears a setting.
f74f701… drh 4724 **
f74f701… drh 4725 ** Settings can have both a "local" repository-only value and "global" value
f74f701… drh 4726 ** that applies to all repositories. The local values are stored in the
f74f701… drh 4727 ** "config" table of the repository and the global values are stored in the
4645e9b… drh 4728 ** configuration database. If both a local and a global value exists for a
4645e9b… drh 4729 ** setting, the local value takes precedence. This command normally operates
4645e9b… drh 4730 ** on the local settings. Use the --global option to change global settings.
75ea5ac… drh 4731 **
75ea5ac… drh 4732 ** Options:
74a5e10… drh 4733 ** --changed Only show settings if the value differs from the default
8f4aedc… drh 4734 ** --exact Only consider exact name matches
2e56ef4… km 4735 ** --global Set or unset the given property globally instead of
2e56ef4… km 4736 ** setting or unsetting it for the open repository only
0d4a31a… preben 4737 ** --value Only show the value of a given property (implies --exact)
dacc694… jan.nijtmans 4738 **
71992d0… drh 4739 ** See also: [[configuration]]
74a5e10… drh 4740 int bIfChng = find_option("changed",0,0)!=0;
816bc43… mistachkin 4741 int exactFlag = find_option("exact",0,0)!=0;
0d4a31a… preben 4742 int valueFlag = find_option("value",0,0)!=0;
346e457… drh 4743 /* Undocumented "--test-for-subsystem SUBSYS" option used to test
346e457… drh 4744 ** the db_get_for_subsystem() interface: */
346e457… drh 4745 const char *zSubsys = find_option("test-for-subsystem",0,1);
4182079… drh 4746 int unsetFlag = g.argv[1][0]=='u';
f74f701… drh 4747 int nSetting;
f74f701… drh 4748 const Setting *aSetting = setting_info(&nSetting);
5347e26… drh 4749 find_repository_option();
816bc43… mistachkin 4750 verify_all_options();
b06cd63… mistachkin 4751 db_open_config(1, 0);
3d59229… drh 4752 if( !globalFlag ){
3d59229… drh 4753 db_find_and_open_repository(OPEN_ANY_SCHEMA | OPEN_OK_NOT_FOUND, 0);
3d59229… drh 4754 }
4e683ef… drh 4755 if( !g.repositoryOpen ){
4e683ef… drh 4756 globalFlag = 1;
4e683ef… drh 4757 }
4182079… drh 4758 if( unsetFlag && g.argc!=3 ){
4182079… drh 4759 usage("PROPERTY ?-global?");
4182079… drh 4760 }
0d4a31a… preben 4761 if( valueFlag ){
0d4a31a… preben 4762 if( g.argc!=3 ){
0d4a31a… preben 4763 fossil_fatal("--value is only supported when qurying a given property");
0d4a31a… preben 4764 }
0d4a31a… preben 4765 }
32f8da0… drh 4766
f74f701… drh 4767 for(i=0; i<nSetting; i++){
74a5e10… drh 4768 print_setting(&aSetting[i], 0, bIfChng);
32f8da0… drh 4769 int n = (int)strlen(zName);
816bc43… mistachkin 4770 const Setting *pSetting = db_find_setting(zName, !exactFlag);
32f8da0… drh 4771 if( pSetting==0 ){
07f6780… drh 4772 fossil_fatal("no such setting: %s", zName);
07f6780… drh 4773 }
32f8da0… drh 4774 if( globalFlag && fossil_strcmp(pSetting->name, "manifest")==0 ){
d13054c… drh 4775 fossil_fatal("cannot set 'manifest' globally");
d13054c… drh 4776 }
3b990b5… drh 4777 if( unsetFlag || g.argc==4 ){
32f8da0… drh 4778 int isManifest = fossil_strcmp(pSetting->name, "manifest")==0;
53db40e… drh 4779 if( n!=(int)strlen(pSetting[0].name) && pSetting[1].name &&
c94efdf… jan.nijtmans 4780 fossil_strncmp(pSetting[1].name, zName, n)==0 ){
32f8da0… drh 4781 Blob x;
32f8da0… drh 4782 int i;
32f8da0… drh 4783 blob_init(&x,0,0);
32f8da0… drh 4784 for(i=0; pSetting[i].name; i++){
32f8da0… drh 4785 if( fossil_strncmp(pSetting[i].name,zName,n)!=0 ) break;
32f8da0… drh 4786 blob_appendf(&x, " %s", pSetting[i].name);
32f8da0… drh 4787 }
32f8da0… drh 4788 fossil_fatal("ambiguous setting \"%s\" - might be:%s",
32f8da0… drh 4789 zName, blob_str(&x));
32f8da0… drh 4790 }
32f8da0… drh 4791 if( globalFlag && isManifest ){
32f8da0… drh 4792 fossil_fatal("cannot set 'manifest' globally");
32f8da0… drh 4793 }
32f8da0… drh 4794 if( unsetFlag ){
0a5d0e1… drh 4795 db_unset(pSetting->name/*works-like:"x"*/, globalFlag);
32f8da0… drh 4796 }else{
f741baa… drh 4797 db_protect_only(PROTECT_NONE);
0a5d0e1… drh 4798 db_set(pSetting->name/*works-like:"x"*/, g.argv[3], globalFlag);
f741baa… drh 4799 db_protect_pop();
32f8da0… drh 4800 }
32f8da0… drh 4801 if( isManifest && g.localOpen ){
32f8da0… drh 4802 manifest_to_disk(db_lget_int("checkout", 0));
32f8da0… drh 4803 }
32f8da0… drh 4804 }else{
816bc43… mistachkin 4805 while( pSetting->name ){
816bc43… mistachkin 4806 if( exactFlag ){
816bc43… mistachkin 4807 if( fossil_strcmp(pSetting->name,zName)!=0 ) break;
816bc43… mistachkin 4808 }else{
816bc43… mistachkin 4809 if( fossil_strncmp(pSetting->name,zName,n)!=0 ) break;
816bc43… mistachkin 4810 }
346e457… drh 4811 if( zSubsys ){
346e457… drh 4812 char *zValue = db_get_for_subsystem(pSetting->name, zSubsys);
346e457… drh 4813 fossil_print("%s (subsystem %s) ->", pSetting->name, zSubsys);
346e457… drh 4814 if( zValue ){
346e457… drh 4815 fossil_print(" [%s]", zValue);
346e457… drh 4816 fossil_free(zValue);
346e457… drh 4817 }
346e457… drh 4818 fossil_print("\n");
346e457… drh 4819 }else{
74a5e10… drh 4820 print_setting(pSetting, valueFlag, bIfChng);
346e457… drh 4821 }
32f8da0… drh 4822 pSetting++;
32f8da0… drh 4823 }
db0c512… drh 4824 }
db0c512… drh 4825 }else{
8dc6873… stephan 4826 usage("?PROPERTY? ?VALUE? ?-global?");
db0c512… drh 4827 }
db0c512… drh 4828 }
db0c512… drh 4829
db0c512… drh 4830 /*
db0c512… drh 4831 ** The input in a timespan measured in days. Return a string which
71c40d3… drh 4832 ** describes that timespan in units of seconds, minutes, hours, days,
71c40d3… drh 4833 ** or years, depending on its duration.
71c40d3… drh 4834 */
71c40d3… drh 4835 char *db_timespan_name(double rSpan){
71c40d3… drh 4836 if( rSpan<0 ) rSpan = -rSpan;
71c40d3… drh 4837 rSpan *= 24.0*3600.0; /* Convert units to seconds */
71c40d3… drh 4838 if( rSpan<120.0 ){
71c40d3… drh 4839 return sqlite3_mprintf("%.1f seconds", rSpan);
71c40d3… drh 4840 }
71c40d3… drh 4841 rSpan /= 60.0; /* Convert units to minutes */
71c40d3… drh 4842 if( rSpan<90.0 ){
71c40d3… drh 4843 return sqlite3_mprintf("%.1f minutes", rSpan);
71c40d3… drh 4844 }
71c40d3… drh 4845 rSpan /= 60.0; /* Convert units to hours */
71c40d3… drh 4846 if( rSpan<=48.0 ){
71c40d3… drh 4847 return sqlite3_mprintf("%.1f hours", rSpan);
71c40d3… drh 4848 }
71c40d3… drh 4849 rSpan /= 24.0; /* Convert units to days */
71c40d3… drh 4850 if( rSpan<=365.0 ){
71c40d3… drh 4851 return sqlite3_mprintf("%.1f days", rSpan);
71c40d3… drh 4852 }
71c40d3… drh 4853 rSpan /= 356.24; /* Convert units to years */
71c40d3… drh 4854 return sqlite3_mprintf("%.1f years", rSpan);
71c40d3… drh 4855 }
71c40d3… drh 4856
71c40d3… drh 4857 /*
71c40d3… drh 4858 ** COMMAND: test-timespan
c3b5f1d… jan.nijtmans 4859 **
8bfd995… rberteig 4860 ** Usage: %fossil test-timespan TIMESTAMP
71c40d3… drh 4861 **
71c40d3… drh 4862 ** Print the approximate span of time from now to TIMESTAMP.
71c40d3… drh 4863 */
71c40d3… drh 4864 void test_timespan_cmd(void){
71c40d3… drh 4865 double rDiff;
71c40d3… drh 4866 if( g.argc!=3 ) usage("TIMESTAMP");
dacc694… jan.nijtmans 4867 sqlite3_open(":memory:", &g.db);
71c40d3… drh 4868 rDiff = db_double(0.0, "SELECT julianday('now') - julianday(%Q)", g.argv[2]);
0aee050… drh 4869 fossil_print("Time differences: %s\n", db_timespan_name(rDiff));
71c40d3… drh 4870 sqlite3_close(g.db);
71c40d3… drh 4871 g.db = 0;
57d8a71… mistachkin 4872 g.repositoryOpen = 0;
aefbd20… mistachkin 4873 g.localOpen = 0;
7ddfd42… drh 4874 }
7ddfd42… drh 4875
7ddfd42… drh 4876 /*
7ddfd42… drh 4877 ** COMMAND: test-without-rowid
c3b5f1d… jan.nijtmans 4878 **
8bfd995… rberteig 4879 ** Usage: %fossil test-without-rowid FILENAME...
7ddfd42… drh 4880 **
7ddfd42… drh 4881 ** Change the Fossil repository FILENAME to make use of the WITHOUT ROWID
4645e9b… drh 4882 ** optimization. FILENAME can also be the configuration database file
4645e9b… drh 4883 ** (~/.fossil or ~/.config/fossil.db) or a local .fslckout or _FOSSIL_ file.
7ddfd42… drh 4884 **
7ddfd42… drh 4885 ** The purpose of this command is for testing the WITHOUT ROWID capabilities
7ddfd42… drh 4886 ** of SQLite. There is no big advantage to using WITHOUT ROWID in Fossil.
7ddfd42… drh 4887 **
7ddfd42… drh 4888 ** Options:
9db696e… danield 4889 ** -n|--dry-run No changes. Just print what would happen.
7ddfd42… drh 4890 */
7ddfd42… drh 4891 void test_without_rowid(void){
7ddfd42… drh 4892 int i, j;
7ddfd42… drh 4893 Stmt q;
7ddfd42… drh 4894 Blob allSql;
7ddfd42… drh 4895 int dryRun = find_option("dry-run", "n", 0)!=0;
7ddfd42… drh 4896 for(i=2; i<g.argc; i++){
06aec61… drh 4897 db_open_or_attach(g.argv[i], "main");
7ddfd42… drh 4898 blob_init(&allSql, "BEGIN;\n", -1);
7ddfd42… drh 4899 db_prepare(&q,
22645e1… drh 4900 "SELECT name, sql FROM main.sqlite_schema "
7ddfd42… drh 4901 " WHERE type='table' AND sql NOT LIKE '%%WITHOUT ROWID%%'"
7ddfd42… drh 4902 " AND name IN ('global_config','shun','concealed','config',"
7ddfd42… drh 4903 " 'plink','tagxref','backlink','vcache');"
7ddfd42… drh 4904 );
7ddfd42… drh 4905 while( db_step(&q)==SQLITE_ROW ){
7ddfd42… drh 4906 const char *zTName = db_column_text(&q, 0);
7ddfd42… drh 4907 const char *zOrigSql = db_column_text(&q, 1);
7ddfd42… drh 4908 Blob newSql;
7ddfd42… drh 4909 blob_init(&newSql, 0, 0);
7ddfd42… drh 4910 for(j=0; zOrigSql[j]; j++){
7ddfd42… drh 4911 if( fossil_strnicmp(zOrigSql+j,"unique",6)==0 ){
7ddfd42… drh 4912 blob_append(&newSql, zOrigSql, j);
7ddfd42… drh 4913 blob_append(&newSql, "PRIMARY KEY", -1);
7ddfd42… drh 4914 zOrigSql += j+6;
7ddfd42… drh 4915 j = -1;
7ddfd42… drh 4916 }
7ddfd42… drh 4917 }
7ddfd42… drh 4918 blob_append(&newSql, zOrigSql, -1);
49b0ff1… drh 4919 blob_append_sql(&allSql,
49b0ff1… drh 4920 "ALTER TABLE \"%w\" RENAME TO \"x_%w\";\n"
7ddfd42… drh 4921 "%s WITHOUT ROWID;\n"
49b0ff1… drh 4922 "INSERT INTO \"%w\" SELECT * FROM \"x_%w\";\n"
49b0ff1… drh 4923 "DROP TABLE \"x_%w\";\n",
49b0ff1… drh 4924 zTName, zTName, blob_sql_text(&newSql), zTName, zTName, zTName
7ddfd42… drh 4925 );
f3455a5… drh 4926 fossil_print("Converting table %s of %s to WITHOUT ROWID.\n",
f3455a5… drh 4927 zTName, g.argv[i]);
7ddfd42… drh 4928 blob_reset(&newSql);
7ddfd42… drh 4929 }
49b0ff1… drh 4930 blob_append_sql(&allSql, "COMMIT;\n");
7ddfd42… drh 4931 db_finalize(&q);
7ddfd42… drh 4932 if( dryRun ){
7ddfd42… drh 4933 fossil_print("SQL that would have been evaluated:\n");
f3455a5… drh 4934 fossil_print("%.78c\n", '-');
49b0ff1… drh 4935 fossil_print("%s", blob_sql_text(&allSql));
7ddfd42… drh 4936 }else{
49b0ff1… drh 4937 db_multi_exec("%s", blob_sql_text(&allSql));
7ddfd42… drh 4938 }
7ddfd42… drh 4939 blob_reset(&allSql);
7ddfd42… drh 4940 db_close(1);
7ddfd42… drh 4941 }
f3455a5… drh 4942 }
f3455a5… drh 4943
f3455a5… drh 4944 /*
f3455a5… drh 4945 ** Make sure the adminlog table exists. Create it if it does not
f3455a5… drh 4946 */
f3455a5… drh 4947 void create_admin_log_table(void){
f3455a5… drh 4948 static int once = 0;
f3455a5… drh 4949 if( once ) return;
2da6010… stephan 4950 if( !db_table_exists("repository","admin_log") ){
2da6010… stephan 4951 once = 1;
2da6010… stephan 4952 db_multi_exec(
2da6010… stephan 4953 "CREATE TABLE repository.admin_log(\n"
2da6010… stephan 4954 " id INTEGER PRIMARY KEY,\n"
2da6010… stephan 4955 " time INTEGER, -- Seconds since 1970\n"
2da6010… stephan 4956 " page TEXT, -- path of page\n"
2da6010… stephan 4957 " who TEXT, -- User who made the change\n"
2da6010… stephan 4958 " what TEXT -- What changed\n"
2da6010… stephan 4959 ")"
2da6010… stephan 4960 );
2da6010… stephan 4961 }
f3455a5… drh 4962 }
f3455a5… drh 4963
f3455a5… drh 4964 /*
f3455a5… drh 4965 ** Write a message into the admin_event table, if admin logging is
f3455a5… drh 4966 ** enabled via the admin-log configuration option.
f3455a5… drh 4967 */
f3455a5… drh 4968 void admin_log(const char *zFormat, ...){
f3455a5… drh 4969 Blob what = empty_blob;
f3455a5… drh 4970 va_list ap;
f3455a5… drh 4971 if( !db_get_boolean("admin-log", 0) ){
f3455a5… drh 4972 /* Potential leak here (on %z params) but
f3455a5… drh 4973 the alternative is to let blob_vappendf()
f3455a5… drh 4974 do it below. */
f3455a5… drh 4975 return;
f3455a5… drh 4976 }
f3455a5… drh 4977 create_admin_log_table();
f3455a5… drh 4978 va_start(ap,zFormat);
f3455a5… drh 4979 blob_vappendf( &what, zFormat, ap );
f3455a5… drh 4980 va_end(ap);
f3455a5… drh 4981 db_multi_exec("INSERT INTO admin_log(time,page,who,what)"
f3455a5… drh 4982 " VALUES(now(), %Q, %Q, %B)",
f3455a5… drh 4983 g.zPath, g.zLogin, &what);
f3455a5… drh 4984 blob_reset(&what);
4636b4b… drh 4985 }
4636b4b… drh 4986
4636b4b… drh 4987 /*
4636b4b… drh 4988 ** COMMAND: test-database-names
4636b4b… drh 4989 **
4636b4b… drh 4990 ** Print the names of the various database files:
4636b4b… drh 4991 ** (1) The main repository database
bc36fdc… danield 4992 ** (2) The local check-out database
4636b4b… drh 4993 ** (3) The global configuration database
4636b4b… drh 4994 */
4636b4b… drh 4995 void test_database_name_cmd(void){
4636b4b… drh 4996 db_find_and_open_repository(OPEN_ANY_SCHEMA, 0);
4636b4b… drh 4997 fossil_print("Repository database: %s\n", g.zRepositoryName);
4636b4b… drh 4998 fossil_print("Local database: %s\n", g.zLocalDbName);
4636b4b… drh 4999 fossil_print("Config database: %s\n", g.zConfigDbName);
1b114d2… drh 5000 }
1b114d2… drh 5001
1b114d2… drh 5002 /*
1b114d2… drh 5003 ** Compute a "fingerprint" on the repository. A fingerprint is used
33d3bf3… km 5004 ** to verify that the repository has not been replaced by a clone
75c45fd… drh 5005 ** of the same repository. More precisely, a fingerprint is used to
1b114d2… drh 5006 ** verify that the mapping between SHA3 hashes and RID values is unchanged.
1b114d2… drh 5007 **
bc36fdc… danield 5008 ** The check-out database ("localdb") stores RID values. When associating
bc36fdc… danield 5009 ** a check-out database against a repository database, it is useful to verify
33d3bf3… km 5010 ** the fingerprint so that we know that the RID values in the check-out
1b114d2… drh 5011 ** database still correspond to the correct entries in the BLOB table of
1b114d2… drh 5012 ** the repository.
1b114d2… drh 5013 **
1b114d2… drh 5014 ** The fingerprint is based on the RCVFROM table. When constructing a
1b114d2… drh 5015 ** new fingerprint, use the most recent RCVFROM entry. (Set rcvid==0 to
1b114d2… drh 5016 ** accomplish this.) When verifying an old fingerprint, use the same
1b114d2… drh 5017 ** RCVFROM entry that generated the fingerprint in the first place.
1b114d2… drh 5018 **
1b114d2… drh 5019 ** The fingerprint consists of the rcvid, a "/", and the MD5 checksum of
1b114d2… drh 5020 ** the remaining fields of the RCVFROM table entry. MD5 is used for this
1b114d2… drh 5021 ** because it is 4x faster than SHA3 and 5x faster than SHA1, and there
1b114d2… drh 5022 ** are no security concerns - this is just a checksum, not a security
1b114d2… drh 5023 ** token.
1b114d2… drh 5024 */
918bcfc… jan.nijtmans 5025 char *db_fingerprint(int rcvid, int iVersion){
1b114d2… drh 5026 char *z = 0;
1b114d2… drh 5027 Blob sql = BLOB_INITIALIZER;
1b114d2… drh 5028 Stmt q;
36d3685… drh 5029 if( iVersion==0 ){
36d3685… drh 5030 /* The original fingerprint algorithm used "quote(mtime)". But this
36d3685… drh 5031 ** could give slightly different answers depending on how the floating-
36d3685… drh 5032 ** point hardware is configured. For example, it gave different
36d3685… drh 5033 ** answers on native Linux versus running under valgrind. */
36d3685… drh 5034 blob_append_sql(&sql,
36d3685… drh 5035 "SELECT rcvid, quote(uid), quote(mtime), quote(nonce), quote(ipaddr)"
36d3685… drh 5036 " FROM rcvfrom"
36d3685… drh 5037 );
36d3685… drh 5038 }else{
36d3685… drh 5039 /* These days, we use "datetime(mtime)" for more consistent answers */
36d3685… drh 5040 blob_append_sql(&sql,
36d3685… drh 5041 "SELECT rcvid, quote(uid), datetime(mtime), quote(nonce), quote(ipaddr)"
36d3685… drh 5042 " FROM rcvfrom"
36d3685… drh 5043 );
36d3685… drh 5044 }
1b114d2… drh 5045 if( rcvid<=0 ){
1b114d2… drh 5046 blob_append_sql(&sql, " ORDER BY rcvid DESC LIMIT 1");
1b114d2… drh 5047 }else{
1b114d2… drh 5048 blob_append_sql(&sql, " WHERE rcvid=%d", rcvid);
1b114d2… drh 5049 }
1b114d2… drh 5050 db_prepare_blob(&q, &sql);
1b114d2… drh 5051 blob_reset(&sql);
1b114d2… drh 5052 if( db_step(&q)==SQLITE_ROW ){
1b114d2… drh 5053 int i;
1b114d2… drh 5054 md5sum_init();
1b114d2… drh 5055 for(i=1; i<=4; i++){
1b114d2… drh 5056 md5sum_step_text(db_column_text(&q,i),-1);
1b114d2… drh 5057 }
1b114d2… drh 5058 z = mprintf("%d/%s",db_column_int(&q,0),md5sum_finish(0));
1b114d2… drh 5059 }
1b114d2… drh 5060 db_finalize(&q);
1b114d2… drh 5061 return z;
1b114d2… drh 5062 }
1b114d2… drh 5063
1b114d2… drh 5064 /*
1b114d2… drh 5065 ** COMMAND: test-fingerprint
1b114d2… drh 5066 **
36d3685… drh 5067 ** Usage: %fossil test-fingerprint ?RCVID?
1b114d2… drh 5068 **
36d3685… drh 5069 ** Display the repository fingerprint using the supplied RCVID or
75c45fd… drh 5070 ** using the latest RCVID if none is given on the command line.
36d3685… drh 5071 ** Show both the legacy and the newer version of the fingerprint,
36d3685… drh 5072 ** and the currently stored fingerprint if there is one.
1b114d2… drh 5073 */
1b114d2… drh 5074 void test_fingerprint(void){
1b114d2… drh 5075 int rcvid = 0;
1b114d2… drh 5076 db_find_and_open_repository(OPEN_ANY_SCHEMA,0);
1b114d2… drh 5077 if( g.argc==3 ){
1b114d2… drh 5078 rcvid = atoi(g.argv[2]);
1b114d2… drh 5079 }else if( g.argc!=2 ){
1b114d2… drh 5080 fossil_fatal("wrong number of arguments");
918bcfc… jan.nijtmans 5081 }
2af7fed… drh 5082 fossil_print("legacy: %z\n", db_fingerprint(rcvid, 0));
36d3685… drh 5083 fossil_print("version-1: %z\n", db_fingerprint(rcvid, 1));
36d3685… drh 5084 if( g.localOpen ){
36d3685… drh 5085 fossil_print("localdb: %z\n", db_lget("fingerprint","(none)"));
36d3685… drh 5086 fossil_print("db_fingerprint_ok(): %d\n", db_fingerprint_ok());
36d3685… drh 5087 }
918bcfc… jan.nijtmans 5088 fossil_print("Fossil version: %s - %.10s %.19s\n",
2af7fed… drh 5089 RELEASE_VERSION, MANIFEST_DATE, MANIFEST_UUID);
1b114d2… drh 5090 }
1b114d2… drh 5091
1b114d2… drh 5092 /*
1b114d2… drh 5093 ** Set the value of the "checkout" entry in the VVAR table.
f72ef85… andybradford 5094 ** If bWriteManifest is non-zero then also attempt to write the manifest
f72ef85… andybradford 5095 ** files to disk.
1b114d2… drh 5096 **
1b114d2… drh 5097 ** Also set "fingerprint" and "checkout-hash".
1b114d2… drh 5098 */
f72ef85… andybradford 5099 void db_set_checkout(int rid, int bWriteManifest){
1b114d2… drh 5100 char *z;
f72ef85… andybradford 5101 if( bWriteManifest ) manifest_to_disk(rid);
1b114d2… drh 5102 db_lset_int("checkout", rid);
fd8816e… jan.nijtmans 5103 if (rid != 0) {
fd8816e… jan.nijtmans 5104 z = db_text(0,"SELECT uuid FROM blob WHERE rid=%d",rid);
fd8816e… jan.nijtmans 5105 db_lset("checkout-hash", z);
fd8816e… jan.nijtmans 5106 fossil_free(z);
fd8816e… jan.nijtmans 5107 z = db_fingerprint(0, 1);
fd8816e… jan.nijtmans 5108 db_lset("fingerprint", z);
fd8816e… jan.nijtmans 5109 fossil_free(z);
fd8816e… jan.nijtmans 5110 }
1b114d2… drh 5111 }
1b114d2… drh 5112
1b114d2… drh 5113 /*
1b114d2… drh 5114 ** Verify that the fingerprint recorded in the "fingerprint" entry
1b114d2… drh 5115 ** of the VVAR table matches the fingerprint on the currently
1b114d2… drh 5116 ** connected repository. Return true if the fingerprint is ok, and
1b114d2… drh 5117 ** return false if the fingerprint does not match.
1b114d2… drh 5118 */
1b114d2… drh 5119 int db_fingerprint_ok(void){
bc36fdc… danield 5120 char *zCkout; /* The fingerprint recorded in the check-out database */
1b114d2… drh 5121 char *zRepo; /* The fingerprint of the repository */
1b114d2… drh 5122 int rc; /* Result */
1b114d2… drh 5123
fd8816e… jan.nijtmans 5124 if( !db_lget_int("checkout", 0) ){
bc36fdc… danield 5125 /* We have an empty check-out, fingerprint is still NULL. */
fd8816e… jan.nijtmans 5126 return 2;
fd8816e… jan.nijtmans 5127 }
ffe7cc5… jan.nijtmans 5128 zCkout = db_text(0,"SELECT value FROM localdb.vvar WHERE name='fingerprint'");
1b114d2… drh 5129 if( zCkout==0 ){
bc36fdc… danield 5130 /* This is an older check-out that does not record a fingerprint.
1b114d2… drh 5131 ** We have to assume everything is ok */
1b114d2… drh 5132 return 2;
1b114d2… drh 5133 }
36d3685… drh 5134 zRepo = db_fingerprint(atoi(zCkout), 1);
1b114d2… drh 5135 rc = fossil_strcmp(zCkout,zRepo)==0;
1b114d2… drh 5136 fossil_free(zRepo);
36d3685… drh 5137 /* If the initial test fails, try again using the older fingerprint
36d3685… drh 5138 ** algorithm */
36d3685… drh 5139 if( !rc ){
36d3685… drh 5140 zRepo = db_fingerprint(atoi(zCkout), 0);
36d3685… drh 5141 rc = fossil_strcmp(zCkout,zRepo)==0;
36d3685… drh 5142 fossil_free(zRepo);
918bcfc… jan.nijtmans 5143 }
36d3685… drh 5144 fossil_free(zCkout);
1b114d2… drh 5145 return rc;
673dc38… stephan 5146 }
673dc38… stephan 5147
673dc38… stephan 5148 /*
673dc38… stephan 5149 ** Adds the given rid to the UNSENT table.
673dc38… stephan 5150 */
673dc38… stephan 5151 void db_add_unsent(int rid){
673dc38… stephan 5152 db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", rid);

Keyboard Shortcuts

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