/* * libwebsockets - small server side websockets and web server implementation * * Copyright (C) 2010 - 2020 Andy Green * * 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 #include #include /* * 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; }