Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Postfix/src/global/   (Postfix Mailserver Version 3.11©)  Datei vom 24.5.2025 mit Größe 10 kB image not shown  

Quelle  dict_sqlite.c

  Sprache: C
 

/*++
/* NAME
/* dict_sqlite 3
/* SUMMARY
/* dictionary manager interface to SQLite3 databases
/* SYNOPSIS
/* #include <dict_sqlite.h>
/*
/* DICT *dict_sqlite_open(name, open_flags, dict_flags)
/* const char *name;
/* int open_flags;
/* int dict_flags;
/* DESCRIPTION
/* dict_sqlite_open() creates a dictionary of type 'sqlite'.
/* This dictionary is an interface for the postfix key->value
/* mappings to SQLite.  The result is a pointer to the installed
/* dictionary.
/* .PP
/* Arguments:
/* .IP name
/* Either the path to the SQLite configuration file (if it
/* starts with '/' or '.'), or the prefix which will be used
/* to obtain main.cf configuration parameters for this search.
/*
/* In the first case, the configuration parameters below are
/* specified in the file as \fIname\fR=\fIvalue\fR pairs.
/*
/* In the second case, the configuration parameters are prefixed
/* with the value of \fIname\fR and an underscore, and they
/* are specified in main.cf.  For example, if this value is
/* \fIsqlitecon\fR, the parameters would look like
/* \fIsqlitecon_dbpath\fR, \fIsqlitecon_query\fR, and so on.
/* .IP open_flags
/* Must be O_RDONLY.
/* .IP dict_flags
/* See dict_open(3).
/* DIAGNOSTICS
/* dict_sqlite_open() logs a warning when the query parameter value
/* does not use the recommended '' quotes to protect against SQL
/* injection (bad examples; no quotes or "" quotes).
/* SEE ALSO
/* dict(3) generic dictionary manager
/* sqlite_table(5) Postfix sqlite client configuration
/* AUTHOR(S)
/* Axel Steiner
/* ast@treibsand.com
/*
/* Adopted and updated by:
/* Wietse Venema
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
/*
/* Wietse Venema
/* porcupine.org
/*--*/


/* System library. */

#include <sys_defs.h>
#include <string.h>
#include <ctype.h>

#ifdef HAS_SQLITE
#include <sqlite3.h>

#if !defined(SQLITE_VERSION_NUMBER) || (SQLITE_VERSION_NUMBER < 3005004)
#define sqlite3_prepare_v2 sqlite3_prepare
#endif
#if !defined(SQLITE_VERSION_NUMBER) || (SQLITE_VERSION_NUMBER < 3005000)
#define sqlite3_open_v2(fname,ppDB,flags,zVfs) sqlite_open(fname,ppDB)
#endif

/* Utility library. */

#include <msg.h>
#include <dict.h>
#include <vstring.h>
#include <stringops.h>
#include <mymalloc.h>

/* Global library. */

#include <cfg_parser.h>
#include <db_common.h>

/* Application-specific. */

#include <dict_sqlite.h>

typedef struct {
    DICT    dict;   /* generic member */
    CFG_PARSER *parser;   /* common parameter parser */
    sqlite3 *db;   /* sqlite handle */
    char   *query;   /* db_common_expand() query */
    char   *result_format;  /* db_common_expand() result_format */
    void   *ctx;   /* db_common_parse() context */
    char   *dbpath;   /* dbpath config attribute */
    int     expansion_limit;  /* expansion_limit config attribute */
} DICT_SQLITE;

/* dict_sqlite_quote - escape SQL metacharacters in input string */

static void dict_sqlite_quote(DICT *dict, const char *raw_text, VSTRING *result)
{
    char   *quoted_text;

    quoted_text = sqlite3_mprintf("%q", raw_text);
    /* Fix 20100616 */
    if (quoted_text == 0)
 msg_fatal("dict_sqlite_quote: out of memory");
    vstring_strcat(result, quoted_text);
    sqlite3_free(quoted_text);
}

/* dict_sqlite_close - close the database */

static void dict_sqlite_close(DICT *dict)
{
    const char *myname = "dict_sqlite_close";
    DICT_SQLITE *dict_sqlite = (DICT_SQLITE *) dict;

    if (msg_verbose)
 msg_info("%s: %s", myname, dict_sqlite->parser->name);

    if (sqlite3_close(dict_sqlite->db) != SQLITE_OK)
 msg_fatal("%s: close %s failed", myname, dict_sqlite->parser->name);
    cfg_parser_free(dict_sqlite->parser);
    myfree(dict_sqlite->dbpath);
    myfree(dict_sqlite->query);
    myfree(dict_sqlite->result_format);
    if (dict_sqlite->ctx)
 db_common_free_ctx(dict_sqlite->ctx);
    if (dict->fold_buf)
 vstring_free(dict->fold_buf);
    dict_free(dict);
}

