/* -*- 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 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 && (iname; ++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; }