/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/*
** Copyright (c) 2013 D. Richard Hipp
**
** This program is free software; you can redistribute it and/or
** modify it under the terms of the Simplified BSD License (also
** known as the "2-Clause License" or "FreeBSD License".)
**
** This program is distributed in the hope that it will be useful,
** but without any warranty; without even the implied warranty of
** merchantability or fitness for a particular purpose.
**
** Author contact information:
** drh@hwaci.com
** http://www.hwaci.com/drh/
**
*******************************************************************************
** This file implements (most of) the fsl_xxx APIs related to handling
** configuration data from the db(s).
*/
#include "fossil-scm/fossil-internal.h"
#include <assert.h>
char const * fsl_config_table_for_role(fsl_confdb_t mode){
switch(mode){
case FSL_CONFDB_REPO: return "config";
case FSL_CONFDB_CKOUT: return "vvar";
case FSL_CONFDB_GLOBAL: return "global_config";
default:
assert(!"Invalid fsl_confdb_t value");
return NULL;
}
}
fsl_db * fsl_config_for_role(fsl_cx * f, fsl_confdb_t mode){
switch(mode){
case FSL_CONFDB_REPO: return fsl_cx_db_repo(f);
case FSL_CONFDB_CKOUT: return fsl_cx_db_checkout(f);
case FSL_CONFDB_GLOBAL: return fsl_cx_db_config(f);
default:
assert(!"Invalid fsl_confdb_t value");
return NULL;
}
}
fsl_int32_t fsl_config_get_int32( fsl_cx * f, fsl_confdb_t mode,
fsl_int32_t dflt, char const * key ){
fsl_db * db = fsl_config_for_role(f, mode);
char const * table = fsl_config_table_for_role(mode);
fsl_int32_t rv = dflt;
if(db){
fsl_stmt * st = NULL;
fsl_db_prepare_cached(db, &st,
"SELECT value FROM %s WHERE name=?", table);
if(st){
fsl_stmt_bind_text(st, 1, key, -1, 0);
if(FSL_RC_STEP_ROW==fsl_stmt_step(st)){
rv = fsl_stmt_g_int32(st, 0);
}
fsl_stmt_cached_yield(st);
}
}
return rv;
}
fsl_int32_t fsl_get_int32_local( fsl_cx * f, fsl_int32_t dflt,
char const * key ){
return fsl_config_get_int32( f, FSL_CONFDB_CKOUT, dflt, key );
}
fsl_int32_t fsl_get_int32_repo( fsl_cx * f, fsl_int32_t dflt,
char const * key ){
return fsl_config_get_int32( f, FSL_CONFDB_REPO, dflt, key );
}
fsl_int32_t fsl_get_int32_global( fsl_cx * f, fsl_int32_t dflt,
char const * key ){
return fsl_config_get_int32( f, FSL_CONFDB_GLOBAL, dflt, key );
}
fsl_int64_t fsl_config_get_int64( fsl_cx * f, fsl_confdb_t mode,
fsl_int64_t dflt, char const * key ){
fsl_int64_t rv = dflt;
fsl_db * db = fsl_config_for_role(f, mode);
char const * table = fsl_config_table_for_role(mode);
if(db){
fsl_stmt * st = NULL;
fsl_db_prepare_cached(db, &st,
"SELECT value FROM %s WHERE name=?", table);
if(st){
fsl_stmt_bind_text(st, 1, key, -1, 0);
if(FSL_RC_STEP_ROW==fsl_stmt_step(st)){
rv = fsl_stmt_g_int64(st, 0);
}
fsl_stmt_cached_yield(st);
}
}
return rv;
}
fsl_int64_t fsl_get_int64_local( fsl_cx * f, fsl_int64_t dflt,
char const * key ){
return fsl_config_get_int64( f, FSL_CONFDB_CKOUT, dflt, key );
}
fsl_int64_t fsl_get_int64_repo( fsl_cx * f, fsl_int64_t dflt,
char const * key ){
return fsl_config_get_int64( f, FSL_CONFDB_REPO, dflt, key );
}
fsl_int64_t fsl_get_int64_global( fsl_cx * f, fsl_int64_t dflt,
char const * key ){
return fsl_config_get_int64( f, FSL_CONFDB_GLOBAL, dflt, key );
}
fsl_id_t fsl_config_get_id( fsl_cx * f, fsl_confdb_t mode,
fsl_id_t dflt, char const * key ){
fsl_db * db = fsl_config_for_role(f, mode);
char const * table = fsl_config_table_for_role(mode);
fsl_id_t rv = dflt;
if(db){
fsl_stmt * st = NULL;
fsl_db_prepare_cached(db, &st,
"SELECT value FROM %s WHERE name=?", table);
if(st){
fsl_stmt_bind_text(st, 1, key, -1, 0);
if(FSL_RC_STEP_ROW==fsl_stmt_step(st)){
rv = fsl_stmt_g_id(st, 0);
}
fsl_stmt_cached_yield(st);
}
}
return rv;
}
fsl_id_t fsl_get_id_local( fsl_cx * f, fsl_id_t dflt,
char const * key ){
return fsl_config_get_id( f, FSL_CONFDB_CKOUT, dflt, key );
}
fsl_id_t fsl_get_id_repo( fsl_cx * f, fsl_id_t dflt,
char const * key ){
return fsl_config_get_id( f, FSL_CONFDB_REPO, dflt, key );
}
fsl_id_t fsl_get_id_global( fsl_cx * f, fsl_id_t dflt,
char const * key ){
return fsl_config_get_id( f, FSL_CONFDB_GLOBAL, dflt, key );
}
fsl_double_t fsl_config_get_double( fsl_cx * f, fsl_confdb_t mode,
fsl_double_t dflt, char const * key ){
fsl_db * db = fsl_config_for_role(f, mode);
char const * table = fsl_config_table_for_role(mode);
fsl_double_t rv = dflt;
if(db){
fsl_stmt * st = NULL;
fsl_db_prepare_cached(db, &st,
"SELECT value FROM %s WHERE name=?", table);
if(st){
fsl_stmt_bind_text(st, 1, key, -1, 0);
if(FSL_RC_STEP_ROW==fsl_stmt_step(st)){
rv = fsl_stmt_g_double(st, 0);
}
fsl_stmt_cached_yield(st);
}
}
return rv;
}
fsl_double_t fsl_get_double_local( fsl_cx * f, fsl_double_t dflt,
char const * key ){
return fsl_config_get_double( f, FSL_CONFDB_CKOUT, dflt, key );
}
fsl_double_t fsl_get_double_repo( fsl_cx * f, fsl_double_t dflt,
char const * key ){
return fsl_config_get_double( f, FSL_CONFDB_REPO, dflt, key );
}
fsl_double_t fsl_get_double_global( fsl_cx * f, fsl_double_t dflt,
char const * key ){
return fsl_config_get_double( f, FSL_CONFDB_GLOBAL, dflt, key );
}
char * fsl_config_get_text( fsl_cx * f, fsl_confdb_t mode,
char const * key, fsl_size_t * len ){
fsl_db * db = fsl_config_for_role(f, mode);
char const * table = fsl_config_table_for_role(mode);
char * rv = NULL;
if(db){
fsl_stmt * st = NULL;
fsl_db_prepare_cached(db, &st,
"SELECT value FROM %s WHERE name=?", table);
if(st){
fsl_stmt_bind_text(st, 1, key, -1, 0);
if(FSL_RC_STEP_ROW==fsl_stmt_step(st)){
char const * s = fsl_stmt_g_text(st, 0, len);
rv = s ? fsl_strndup( s, len ? (fsl_int_t)*len : -1) : NULL;
}
fsl_stmt_cached_yield(st);
}
}
return rv;
}
char * fsl_get_text_local( fsl_cx * f, char const * key,
fsl_size_t * len ){
return fsl_config_get_text( f, FSL_CONFDB_CKOUT, key, len );
}
char * fsl_get_text_repo( fsl_cx * f, char const * key,
fsl_size_t * len ){
return fsl_config_get_text( f, FSL_CONFDB_REPO, key, len );
}
char * fsl_get_text_global( fsl_cx * f, char const * key,
fsl_size_t * len ){
return fsl_config_get_text( f, FSL_CONFDB_GLOBAL, key, len );
}
int fsl_config_get_buffer( fsl_cx * f, fsl_confdb_t mode,
char const * key, fsl_buffer * b ){
fsl_db * db = fsl_config_for_role(f, mode);
char const * table = fsl_config_table_for_role(mode);
int rc = FSL_RC_NOT_FOUND;
if(db){
fsl_stmt * st = NULL;
rc = fsl_db_prepare_cached(db, &st,
"SELECT value FROM %s WHERE name=?", table);
if(!rc){
fsl_stmt_bind_text(st, 1, key, -1, 0);
if(FSL_RC_STEP_ROW==fsl_stmt_step(st)){
fsl_size_t len = 0;
char const * s = fsl_stmt_g_text(st, 0, &len);
rc = s ? fsl_buffer_append(b, s, len) : 0;
}else{
rc = FSL_RC_NOT_FOUND;
}
fsl_stmt_cached_yield(st);
}
}
return rc;
}
char fsl_config_get_bool( fsl_cx * f,
fsl_confdb_t mode,
char dflt,
char const * key ){
int rc;
char rv = dflt;
fsl_stmt * st = NULL;
char const * table = fsl_config_table_for_role(mode);
fsl_db * db;
if(!f || !key || !*key) return dflt;
db = fsl_config_for_role(f, mode);
if(!db) return dflt;
rc = fsl_db_prepare_cached(db, &st,
"SELECT value FROM %s WHERE name=?",
/* Normally we wouldn't use %s in a
cached statement, but we're only
expecting 3 values for table here
(config, vvar, and global_config) and
each one will only be cached once.
*/
table);
if(!rc){
fsl_stmt_bind_text(st, 1, key, -1, 0);
if(FSL_RC_STEP_ROW==fsl_stmt_step(st)){
char const * col = fsl_stmt_g_text(st, 1, NULL);
rv = col ? fsl_str_bool(col) : dflt /* 0? */;
}
fsl_stmt_cached_yield(st);
}
return rv;
}
int fsl_config_set_text( fsl_cx * f, fsl_confdb_t mode,
char const * key, char const * val ){
char const * table = fsl_config_table_for_role(mode);
fsl_db * db = fsl_config_for_role(f,mode);
if(!db || !key) return FSL_RC_MISUSE;
else if(!*key) return FSL_RC_RANGE;
return fsl_db_exec(db,
"REPLACE INTO %s(name,value) VALUES(%Q,%Q)",
table, key, val);
}
int fsl_config_set_blob( fsl_cx * f, fsl_confdb_t mode,
char const * key, fsl_buffer const * val ){
char const * table = fsl_config_table_for_role(mode);
fsl_db * db = fsl_config_for_role(f,mode);
if(!db || !key) return FSL_RC_MISUSE;
else if(!*key) return FSL_RC_RANGE;
else{
fsl_stmt st = fsl_stmt_empty;
int rc = fsl_db_prepare(db,&st,
"REPLACE INTO %s(name,value) VALUES(%Q,?)",
table, key);
if(!rc){
rc = fsl_stmt_bind_blob(&st, 1, val->mem,
(fsl_int_t)val->used, 0);
if(!rc){
rc = fsl_stmt_step(&st);
rc = (FSL_RC_STEP_DONE == rc)
? 0 : rc;
}
fsl_stmt_finalize(&st);
}
return rc;
}
}
int fsl_config_set_int32( fsl_cx * f, fsl_confdb_t mode,
char const * key, fsl_int32_t val ){
char const * table = fsl_config_table_for_role(mode);
fsl_db * db = fsl_config_for_role(f,mode);
if(!db || !key) return FSL_RC_MISUSE;
else if(!*key) return FSL_RC_RANGE;
return fsl_db_exec(db,
"REPLACE INTO %s(name,value) "
"VALUES(%Q,%"FSL_INT32_T_PFMT")",
table, key, (fsl_int32_t)val);
}
int fsl_config_set_int64( fsl_cx * f, fsl_confdb_t mode,
char const * key, fsl_int64_t val ){
char const * table = fsl_config_table_for_role(mode);
fsl_db * db = fsl_config_for_role(f,mode);
if(!db || !key) return FSL_RC_MISUSE;
else if(!*key) return FSL_RC_RANGE;
return fsl_db_exec(db,
"REPLACE INTO %s(name,value) "
"VALUES(%Q,%"FSL_INT64_T_PFMT")",
table, key, (fsl_int64_t)val);
}
int fsl_config_set_double( fsl_cx * f, fsl_confdb_t mode,
char const * key, fsl_double_t val ){
char const * table = fsl_config_table_for_role(mode);
fsl_db * db = fsl_config_for_role(f,mode);
if(!db || !key) return FSL_RC_MISUSE;
else if(!*key) return FSL_RC_RANGE;
return fsl_db_exec(db,
"REPLACE INTO %s(name,value) "
"VALUES(%Q,%"FSL_DOUBLE_T_PFMT")",
table, key, (fsl_double_t)val);
}
int fsl_config_set_bool( fsl_cx * f, fsl_confdb_t mode,
char const * key, char val ){
char const * table = fsl_config_table_for_role(mode);
fsl_db * db = fsl_config_for_role(f,mode);
if(!db || !key) return FSL_RC_MISUSE;
else if(!*key) return FSL_RC_RANGE;
return fsl_db_exec(db,
"REPLACE INTO %s(name,value) "
"VALUES(%Q,%d)",
table, key, val ? 1 : 0);
}
/*
TODO: the infrastructure from v1's configure.c and db.c which deals
with the config db and the list of known/allowed settings.
*/
static const struct {
const char *zName; /* Name of the configuration set */
int groupMask; /* Mask for that configuration set */
const char *zHelp; /* What it does */
} fslConfigGroups[] = {
{ "/email", FSL_CONFIGSET_ADDR, "Concealed email addresses in tickets" },
{ "/project", FSL_CONFIGSET_PROJ, "Project name and description" },
{ "/skin", FSL_CONFIGSET_SKIN | FSL_CONFIGSET_CSS,
"Web interface appearance settings" },
{ "/css", FSL_CONFIGSET_CSS, "Style sheet" },
{ "/shun", FSL_CONFIGSET_SHUN, "List of shunned artifacts" },
{ "/ticket", FSL_CONFIGSET_TKT, "Ticket setup", },
{ "/user", FSL_CONFIGSET_USER, "Users and privilege settings" },
{ "/xfer", FSL_CONFIGSET_XFER, "Transfer setup", },
{ "/all", FSL_CONFIGSET_ALL, "All of the above" },
{NULL, 0, NULL}
};
/*
** The following is a list of settings that we are willing to
** transfer.
**
** Setting names that begin with an alphabetic characters refer to
** single entries in the CONFIG table. Setting names that begin with
** "@" are for special processing.
*/
static struct {
const char *zName; /* Name of the configuration parameter */
int groupMask; /* Which config groups is it part of */
} fslConfigXfer[] = {
{ "css", FSL_CONFIGSET_CSS },
{ "header", FSL_CONFIGSET_SKIN },
{ "footer", FSL_CONFIGSET_SKIN },
{ "logo-mimetype", FSL_CONFIGSET_SKIN },
{ "logo-image", FSL_CONFIGSET_SKIN },
{ "background-mimetype", FSL_CONFIGSET_SKIN },
{ "background-image", FSL_CONFIGSET_SKIN },
{ "index-page", FSL_CONFIGSET_SKIN },
{ "timeline-block-markup", FSL_CONFIGSET_SKIN },
{ "timeline-max-comment", FSL_CONFIGSET_SKIN },
{ "timeline-plaintext", FSL_CONFIGSET_SKIN },
{ "adunit", FSL_CONFIGSET_SKIN },
{ "adunit-omit-if-admin", FSL_CONFIGSET_SKIN },
{ "adunit-omit-if-user", FSL_CONFIGSET_SKIN },
{ "th1-setup", FSL_CONFIGSET_ALL },
{ "tcl", FSL_CONFIGSET_SKIN|FSL_CONFIGSET_TKT|FSL_CONFIGSET_XFER },
{ "tcl-setup", FSL_CONFIGSET_SKIN|FSL_CONFIGSET_TKT|FSL_CONFIGSET_XFER },
{ "project-name", FSL_CONFIGSET_PROJ },
{ "project-description", FSL_CONFIGSET_PROJ },
{ "manifest", FSL_CONFIGSET_PROJ },
{ "binary-glob", FSL_CONFIGSET_PROJ },
{ "clean-glob", FSL_CONFIGSET_PROJ },
{ "ignore-glob", FSL_CONFIGSET_PROJ },
{ "keep-glob", FSL_CONFIGSET_PROJ },
{ "crnl-glob", FSL_CONFIGSET_PROJ },
{ "encoding-glob", FSL_CONFIGSET_PROJ },
{ "empty-dirs", FSL_CONFIGSET_PROJ },
{ "allow-symlinks", FSL_CONFIGSET_PROJ },
{ "ticket-table", FSL_CONFIGSET_TKT },
{ "ticket-common", FSL_CONFIGSET_TKT },
{ "ticket-change", FSL_CONFIGSET_TKT },
{ "ticket-newpage", FSL_CONFIGSET_TKT },
{ "ticket-viewpage", FSL_CONFIGSET_TKT },
{ "ticket-editpage", FSL_CONFIGSET_TKT },
{ "ticket-reportlist", FSL_CONFIGSET_TKT },
{ "ticket-report-template", FSL_CONFIGSET_TKT },
{ "ticket-key-template", FSL_CONFIGSET_TKT },
{ "ticket-title-expr", FSL_CONFIGSET_TKT },
{ "ticket-closed-expr", FSL_CONFIGSET_TKT },
{ "@reportfmt", FSL_CONFIGSET_TKT },
{ "@user", FSL_CONFIGSET_USER },
{ "@concealed", FSL_CONFIGSET_ADDR },
{ "@shun", FSL_CONFIGSET_SHUN },
{ "xfer-common-script", FSL_CONFIGSET_XFER },
{ "xfer-push-script", FSL_CONFIGSET_XFER },
};
/*
** Return a pointer to a string that contains the RHS of an IN
** operator that will select CONFIG table names that are part of the
** configuration that matches iMatch. The returned string must
** eventually be fsl_free()'d.
*/
char *fsl_config_inop_rhs(int iMask){
fsl_buffer x = fsl_buffer_empty;
const char *zSep = "";
const int n = sizeof(fslConfigXfer)/sizeof(fslConfigXfer[0]);
int i;
int rc = fsl_buffer_append(&x, "(", 1);
for(i=0; !rc && (i<n); i++){
if( (fslConfigXfer[i].groupMask & iMask)==0 ) continue;
if( fslConfigXfer[i].zName[0]=='@' ) continue;
rc = fsl_buffer_appendf(&x, "%s'%s'", zSep, fslConfigXfer[i].zName);
zSep = ",";
}
if(!rc){
rc = fsl_buffer_append(&x, ")", 1);
}
if(rc){
fsl_buffer_clear(&x);
assert(!x.mem);
}else{
fsl_buffer_resize(&x, x.used);
}
return (char *)x.mem;
}
struct fsl_config_ctrl {
/** Name of the setting */
char const *name;
/** Internal variable name used by db_set() */
char const *var;
/** Historical (HTML UI). Width of display. 0 for boolean
values.
*/
int width;
/** Is this setting versionable? */
int versionable;
/** Default value */
char const *def;
};
typedef struct fsl_config_ctrl fsl_config_ctrl;
static struct fsl_config_ctrl const fslConfigCtrl[] = {
{ "access-log", 0, 0, 0, "off" },
{ "allow-symlinks",0, 0, 1, "off" },
{ "auto-captcha", "autocaptcha", 0, 0, "on" },
{ "auto-hyperlink",0, 0, 0, "on", },
{ "auto-shun", 0, 0, 0, "on" },
{ "autosync", 0, 0, 0, "on" },
{ "binary-glob", 0, 40, 1, "" },
{ "clearsign", 0, 0, 0, "off" },
#if defined(_WIN32) || defined(__CYGWIN__) || defined(__DARWIN__) || defined(__APPLE__)
{ "case-sensitive",0, 0, 0, "off" },
#else
{ "case-sensitive",0, 0, 0, "on" },
#endif
{ "clean-glob", 0, 40, 1, "" },
{ "crnl-glob", 0, 40, 1, "" },
{ "default-perms", 0, 16, 0, "u" },
{ "diff-binary", 0, 0, 0, "on" },
{ "diff-command", 0, 40, 0, "" },
{ "dont-push", 0, 0, 0, "off" },
{ "editor", 0, 32, 0, "" },
{ "empty-dirs", 0, 40, 1, "" },
{ "encoding-glob", 0, 40, 1, "" },
{ "gdiff-command", 0, 40, 0, "gdiff" },
{ "gmerge-command",0, 40, 0, "" },
{ "http-port", 0, 16, 0, "8080" },
{ "https-login", 0, 0, 0, "off" },
{ "ignore-glob", 0, 40, 1, "" },
{ "keep-glob", 0, 40, 1, "" },
{ "localauth", 0, 0, 0, "off" },
{ "main-branch", 0, 40, 0, "trunk" },
{ "manifest", 0, 0, 1, "off" },
{ "max-upload", 0, 25, 0, "250000" },
{ "mtime-changes", 0, 0, 0, "on" },
{ "pgp-command", 0, 40, 0, "gpg --clearsign -o " },
{ "proxy", 0, 32, 0, "off" },
{ "relative-paths",0, 0, 0, "on" },
{ "repo-cksum", 0, 0, 0, "on" },
{ "self-register", 0, 0, 0, "off" },
{ "ssh-command", 0, 40, 0, "" },
{ "ssl-ca-location",0, 40, 0, "" },
{ "ssl-identity", 0, 40, 0, "" },
{ "tcl", 0, 0, 0, "off" },
{ "tcl-setup", 0, 40, 0, "" },
{ "th1-setup", 0, 40, 0, "" },
{ "web-browser", 0, 32, 0, "" },
{ "white-foreground", 0, 0, 0, "off" },
{ 0,0,0,0,0 }
};
/*
** Return a pointer to a string that contains the RHS of an IN operator
** that will select CONFIG table names that are in the list of control
** settings.
*/
char *fsl_db_setting_inop_rhs(){
fsl_buffer x = fsl_buffer_empty;
const char *zSep = "";
fsl_config_ctrl const * ct = &fslConfigCtrl[0];
int rc = fsl_buffer_append(&x, "(", 1);
for( ; !rc && ct && ct->name; ++ct){
rc = fsl_buffer_appendf(&x, "%s'%s'", zSep, ct->name);
zSep = ",";
}
if(!rc){
rc = fsl_buffer_append(&x, ")", 1);
}
if(rc){
fsl_buffer_clear(&x);
assert(!x.mem);
}else{
fsl_buffer_resize(&x, x.used);
}
return (char *)x.mem;
}