static heim_object_t parse_value(struct parse_ctx *ctx) { size_t len; heim_object_t o; if (white_spaces(ctx)) return NULL; if (*ctx->p == '"') { return parse_string(ctx); } else if (*ctx->p == '{') { if (ctx->depth-- == 1) { ctx->error = heim_error_create(EINVAL, "JSON object too deep"); return NULL; } o = parse_dict(ctx); ctx->depth++; return o; } else if (*ctx->p == '[') { if (ctx->depth-- == 1) { ctx->error = heim_error_create(EINVAL, "JSON object too deep"); return NULL; } o = parse_array(ctx); ctx->depth++; return o; } else if (is_number(*ctx->p) || *ctx->p == '-') { return parse_number(ctx); } len = ctx->pend - ctx->p; if ((ctx->flags & HEIM_JSON_F_NO_C_NULL) == 0 && len >= 6 && memcmp(ctx->p, "<NULL>", 6) == 0) { ctx->p += 6; return heim_null_create(); } else if (len >= 4 && memcmp(ctx->p, "null", 4) == 0) { ctx->p += 4; return heim_null_create(); } else if (len >= 4 && strncasecmp((char *)ctx->p, "true", 4) == 0) { ctx->p += 4; return heim_bool_create(1); } else if (len >= 5 && strncasecmp((char *)ctx->p, "false", 5) == 0) { ctx->p += 5; return heim_bool_create(0); } ctx->error = heim_error_create(EINVAL, "unknown char %c at %lu line %lu", (char)*ctx->p, (unsigned long)(ctx->p - ctx->pstart), ctx->lineno); return NULL; }
static heim_string_t parse_string(struct parse_ctx *ctx) { const uint8_t *start; int quote = 0; heim_assert(*ctx->p == '"', "string doesnt' start with \""); 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 += 1; 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 qouted 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); } ctx->p += 1; return o; } ctx->p += 1; } out: ctx->error = heim_error_create(EINVAL, "ran out of string"); 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 result; 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); } result = _heim_alloc_object(&db_object, sizeof(*result)); if (result == NULL) { if (error) *error = heim_error_create_enomem(); return NULL; } result->set_keys = NULL; result->del_keys = NULL; ret = db->plug->clonef(db->db_data, &result->db_data, error); if (ret) { heim_release(result); if (error && !*error) *error = heim_error_create(ENOENT, N_("Could not re-open DB while cloning", "")); return NULL; } db->db_data = NULL; return result; }
static heim_object_t parse_value(struct parse_ctx *ctx) { size_t len; if (white_spaces(ctx)) return NULL; if (*ctx->p == '"') { return parse_string(ctx); } else if (*ctx->p == '{') { return parse_dict(ctx); } else if (*ctx->p == '[') { return parse_array(ctx); } else if (is_number(*ctx->p) || *ctx->p == '-') { return parse_number(ctx); } len = ctx->pend - ctx->p; if (len >= 4 && memcmp(ctx->p, "null", 4) == 0) { ctx->p += 4; return heim_null_create(); } else if (len >= 4 && strncasecmp((char *)ctx->p, "true", 4) == 0) { ctx->p += 4; return heim_bool_create(1); } else if (len >= 5 && strncasecmp((char *)ctx->p, "false", 5) == 0) { ctx->p += 5; return heim_bool_create(0); } ctx->error = heim_error_create(EINVAL, "unknown char %c at %lu line %lu", (char)*ctx->p, (unsigned long)(ctx->p - ctx->pstart), ctx->lineno); 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_create_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_create_enomem(); return NULL; } } return db; }
static int open_file(const char *dbname, int for_write, int excl, int *fd_out, heim_error_t *error) { #ifdef WIN32 HANDLE hFile; int ret = 0; if (fd_out) *fd_out = -1; if (for_write) hFile = CreateFile(dbname, GENERIC_WRITE | GENERIC_READ, 0, NULL, /* we'll close as soon as we read */ CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); else hFile = CreateFile(dbname, GENERIC_READ, FILE_SHARE_READ, NULL, /* we'll close as soon as we read */ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE) { ret = GetLastError(); _set_errno(ret); /* CreateFile() does not set errno */ goto err; } if (fd_out == NULL) { (void) CloseHandle(hFile); return 0; } *fd_out = _open_osfhandle((intptr_t) hFile, 0); if (*fd_out < 0) { ret = errno; (void) CloseHandle(hFile); goto err; } /* No need to lock given share deny mode */ return 0; err: if (error != NULL) { char *s = NULL; FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, 0, ret, 0, (LPTSTR) &s, 0, NULL); *error = heim_error_create(ret, N_("Could not open JSON file %s: %s", ""), dbname, s ? s : "<error formatting error>"); LocalFree(s); } return ret; #else int ret = 0; int fd; if (fd_out) *fd_out = -1; if (for_write && excl) fd = open(dbname, O_CREAT | O_EXCL | O_WRONLY, 0600); else if (for_write) fd = open(dbname, O_CREAT | O_TRUNC | O_WRONLY, 0600); else fd = open(dbname, O_RDONLY); if (fd < 0) { if (error != NULL) *error = heim_error_create(ret, N_("Could not open JSON file %s: %s", ""), dbname, strerror(errno)); return errno; } if (fd_out == NULL) { (void) close(fd); return 0; } ret = flock(fd, for_write ? LOCK_EX : LOCK_SH); if (ret == -1) { /* Note that we if O_EXCL we're leaving the [lock] file around */ (void) close(fd); return HEIM_ERROR(error, errno, (errno, N_("Could not lock JSON file %s: %s", ""), dbname, strerror(errno))); } *fd_out = fd; return 0; #endif }
static int parse_pair(heim_dict_t dict, struct parse_ctx *ctx) { heim_string_t key; heim_object_t value; if (white_spaces(ctx)) return -1; if (*ctx->p == '}') { ctx->p++; return 0; } if (ctx->flags & HEIM_JSON_F_STRICT_DICT) /* JSON allows only string keys */ key = parse_string(ctx); else /* heim_dict_t allows any heim_object_t as key */ key = parse_value(ctx); if (key == NULL) /* Even heim_dict_t does not allow C NULLs as keys though! */ return -1; if (white_spaces(ctx)) { heim_release(key); return -1; } if (*ctx->p != ':') { heim_release(key); return -1; } ctx->p += 1; /* safe because we call white_spaces() next */ if (white_spaces(ctx)) { heim_release(key); return -1; } value = parse_value(ctx); if (value == NULL && (ctx->error != NULL || (ctx->flags & HEIM_JSON_F_NO_C_NULL))) { if (ctx->error == NULL) ctx->error = heim_error_create(EINVAL, "Invalid JSON encoding"); heim_release(key); return -1; } heim_dict_set_value(dict, key, value); heim_release(key); heim_release(value); if (white_spaces(ctx)) return -1; if (*ctx->p == '}') { /* * Return 1 but don't consume the '}' so we can count the one * pair in a one-pair dict */ return 1; } else if (*ctx->p == ',') { ctx->p++; return 1; } return -1; }
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_create_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_create_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; }