mirror of
https://libwebsockets.org/repo/libwebsockets
synced 2024-11-24 01:39:33 +00:00
c9731c5f17
This is a huge patch that should be a global NOP. For unix type platforms it enables -Wconversion to issue warnings (-> error) for all automatic casts that seem less than ideal but are normally concealed by the toolchain. This is things like passing an int to a size_t argument. Once enabled, I went through all args on my default build (which build most things) and tried to make the removed default cast explicit. With that approach it neither change nor bloat the code, since it compiles to whatever it was doing before, just with the casts made explicit... in a few cases I changed some length args from int to size_t but largely left the causes alone. From now on, new code that is relying on less than ideal casting will complain and nudge me to improve it by warnings.
559 lines
13 KiB
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;
|
|
}
|