/* dict_sqlite_lookup - find database entry */

static const char *dict_sqlite_lookup(DICT *dict, const char *name)
{
    const char *myname = "dict_sqlite_lookup";
    DICT_SQLITE *dict_sqlite = (DICT_SQLITE *) dict;
    sqlite3_stmt *sql_stmt;
    const char *query_remainder;
    static VSTRING *query;
    static VSTRING *result;
    const char *retval;
    int     expansion = 0;
    int     status;
    int     domain_rc;

    /*
     * In case of return without lookup (skipped key, etc.).
     */

    dict->error = 0;

    /*
     * Don't frustrate future attempts to make Postfix UTF-8 transparent.
     */

    if ((dict->flags & DICT_FLAG_UTF8_ACTIVE) == 0
 && !valid_utf8_stringz(name)) {
 if (msg_verbose)
     msg_info("%s: %s: Skipping lookup of non-UTF-8 key '%s'",
       myname, dict_sqlite->parser->name, name);
 return (0);
    }

    /*
     * Optionally fold the key. Folding may be enabled on-the-fly.
     */

    if (dict->flags & DICT_FLAG_FOLD_FIX) {
 if (dict->fold_buf == 0)
     dict->fold_buf = vstring_alloc(100);
 vstring_strcpy(dict->fold_buf, name);
 name = lowercase(vstring_str(dict->fold_buf));
    }

    /*
     * Apply the optional domain filter for email address lookups.
     */

    if ((domain_rc = db_common_check_domain(dict_sqlite->ctx, name)) == 0) {
 if (msg_verbose)
     msg_info("%s: %s: Skipping lookup of '%s'",
       myname, dict_sqlite->parser->name, name);
 return (0);
    }
    if (domain_rc < 0)
 DICT_ERR_VAL_RETURN(dict, domain_rc, (char *) 0);

    /*
     * Expand the query and query the database.
     */

#define INIT_VSTR(buf, len) do { \
 if (buf == 0) \
  buf = vstring_alloc(len); \
 VSTRING_RESET(buf); \
 VSTRING_TERMINATE(buf); \
    } while (0)

    INIT_VSTR(query, 10);

    if (!db_common_expand(dict_sqlite->ctx, dict_sqlite->query,
     name, 0, query, dict_sqlite_quote))
 return (0);

    if (msg_verbose)
 msg_info("%s: %s: Searching with query %s",
   myname, dict_sqlite->parser->name, vstring_str(query));

    if (sqlite3_prepare_v2(dict_sqlite->db, vstring_str(query), -1,
      &sql_stmt, &query_remainder) != SQLITE_OK)
 msg_fatal("%s: %s: SQL prepare failed: %s\n",
    myname, dict_sqlite->parser->name,
    sqlite3_errmsg(dict_sqlite->db));

    if (*query_remainder && msg_verbose)
 msg_info("%s: %s: Ignoring text at end of query: %s",
   myname, dict_sqlite->parser->name, query_remainder);

    /*
     * Retrieve and expand the result(s).
     */

    INIT_VSTR(result, 10);
    while ((status = sqlite3_step(sql_stmt)) != SQLITE_DONE) {
 if (status == SQLITE_ROW) {
     if (db_common_expand(dict_sqlite->ctx, dict_sqlite->result_format,
       (const char *) sqlite3_column_text(sql_stmt, 0),
     name, result, 0)
  && dict_sqlite->expansion_limit > 0
  && ++expansion > dict_sqlite->expansion_limit) {
  msg_warn("%s: %s: Expansion limit exceeded for key '%s'",
    myname, dict_sqlite->parser->name, name);
  dict->error = DICT_ERR_RETRY;
  break;
     }
 }
 /* Fix 20100616 */
 else {
     msg_warn("%s: %s: SQL step failed for query '%s': %s\n",
       myname, dict_sqlite->parser->name,
       vstring_str(query), sqlite3_errmsg(dict_sqlite->db));
     dict->error = DICT_ERR_RETRY;
     break;
 }
    }

    /*
     * Clean up.
     */

    if (sqlite3_finalize(sql_stmt))
 msg_fatal("%s: %s: SQL finalize failed for query '%s': %s\n",
    myname, dict_sqlite->parser->name,
    vstring_str(query), sqlite3_errmsg(dict_sqlite->db));

    return ((dict->error == 0 && *(retval = vstring_str(result)) != 0) ?
     retval : 0);
}

/* flag_non_recommended_query - as the name says. */

