/* Some text for messages */
# define EXIM_DBTYPE "sqlite3"
-# /* Access functions */
+/* Utility functionss */
+
+extern uschar *xtextencode(uschar *, int);
+extern int xtextdecode(uschar *, uschar**);
+
+
+/* Access functions */
static inline BOOL
exim_lockfile_needed(void)
int ret;
res->len = (size_t) -1;
-/* fprintf(stderr, "exim_dbget__(%s)\n", s); */
+/* DEBUG(D_hints_lookup) debug_printf_indent("exim_dbget__(%s)\n", s); */
if ((ret = sqlite3_prepare_v2(dbp, CCS s, -1, &statement, NULL)) != SQLITE_OK)
{
-/* fprintf(stderr, "prepare fail: %s\n", sqlite3_errmsg(dbp)); */
+ DEBUG(D_hints_lookup)
+ debug_printf_indent("prepare fail: %s\n", sqlite3_errmsg(dbp));
return FALSE;
}
if (sqlite3_step(statement) != SQLITE_ROW)
{
-/* fprintf(stderr, "step fail: %s\n", sqlite3_errmsg(dbp)); */
+ /* DEBUG(D_hints_lookup)
+ debug_printf_indent("step fail: %s\n", sqlite3_errmsg(dbp)); */
sqlite3_finalize(statement);
return FALSE;
}
# endif
memcpy(res->data, sqlite3_column_blob(statement, 0), res->len);
res->data[res->len] = '\0';
-/* fprintf(stderr, "res %d bytes: '%.*s'\n", (int)res->len, (int)res->len, res->data); */
+/* DEBUG(D_hints_lookup) debug_printf_indent("res %d bytes: '%.*s'\n",
+ (int)res->len, (int)res->len, res->data); */
sqlite3_finalize(statement);
return TRUE;
}
static inline BOOL
exim_dbget(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * res)
{
-# define FMT "SELECT dat FROM tbl WHERE ky = '%.*s';"
-uschar * qry;
+# define FMT "SELECT dat FROM tbl WHERE ky = '%s';"
+uschar * encoded_key, * qry;
int i;
BOOL ret;
# ifdef COMPILE_UTILITY
-/* fprintf(stderr, "exim_dbget(k len %d '%.*s')\n", (int)key->len, (int)key->len, key->data); */
-i = snprintf(NULL, 0, FMT, (int) key->len, key->data)+1;
+if (!(encoded_key = xtextencode(key->data, key->len)))
+ return FALSE;
+# else
+encoded_key = xtextencode(key->data, key->len);
+# endif
+/* DEBUG(D_hints_lookup) debug_printf_indent("exim_dbget(k len %d '%s')\n",
+ (int)key->len, encoded_key); */
+
+# ifdef COMPILE_UTILITY
+i = snprintf(NULL, 0, FMT, encoded_key) + 1;
if (!(qry = malloc(i)))
return FALSE;
-snprintf(CS qry, i, FMT, (int) key->len, key->data);
+snprintf(CS qry, i, FMT, encoded_key);
ret = exim_dbget__(dbp, qry, res);
free(qry);
+free(encoded_key);
# else
-/* fprintf(stderr, "exim_dbget(k len %d '%.*s')\n", (int)key->len, (int)key->len, key->data); */
-qry = string_sprintf(FMT, (int) key->len, key->data);
+qry = string_sprintf(FMT, encoded_key);
ret = exim_dbget__(dbp, qry, res);
# endif
}
/* Note that we return claiming a duplicate record for any error.
-It seem not uncommon to get a "database is locked" error. */
+It seem not uncommon to get a "database is locked" error.
+
+Keys are stored xtext-encoded (which is mostly readable, for plaintext).
+Values are stored in a BLOB type in the DB, for which the SQL interface
+is hex-encoded. */
# define EXIM_DBPUTB_OK 0
# define EXIM_DBPUTB_DUP (-1)
exim_s_dbp(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * data, const uschar * alt)
{
int hlen = data->len * 2, off = 0, res;
-# define FMT "INSERT OR %s INTO tbl (ky,dat) VALUES ('%.*s', X'%.*s');"
-uschar * qry;
+# define FMT "INSERT OR %s INTO tbl (ky,dat) VALUES ('%s', X'%.*s');"
+uschar * encoded_key, * qry;
# ifdef COMPILE_UTILITY
uschar * hex = malloc(hlen+1);
if (!hex) return EXIM_DBPUTB_DUP; /* best we can do */
uschar * hex = store_get(hlen+1, data->data);
# endif
+/* Encode the value for the SQL API */
+
for (const uschar * s = data->data, * t = s + data->len; s < t; s++, off += 2)
sprintf(CS hex + off, "%02X", *s);
# ifdef COMPILE_UTILITY
-res = snprintf(CS hex, 0, FMT, alt, (int) key->len, key->data, hlen, hex) +1;
+if (!(encoded_key = xtextencode(key->data, key->len)))
+ return EXIM_DBPUTB_DUP;
+res = snprintf(CS hex, 0, FMT, alt, encoded_key, hlen, hex) +1;
if (!(qry = malloc(res))) return EXIM_DBPUTB_DUP;
-snprintf(CS qry, res, FMT, alt, (int) key->len, key->data, hlen, hex);
-/* fprintf(stderr, "exim_s_dbp(%s)\n", qry); */
+snprintf(CS qry, res, FMT, alt, encoded_key, hlen, hex);
+DEBUG(D_hints_lookup) debug_printf_indent("exim_s_dbp(%s)\n", qry);
+
res = sqlite3_exec(dbp, CS qry, NULL, NULL, NULL);
free(qry);
+free(encoded_key);
free(hex);
+
# else
-qry = string_sprintf(FMT, alt, (int) key->len, key->data, hlen, hex);
-/* fprintf(stderr, "exim_s_dbp(%s)\n", qry); */
+encoded_key = xtextencode(key->data, key->len);
+qry = string_sprintf(FMT, alt, encoded_key, hlen, hex);
+/* DEBUG(D_hints_lookup) debug_printf_indent("exim_s_dbp(%s)\n", qry); */
+
res = sqlite3_exec(dbp, CS qry, NULL, NULL, NULL);
-/* fprintf(stderr, "exim_s_dbp res %d\n", res); */
+/* DEBUG(D_hints_lookup) debug_printf_indent("exim_s_dbp res %d\n", res); */
# endif
# ifdef COMPILE_UTILITY
if (res != SQLITE_OK)
- fprintf(stderr, "sqlite3_exec: %s\n", sqlite3_errmsg(dbp));
+ DEBUG(D_hints_lookup)
+ debug_printf_indent("sqlite3_exec: %s\n", sqlite3_errmsg(dbp));
# endif
return res == SQLITE_OK ? EXIM_DBPUTB_OK : EXIM_DBPUTB_DUP;
static inline int
exim_dbput(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * data)
{
-/* fprintf(stderr, "exim_dbput()\n"); */
+/* DEBUG(D_hints_lookup) debug_printf_indent("exim_dbput()\n"); */
(void) exim_s_dbp(dbp, key, data, US"REPLACE");
return 0;
}
static inline int
exim_dbdel(EXIM_DB * dbp, EXIM_DATUM * key)
{
-# define FMT "DELETE FROM tbl WHERE ky = '%.*s';"
-uschar * qry;
+# define FMT "DELETE FROM tbl WHERE ky = '%s';"
+uschar * encoded_key, * qry;
int res;
# ifdef COMPILE_UTILITY
-res = snprintf(NULL, 0, FMT, (int) key->len, key->data) +1; /* res includes nul */
+if (!(encoded_key = xtextencode(key->data, key->len)))
+ return EXIM_DBPUTB_DUP;
+res = snprintf(NULL, 0, FMT, encoded_key) +1; /* res includes nul */
if (!(qry = malloc(res))) return SQLITE_NOMEM;
-snprintf(CS qry, res, FMT, (int) key->len, key->data);
+snprintf(CS qry, res, FMT, encoded_key);
res = sqlite3_exec(dbp, CS qry, NULL, NULL, NULL);
free(qry);
+
# else
-qry = string_sprintf(FMT, (int) key->len, key->data);
+encoded_key = xtextencode(key->data, key->len);
+qry = string_sprintf(FMT, encoded_key);
res = sqlite3_exec(dbp, CS qry, NULL, NULL, NULL);
+
# endif
-return res;
+return res == SQLITE_OK ? EXIM_DBPUTB_OK : EXIM_DBPUTB_DUP;
# undef FMT
}
}
/* EXIM_DBSCAN */
-/* Note that we return the (next) key, not the record value */
+/* Note that we return the (next) key, not the record value.
+We allocate memory for the return. */
+
static inline BOOL
exim_dbscan(EXIM_DB * dbp, EXIM_DATUM * key, EXIM_DATUM * res, BOOL first,
EXIM_CURSOR * cursor)
{
# define FMT "SELECT ky FROM tbl ORDER BY ky LIMIT 1 OFFSET %d;"
uschar * qry;
-int i;
+EXIM_DATUM encoded_key;
BOOL ret;
# ifdef COMPILE_UTILITY
-i = snprintf(NULL, 0, FMT, *cursor)+1;
+int i = snprintf(NULL, 0, FMT, *cursor)+1;
+
if (!(qry = malloc(i))) return FALSE;
snprintf(CS qry, i, FMT, *cursor);
-/* fprintf(stderr, "exim_dbscan(%s)\n", qry); */
-ret = exim_dbget__(dbp, qry, key);
+DEBUG(D_hints_lookup) debug_printf_indent("exim_dbscan(%s)\n", qry);
+ret = exim_dbget__(dbp, qry, &encoded_key);
free(qry);
-/* fprintf(stderr, "exim_dbscan ret %c\n", ret ? 'T':'F'); */
-# else
+
+# else /*!COMPILE_UTILITY*/
qry = string_sprintf(FMT, *cursor);
-/* fprintf(stderr, "exim_dbscan(%s)\n", qry); */
-ret = exim_dbget__(dbp, qry, key);
-/* fprintf(stderr, "exim_dbscan ret %c\n", ret ? 'T':'F'); */
-# endif
-if (ret) *cursor = *cursor + 1;
+DEBUG(D_hints_lookup) debug_printf_indent("exim_dbscan(%s)\n", qry);
+ret = exim_dbget__(dbp, qry, &encoded_key);
+
+# endif /*COMPILE_UTILITY*/
+
+DEBUG(D_hints_lookup)
+ debug_printf_indent("exim_dbscan ret %c\n", ret ? 'T':'F');
+
+if (ret)
+ {
+ key->len = xtextdecode(encoded_key.data, &key->data);
+ *cursor = *cursor + 1;
+ }
return ret;
# undef FMT
}