static heim_dict_t parse_dict(struct parse_ctx *ctx) { heim_dict_t dict; size_t count = 0; int ret; heim_assert(*ctx->p == '{', "string doesn't start with {"); dict = heim_dict_create(11); if (dict == NULL) { ctx->error = heim_error_enomem(); return NULL; } ctx->p += 1; /* safe because parse_pair() calls white_spaces() first */ while ((ret = parse_pair(dict, ctx)) > 0) count++; if (ret < 0) { heim_release(dict); return NULL; } if (count == 1 && !(ctx->flags & HEIM_JSON_F_NO_DATA_DICT)) { heim_object_t v = heim_dict_copy_value(dict, heim_tid_data_uuid_key); /* * Binary data encoded as a dict with a single magic key with * base64-encoded value? Decode as heim_data_t. */ if (v != NULL && heim_get_tid(v) == HEIM_TID_STRING) { void *buf; size_t len; buf = malloc(strlen(heim_string_get_utf8(v))); if (buf == NULL) { heim_release(dict); heim_release(v); ctx->error = heim_error_enomem(); return NULL; } len = base64_decode(heim_string_get_utf8(v), buf); heim_release(v); if (len == -1) { free(buf); return dict; /* assume aliasing accident */ } heim_release(dict); return (heim_dict_t)heim_data_ref_create(buf, len, free); } } return dict; }
/** * Lookup a key's value in the DB. * * Returns 0 on success, -1 if the key does not exist in the DB, or a * system error number on failure. * * @param db Open DB handle * @param key Key * @param error Output error object * * @return the value (retained), if there is one for the given key * * @addtogroup heimbase */ heim_data_t heim_db_copy_value(heim_db_t db, heim_string_t table, heim_data_t key, heim_error_t *error) { heim_object_t v; heim_data_t result; if (heim_get_tid(db) != HEIM_TID_DB) return NULL; if (error != NULL) *error = NULL; if (table == NULL) table = HSTR(""); if (db->in_transaction) { heim_string_t key64; key64 = to_base64(key, error); if (key64 == NULL) { if (error) *error = heim_error_enomem(); return NULL; } v = heim_path_copy(db->set_keys, error, table, key64, NULL); if (v != NULL) { heim_release(key64); return v; } v = heim_path_copy(db->del_keys, error, table, key64, NULL); /* can't be NULL */ heim_release(key64); if (v != NULL) return NULL; } result = db->plug->copyf(db->db_data, table, key, error); return result; }
static heim_data_t from_base64(heim_string_t s, heim_error_t *error) { void *buf; size_t len; heim_data_t d; buf = malloc(strlen(heim_string_get_utf8(s))); if (buf == NULL) goto enomem; len = base64_decode(heim_string_get_utf8(s), buf); d = heim_data_ref_create(buf, len, free); if (d == NULL) goto enomem; return d; enomem: free(buf); if (error) *error = heim_error_enomem(); return NULL; }
static heim_string_t to_base64(heim_data_t data, heim_error_t *error) { char *b64 = NULL; heim_string_t s = NULL; const heim_octet_string *d; int ret; d = heim_data_get_data(data); ret = base64_encode(d->data, d->length, &b64); if (ret < 0 || b64 == NULL) goto enomem; s = heim_string_ref_create(b64, free); if (s == NULL) goto enomem; return s; enomem: free(b64); if (error) *error = heim_error_enomem(); return NULL; }
/** * Clone (duplicate) an open DB handle. * * This is useful for multi-threaded applications. Applications must * synchronize access to any given DB handle. * * Returns EBUSY if there is an open transaction for the input db. * * @param db Open DB handle * @param error Output error object * * @return a DB handle * * @addtogroup heimbase */ heim_db_t heim_db_clone(heim_db_t db, heim_error_t *error) { heim_db_t clone; int ret; if (heim_get_tid(db) != HEIM_TID_DB) heim_abort("Expected a database"); if (db->in_transaction) heim_abort("DB handle is busy"); if (db->plug->clonef == NULL) { return heim_db_create(heim_string_get_utf8(db->dbtype), heim_string_get_utf8(db->dbname), db->options, error); } clone = _heim_alloc_object(&db_object, sizeof(*clone)); if (clone == NULL) { if (error) *error = heim_error_enomem(); return NULL; } clone->set_keys = NULL; clone->del_keys = NULL; ret = db->plug->clonef(db->db_data, &clone->db_data, error); if (ret) { heim_release(clone); if (error && !*error) *error = heim_error_create(ENOENT, N_("Could not re-open DB while cloning", "")); return NULL; } db->db_data = NULL; return clone; }
static heim_string_t parse_string(struct parse_ctx *ctx) { const uint8_t *start; int quote = 0; if (ctx->flags & HEIM_JSON_F_STRICT_STRINGS) { ctx->error = heim_error_create(EINVAL, "Strict JSON string encoding " "not yet supported"); return NULL; } if (*ctx->p != '"') { ctx->error = heim_error_create(EINVAL, "Expected a JSON string but " "found something else at line %lu", ctx->lineno); return NULL; } start = ++ctx->p; while (ctx->p < ctx->pend) { if (*ctx->p == '\n') { ctx->lineno++; } else if (*ctx->p == '\\') { if (ctx->p + 1 == ctx->pend) goto out; ctx->p++; quote = 1; } else if (*ctx->p == '"') { heim_object_t o; if (quote) { char *p0, *p; p = p0 = malloc(ctx->p - start); if (p == NULL) goto out; while (start < ctx->p) { if (*start == '\\') { start++; /* XXX validate quoted char */ } *p++ = *start++; } o = heim_string_create_with_bytes(p0, p - p0); free(p0); } else { o = heim_string_create_with_bytes(start, ctx->p - start); if (o == NULL) { ctx->error = heim_error_enomem(); return NULL; } /* If we can decode as base64, then let's */ if (ctx->flags & HEIM_JSON_F_TRY_DECODE_DATA) { void *buf; size_t len; const char *s; s = heim_string_get_utf8(o); len = strlen(s); if (len >= 4 && strspn(s, base64_chars) >= len - 2) { buf = malloc(len); if (buf == NULL) { heim_release(o); ctx->error = heim_error_enomem(); return NULL; } len = base64_decode(s, buf); if (len == -1) { free(buf); return o; } heim_release(o); o = heim_data_ref_create(buf, len, free); } } } ctx->p += 1; return o; } ctx->p += 1; } out: ctx->error = heim_error_create(EINVAL, "ran out of string"); return NULL; }
/** * Open a database of the given dbtype. * * Database type names can be composed of one or more pseudo-DB types * and one concrete DB type joined with a '+' between each. For * example: "transaction+bdb" might be a Berkeley DB with a layer above * that provides transactions. * * Options may be provided via a dict (an associative array). Existing * options include: * * - "create", with any value (create if DB doesn't exist) * - "exclusive", with any value (exclusive create) * - "truncate", with any value (truncate the DB) * - "read-only", with any value (disallow writes) * - "sync", with any value (make transactions durable) * - "journal-name", with a string value naming a journal file name * * @param dbtype Name of DB type * @param dbname Name of DB (likely a file path) * @param options Options dict * @param db Output open DB handle * @param error Output error object * * @return a DB handle * * @addtogroup heimbase */ heim_db_t heim_db_create(const char *dbtype, const char *dbname, heim_dict_t options, heim_error_t *error) { heim_string_t s; char *p; db_plugin plug; heim_db_t db; int ret = 0; if (options == NULL) { options = heim_dict_create(11); if (options == NULL) { if (error) *error = heim_error_enomem(); return NULL; } } else { (void) heim_retain(options); } if (db_plugins == NULL) { heim_release(options); return NULL; } if (dbtype == NULL || *dbtype == '\0') { struct dbtype_iter iter_ctx = { NULL, dbname, options, error}; /* Try all dbtypes */ heim_dict_iterate_f(db_plugins, &iter_ctx, dbtype_iter2create_f); heim_release(options); return iter_ctx.db; } else if (strstr(dbtype, "json")) { (void) heim_db_register(dbtype, NULL, &json_dbt); } /* * Allow for dbtypes that are composed from pseudo-dbtypes chained * to a real DB type with '+'. For example a pseudo-dbtype might * add locking, transactions, transcoding of values, ... */ p = strchr(dbtype, '+'); if (p != NULL) s = heim_string_create_with_bytes(dbtype, p - dbtype); else s = heim_string_create(dbtype); if (s == NULL) { heim_release(options); return NULL; } HEIMDAL_MUTEX_lock(&db_type_mutex); plug = heim_dict_get_value(db_plugins, s); HEIMDAL_MUTEX_unlock(&db_type_mutex); heim_release(s); if (plug == NULL) { if (error) *error = heim_error_create(ENOENT, N_("Heimdal DB plugin not found: %s", ""), dbtype); heim_release(options); return NULL; } db = _heim_alloc_object(&db_object, sizeof(*db)); if (db == NULL) { heim_release(options); return NULL; } db->in_transaction = 0; db->ro_tx = 0; db->set_keys = NULL; db->del_keys = NULL; db->plug = plug; db->options = options; ret = plug->openf(plug->data, dbtype, dbname, options, &db->db_data, error); if (ret) { heim_release(db); if (error && *error == NULL) *error = heim_error_create(ENOENT, N_("Heimdal DB could not be opened: %s", ""), dbname); return NULL; } ret = db_replay_log(db, error); if (ret) { heim_release(db); return NULL; } if (plug->clonef == NULL) { db->dbtype = heim_string_create(dbtype); db->dbname = heim_string_create(dbname); if (!db->dbtype || ! db->dbname) { heim_release(db); if (error) *error = heim_error_enomem(); return NULL; } } return db; }