static void flag_non_recommended_query(const char *query,
                   const char *sqlitecf)
{
    const char *cp;
    int     in_quote;
    const int squote = '\'';
    const int dquote = '"';

    for (in_quote = 0, cp = query; *cp != 0; cp++) {
 if (in_quote == 0) {
     if (*cp == squote || *cp == dquote)
  in_quote = *cp;
 } else if (*cp == in_quote) {
     in_quote = 0;
 }
 if (in_quote == squote)
     continue;
 if (*cp == '%') {
     if (cp[1] == '%') {
  cp += 1;
     } else if (ISALNUM(cp[1])) {
  msg_warn("%s:%s: query >%s< contains >%.2s< without the "
    "recommended '' quotes", DICT_TYPE_SQLITE, sqlitecf,
    query, cp);
  cp += 1;
     }
 }
    }
}

/* sqlite_parse_config - parse sqlite configuration file */

static void sqlite_parse_config(DICT_SQLITE *dict_sqlite, const char *sqlitecf)
{
    VSTRING *buf;

    /*
     * Parse the primary configuration parameters, and emulate the legacy
     * query interface if necessary. This simplifies migration from one SQL
     * database type to another.
     */

    dict_sqlite->dbpath = cfg_get_str(dict_sqlite->parser, "dbpath"""10);
    dict_sqlite->query = cfg_get_str(dict_sqlite->parser, "query", NULL, 00);
    if (dict_sqlite->query == 0) {
 buf = vstring_alloc(100);
 db_common_sql_build_query(buf, dict_sqlite->parser);
 dict_sqlite->query = vstring_export(buf);
    }
    /* Flag %[a-zA-Z0-9] if not protected with ''. */
    flag_non_recommended_query(dict_sqlite->query, sqlitecf);
    dict_sqlite->result_format =
 cfg_get_str(dict_sqlite->parser, "result_format""%s"10);
    dict_sqlite->expansion_limit =
 cfg_get_int(dict_sqlite->parser, "expansion_limit"000);

    /*
     * Parse the query / result templates and the optional domain filter.
     */

    dict_sqlite->ctx = 0;
    (void) db_common_parse(&dict_sqlite->dict, &dict_sqlite->ctx,
      dict_sqlite->query, 1);
    (void) db_common_parse(0, &dict_sqlite->ctx, dict_sqlite->result_format, 0);
    db_common_parse_domain(dict_sqlite->parser, dict_sqlite->ctx);

    /*
     * Maps that use substring keys should only be used with the full input
     * key.
     */

    if (db_common_dict_partial(dict_sqlite->ctx))
 dict_sqlite->dict.flags |= DICT_FLAG_PATTERN;
    else
 dict_sqlite->dict.flags |= DICT_FLAG_FIXED;
}

/* dict_sqlite_open - open sqlite database */

DICT   *dict_sqlite_open(const char *name, int open_flags, int dict_flags)
{
    DICT_SQLITE *dict_sqlite;
    CFG_PARSER *parser;

    /*
     * Sanity checks.
     */

    if (open_flags != O_RDONLY)
 return (dict_surrogate(DICT_TYPE_SQLITE, name, open_flags, dict_flags,
          "%s:%s map requires O_RDONLY access mode",
          DICT_TYPE_SQLITE, name));

    /*
     * Open the configuration file.
     */

    if ((parser = cfg_parser_alloc(name)) == 0)
 return (dict_surrogate(DICT_TYPE_SQLITE, name, open_flags, dict_flags,
          "open %s: %m", name));

    dict_sqlite = (DICT_SQLITE *) dict_alloc(DICT_TYPE_SQLITE, name,
          sizeof(DICT_SQLITE));
    dict_sqlite->dict.lookup = dict_sqlite_lookup;
    dict_sqlite->dict.close = dict_sqlite_close;
    dict_sqlite->dict.flags = dict_flags;

    dict_sqlite->parser = parser;
    sqlite_parse_config(dict_sqlite, name);

    if (sqlite3_open_v2(dict_sqlite->dbpath, &dict_sqlite->db,
   SQLITE_OPEN_READONLY, NULL) != SQLITE_OK) {
 DICT   *dict = dict_surrogate(DICT_TYPE_SQLITE, name, open_flags,
         dict_flags, "%s:%s: open database %s: %s: %m",
    DICT_TYPE_SQLITE, name, dict_sqlite->dbpath,
          sqlite3_errmsg(dict_sqlite->db));

 dict_sqlite_close(&dict_sqlite->dict);
 return dict;
    }
    dict_sqlite->dict.owner = cfg_get_owner(dict_sqlite->parser);

    return (&dict_sqlite->dict);
}

#endif

Messung V0.5 in Prozent
C=74 H=95 G=84

¤ Dauer der Verarbeitung: 0.11 Sekunden  (vorverarbeitet am  2026-08-08) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.