Пример #1
0
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;
}
Пример #2
0
/**
 * 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;
}
Пример #3
0
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;
}
Пример #4
0
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;
}
Пример #5
0
/**
 * 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;
}
Пример #6
0
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;
}
Пример #7
0
/**
 * 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;
}