android13/external/libwebsockets/lib/misc/lws-struct-sqlite.c

559 lines
13 KiB
C

/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include <libwebsockets.h>
#include <private-lib-core.h>
#include <sqlite3.h>
/*
* we get one of these per matching result from the query
*/
static int
lws_struct_sq3_deser_cb(void *priv, int cols, char **cv, char **cn)
{
lws_struct_args_t *a = (lws_struct_args_t *)priv;
char *u = lwsac_use_zero(&a->ac, a->dest_len, a->ac_block_size);
lws_dll2_owner_t *o = (lws_dll2_owner_t *)a->cb_arg;
const lws_struct_map_t *map = a->map_st[0];
int n, mems = (int)(ssize_t)a->map_entries_st[0];
long long li;
size_t lim;
char **pp;
char *s;
if (!u) {
lwsl_err("OOM\n");
return 1;
}
lws_dll2_add_tail((lws_dll2_t *)((char *)u + a->toplevel_dll2_ofs), o);
while (mems--) {
for (n = 0; n < cols; n++) {
if (!cv[n] || strcmp(cn[n], map->colname))
continue;
switch (map->type) {
case LSMT_SIGNED:
if (map->aux == sizeof(signed char)) {
signed char *pc;
pc = (signed char *)(u + map->ofs);
*pc = (signed char)atoi(cv[n]);
break;
}
if (map->aux == sizeof(short)) {
short *ps;
ps = (short *)(u + map->ofs);
*ps = (short)atoi(cv[n]);
break;
}
if (map->aux == sizeof(int)) {
int *pi;
pi = (int *)(u + map->ofs);
*pi = (int)atoll(cv[n]); /* 32-bit OS */
break;
}
if (map->aux == sizeof(long)) {
long *pl;
pl = (long *)(u + map->ofs);
*pl = (long)atoll(cv[n]); /* 32-bit OS */
break;
}
{
long long *pll;
pll = (long long *)(u + map->ofs);
*pll = atoll(cv[n]);
}
break;
case LSMT_UNSIGNED:
if (map->aux == sizeof(unsigned char)) {
unsigned char *pc;
pc = (unsigned char *)(u + map->ofs);
*pc = (unsigned char)(unsigned int)atoi(cv[n]);
break;
}
if (map->aux == sizeof(unsigned short)) {
unsigned short *ps;
ps = (unsigned short *)(u + map->ofs);
*ps = (unsigned short)atoi(cv[n]);
break;
}
if (map->aux == sizeof(unsigned int)) {
unsigned int *pi;
pi = (unsigned int *)(u + map->ofs);
*pi = (unsigned int)atoi(cv[n]);
break;
}
if (map->aux == sizeof(unsigned long)) {
unsigned long *pl;
pl = (unsigned long *)(u + map->ofs);
*pl = (unsigned long)atol(cv[n]);
break;
}
{
unsigned long long *pll;
pll = (unsigned long long *)(u + map->ofs);
*pll = (unsigned long long)atoll(cv[n]);
}
break;
case LSMT_BOOLEAN:
li = 0;
if (!strcmp(cv[n], "true") ||
!strcmp(cv[n], "TRUE") || cv[n][0] == '1')
li = 1;
if (map->aux == sizeof(char)) {
char *pc;
pc = (char *)(u + map->ofs);
*pc = (char)li;
break;
}
if (map->aux == sizeof(int)) {
int *pi;
pi = (int *)(u + map->ofs);
*pi = (int)li;
} else {
uint64_t *p64;
p64 = (uint64_t *)(u + map->ofs);
*p64 = (uint64_t)li;
}
break;
case LSMT_STRING_CHAR_ARRAY:
s = (char *)(u + map->ofs);
lim = map->aux;
lws_strncpy(s, cv[n], lim);
break;
case LSMT_STRING_PTR:
pp = (char **)(u + map->ofs);
lim = strlen(cv[n]);
s = lwsac_use(&a->ac, lim + 1, a->ac_block_size);
if (!s)
return 1;
*pp = s;
memcpy(s, cv[n], lim);
s[lim] = '\0';
break;
default:
break;
}
}
map++;
}
return 0;
}
/*
* Call this with an LSM_SCHEMA map, its colname is the table name and its
* type information describes the toplevel type. Schema is dereferenced and
* put in args before the actual sq3 query, which is given the child map.
*/
int
lws_struct_sq3_deserialize(sqlite3 *pdb, const char *filter, const char *order,
const lws_struct_map_t *schema, lws_dll2_owner_t *o,
struct lwsac **ac, int start, int _limit)
{
int limit = _limit < 0 ? -_limit : _limit;
char s[768], results[512], where[250];
lws_struct_args_t a;
int n, m;
if (!order)
order = "_lws_idx";
memset(&a, 0, sizeof(a));
a.ac = *ac;
a.cb_arg = o; /* lws_dll2_owner tracking query result objects */
a.map_st[0] = schema->child_map;
a.map_entries_st[0] = schema->child_map_size;
a.dest_len = schema->aux; /* size of toplevel object to allocate */
a.toplevel_dll2_ofs = schema->ofs;
lws_dll2_owner_clear(o);
/*
* Explicitly list the columns instead of use *, so we can skip blobs
*/
m = 0;
for (n = 0; n < (int)schema->child_map_size; n++)
m += lws_snprintf(&results[m], sizeof(results) - (unsigned int)n - 1,
"%s%c", schema->child_map[n].colname,
n + 1 == (int)schema->child_map_size ? ' ' : ',');
where[0] = '\0';
lws_snprintf(where, sizeof(where), " where _lws_idx >= %llu %s",
(unsigned long long)start, filter ? filter : "");
lws_snprintf(s, sizeof(s) - 1, "select %s "
"from %s %s order by %s %slimit %d;", results,
schema->colname, where, order,
_limit < 0 ? "desc " : "", limit);
if (sqlite3_exec(pdb, s, lws_struct_sq3_deser_cb, &a, NULL) != SQLITE_OK) {
lwsl_err("%s: %s: fail %s\n", __func__, sqlite3_errmsg(pdb), s);
lwsac_free(&a.ac);
return -1;
}
*ac = a.ac;
return 0;
}
/*
* This takes a struct and turns it into an sqlite3 UPDATE, using the given
* schema... which has one LSM_SCHEMA_DLL2 entry wrapping the actual schema
*/
static int
_lws_struct_sq3_ser_one(sqlite3 *pdb, const lws_struct_map_t *schema,
uint32_t idx, void *st)
{
const lws_struct_map_t *map = schema->child_map;
int n, m, pk = 0, nentries = (int)(ssize_t)schema->child_map_size, nef = 0, did;
size_t sql_est = 46 + strlen(schema->colname) + 1;
/* "insert into (_lws_idx, ) values (00000001,);" ...
* plus the table name */
uint8_t *stb = (uint8_t *)st;
const char *p;
char *sql;
/*
* Figure out effective number of columns, exluding BLOB.
*
* The first UNSIGNED is a hidden index. Blobs are not handled by
* lws_struct except to create the column in the schema.
*/
pk = 0;
nef = 0;
for (n = 0; n < nentries; n++) {
if (!pk && map[n].type == LSMT_UNSIGNED) {
pk = 1;
continue;
}
if (map[n].type == LSMT_BLOB_PTR)
continue;
nef++;
}
/*
* Figure out an estimate for the length of the populated sqlite
* command, and then malloc it up
*/
for (n = 0; n < nentries; n++) {
sql_est += strlen(map[n].colname) + 2;
switch (map[n].type) {
case LSMT_SIGNED:
case LSMT_UNSIGNED:
case LSMT_BOOLEAN:
switch (map[n].aux) {
case 1:
sql_est += 3 + 2;
break;
case 2:
sql_est += 5 + 2;
break;
case 4:
sql_est += 10 + 2;
break;
case 8:
sql_est += 20 + 2;
break;
}
if (map[n].type == LSMT_SIGNED)
sql_est++; /* minus sign */
break;
case LSMT_STRING_CHAR_ARRAY:
sql_est += (unsigned int)lws_sql_purify_len((const char *)st +
map[n].ofs) + 2;
break;
case LSMT_STRING_PTR:
p = *((const char * const *)&stb[map[n].ofs]);
sql_est += (unsigned int)((p ? lws_sql_purify_len(p) : 0) + 2);
break;
case LSMT_BLOB_PTR:
/* we don't deal with blobs actually */
sql_est -= strlen(map[n].colname) + 2;
break;
default:
lwsl_err("%s: unsupported type\n", __func__);
assert(0);
break;
}
}
sql = malloc(sql_est);
if (!sql)
return -1;
m = lws_snprintf(sql, sql_est, "insert into %s(_lws_idx, ",
schema->colname);
/*
* First explicit integer type is primary key autoincrement, should
* not be specified
*/
pk = 0;
did = 0;
for (n = 0; n < nentries; n++) {
if (!pk && map[n].type == LSMT_UNSIGNED) {
pk = 1;
continue;
}
if (map[n].type == LSMT_BLOB_PTR)
continue;
did++;
m += lws_snprintf(sql + m, sql_est - (unsigned int)m,
did == nef ? "%s" : "%s, ",
map[n].colname);
}
m += lws_snprintf(sql + m, sql_est - (unsigned int)m, ") values(%u, ", idx);
pk = 0;
did = 0;
for (n = 0; n < nentries; n++) {
uint64_t uu64;
size_t q;
if (!pk && map[n].type == LSMT_UNSIGNED) {
pk = 1;
continue;
}
switch (map[n].type) {
case LSMT_SIGNED:
case LSMT_UNSIGNED:
case LSMT_BOOLEAN:
uu64 = 0;
for (q = 0; q < map[n].aux; q++)
uu64 |= ((uint64_t)stb[map[n].ofs + q] <<
(q << 3));
if (map[n].type == LSMT_SIGNED)
m += lws_snprintf(sql + m, sql_est - (unsigned int)m, "%lld",
(long long)(int64_t)uu64);
else
m += lws_snprintf(sql + m, sql_est - (unsigned int)m, "%llu",
(unsigned long long)uu64);
break;
case LSMT_STRING_CHAR_ARRAY:
sql[m++] = '\'';
lws_sql_purify(sql + m, (const char *)&stb[map[n].ofs],
sql_est - (size_t)(ssize_t)m - 4);
m += (int)(ssize_t)strlen(sql + m);
sql[m++] = '\'';
break;
case LSMT_STRING_PTR:
p = *((const char * const *)&stb[map[n].ofs]);
sql[m++] = '\'';
if (p) {
lws_sql_purify(sql + m, p, sql_est - (unsigned int)m - 4);
m += (int)(ssize_t)strlen(sql + m);
}
sql[m++] = '\'';
break;
case LSMT_BLOB_PTR:
continue;
default:
lwsl_err("%s: unsupported type\n", __func__);
assert(0);
break;
}
did++;
if (did != nef) {
if (sql_est - (unsigned int)m < 6)
return -1;
sql[m++] = ',';
sql[m++] = ' ';
}
}
lws_snprintf(sql + m, sql_est - (unsigned int)m, ");");
n = sqlite3_exec(pdb, sql, NULL, NULL, NULL);
if (n != SQLITE_OK) {
lwsl_err("%s\n", sql);
free(sql);
lwsl_err("%s: %s: fail\n", __func__, sqlite3_errmsg(pdb));
return -1;
}
free(sql);
return 0;
}
int
lws_struct_sq3_serialize(sqlite3 *pdb, const lws_struct_map_t *schema,
lws_dll2_owner_t *owner, uint32_t manual_idx)
{
uint32_t idx = manual_idx;
lws_start_foreach_dll(struct lws_dll2 *, p, owner->head) {
void *item = (void *)((uint8_t *)p - schema->ofs_clist);
if (_lws_struct_sq3_ser_one(pdb, schema, idx++, item))
return 1;
} lws_end_foreach_dll(p);
return 0;
}
int
lws_struct_sq3_create_table(sqlite3 *pdb, const lws_struct_map_t *schema)
{
const lws_struct_map_t *map = schema->child_map;
int map_size = (int)(ssize_t)schema->child_map_size, subsequent = 0;
char s[2048], *p = s, *end = &s[sizeof(s) - 1],
*pri = " primary key autoincrement", *use;
p += lws_snprintf(p, (unsigned int)lws_ptr_diff(end, p),
"create table if not exists %s (_lws_idx integer, ",
schema->colname);
while (map_size--) {
if (map->type > LSMT_STRING_PTR && map->type != LSMT_BLOB_PTR) {
map++;
continue;
}
if (subsequent && (end - p) > 4) {
*p++ = ',';
*p++ = ' ';
}
subsequent = 1;
if (map->type == LSMT_BLOB_PTR) {
p += lws_snprintf(p, (unsigned int)lws_ptr_diff(end, p), "%s blob", map->colname);
} else {
if (map->type < LSMT_STRING_CHAR_ARRAY) {
use = "";
if (map->colname[0] != '_') /* _lws_idx is not primary key */
use = pri;
p += lws_snprintf(p, (unsigned int)lws_ptr_diff(end, p), "%s integer%s",
map->colname, use);
if (map->colname[0] != '_')
pri = "";
} else
p += lws_snprintf(p, (unsigned int)lws_ptr_diff(end, p), "%s varchar",
map->colname);
}
map++;
}
p += lws_snprintf(p, (unsigned int)lws_ptr_diff(end, p), ");");
if (sqlite3_exec(pdb, s, NULL, NULL, NULL) != SQLITE_OK) {
lwsl_err("%s: %s: fail\n", __func__, sqlite3_errmsg(pdb));
return -1;
}
return 0;
}
int
lws_struct_sq3_open(struct lws_context *context, const char *sqlite3_path,
char create_if_missing, sqlite3 **pdb)
{
#if !defined(WIN32)
uid_t uid = 0;
gid_t gid = 0;
#endif
if (sqlite3_open_v2(sqlite3_path, pdb,
SQLITE_OPEN_READWRITE |
(create_if_missing ? SQLITE_OPEN_CREATE : 0),
NULL) != SQLITE_OK) {
lwsl_info("%s: Unable to open db %s: %s\n",
__func__, sqlite3_path, sqlite3_errmsg(*pdb));
return 1;
}
#if !defined(WIN32)
lws_get_effective_uid_gid(context, &uid, &gid);
if (uid)
if (chown(sqlite3_path, uid, gid))
lwsl_err("%s: failed to chown %s\n", __func__, sqlite3_path);
chmod(sqlite3_path, 0600);
lwsl_debug("%s: created %s owned by %u:%u mode 0600\n", __func__,
sqlite3_path, (unsigned int)uid, (unsigned int)gid);
#else
lwsl_debug("%s: created %s\n", __func__, sqlite3_path);
#endif
sqlite3_extended_result_codes(*pdb, 1);
return 0;
}
int
lws_struct_sq3_close(sqlite3 **pdb)
{
int n;
if (!*pdb)
return 0;
n = sqlite3_close(*pdb);
if (n != SQLITE_OK) {
/*
* trouble...
*/
lwsl_err("%s: failed to close: %d\n", __func__, n);
return 1;
}
*pdb = NULL;
return 0;
}