<commit_before>#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdarg.h>
#include <string.h>
#include <assert.h>

#include "conflate.h"
#include "conflate_internal.h"
#include "conflate_convenience.h"

/* Randomly generated by a fair dice roll */
#define INITIALIZATION_MAGIC 1422850115

/* The private key under which the JID to use is stored */
#define STORED_JID_KEY "stored_jid"

struct _conflate_form_result {
    xmpp_conn_t   *conn;
    xmpp_ctx_t    *ctx;
    xmpp_stanza_t *reply;
    xmpp_stanza_t *cmd_res;
    xmpp_stanza_t *container;
    xmpp_stanza_t *current;
};

struct command_def {
    char *name;
    char *description;
    conflate_mgmt_cb_t cb;
    struct command_def *next;
};

bool commands_initialized = false;
struct command_def *commands = NULL;

void conflate_register_mgmt_cb(const char *cmd, const char *desc,
                               conflate_mgmt_cb_t cb)
{
    struct command_def *c = calloc(1, sizeof(struct command_def));
    assert(c);

    c->name = safe_strdup(cmd);
    c->description = safe_strdup(desc);
    c->cb = cb;
    c->next = commands;

    commands = c;
}

static char *get_form_value(kvpair_t *form, const char *key)
{
    assert(key);

    char *rv = NULL;

    if (form) {
        kvpair_t *valnode = find_kvpair(form, key);
        if (valnode) {
            rv = safe_strdup(valnode->values[0]);
        }
    }

    return rv;
}

static void add_and_release(xmpp_stanza_t* parent, xmpp_stanza_t* child)
{
    xmpp_stanza_add_child(parent, child);
    xmpp_stanza_release(child);
}

/* Copy an attribute from one stanza to another iff it exists in the
   src. */
static void copy_attr(xmpp_ctx_t *ctx,
                      xmpp_stanza_t* src, xmpp_stanza_t* dest,
                      const char* attr)
{
    assert(src);
    assert(dest);
    assert(attr);

    char *val = xmpp_stanza_get_attribute(src, attr);
    if (val) {
        xmpp_stanza_set_attribute(dest, attr, val);
    }
}

/* Create an IQ response */
static xmpp_stanza_t* create_reply(xmpp_ctx_t* ctx, xmpp_stanza_t* stanza)
{
    xmpp_stanza_t* reply = xmpp_stanza_new(ctx);

    assert(reply);

    xmpp_stanza_set_name(reply, "iq");
    xmpp_stanza_set_type(reply, "result");
    if (xmpp_stanza_get_id(stanza)) {
        xmpp_stanza_set_id(reply, xmpp_stanza_get_id(stanza));
    }
    xmpp_stanza_set_attribute(reply, "to",
                              xmpp_stanza_get_attribute(stanza, "from"));

    return reply;
}

/* Create an XMPP command response */
static xmpp_stanza_t* create_cmd_response(xmpp_ctx_t* ctx,
                                          xmpp_stanza_t* cmd_stanza)
{
    xmpp_stanza_t* cmd_res = xmpp_stanza_new(ctx);
    assert(cmd_res);

    xmpp_stanza_set_name(cmd_res, "command");
    copy_attr(ctx, cmd_stanza, cmd_res, "xmlns");
    copy_attr(ctx, cmd_stanza, cmd_res, "node");
    copy_attr(ctx, cmd_stanza, cmd_res, "sessionid");
    xmpp_stanza_set_attribute(cmd_res, "status", "completed");

    return cmd_res;
}

static int version_handler(xmpp_conn_t * const conn,
                           xmpp_stanza_t * const stanza,
                           void * const userdata)
{
    xmpp_stanza_t *reply, *query, *name, *version, *text;
    conflate_handle_t *handle = (conflate_handle_t*) userdata;
    xmpp_ctx_t *ctx = handle->ctx;
    char *ns;

    printf("Received version request from %s\n", xmpp_stanza_get_attribute(stanza, "from"));

    reply = xmpp_stanza_new(ctx);
    assert(reply);
    xmpp_stanza_set_name(reply, "iq");
    xmpp_stanza_set_type(reply, "result");
    xmpp_stanza_set_id(reply, xmpp_stanza_get_id(stanza));
    xmpp_stanza_set_attribute(reply, "to", xmpp_stanza_get_attribute(stanza, "from"));

    query = xmpp_stanza_new(ctx);
    assert(query);
    xmpp_stanza_set_name(query, "query");
    ns = xmpp_stanza_get_ns(xmpp_stanza_get_children(stanza));
    if (ns) {
        xmpp_stanza_set_ns(query, ns);
    }

    name = xmpp_stanza_new(ctx);
    assert(name);
    xmpp_stanza_set_name(name, "name");
    add_and_release(query, name);

    text = xmpp_stanza_new(ctx);
    assert(text);
    xmpp_stanza_set_text(text, handle->conf->software);
    add_and_release(name, text);

    version = xmpp_stanza_new(ctx);
    assert(version);
    xmpp_stanza_set_name(version, "version");
    add_and_release(query, version);

    text = xmpp_stanza_new(ctx);
    assert(text);
    xmpp_stanza_set_text(text, handle->conf->version);
    add_and_release(version, text);

    add_and_release(reply, query);

    xmpp_send(conn, reply);
    xmpp_stanza_release(reply);
    return 1;
}

static char **get_form_values(xmpp_stanza_t *t) {
    xmpp_stanza_t *current = NULL;
    int i = 0, allocated = 8;
    char **rv = calloc(allocated, sizeof(char*));
    assert(rv);

    current = xmpp_stanza_get_child_by_name(t, "value");
    while (current) {
        xmpp_stanza_t *val = xmpp_stanza_get_children(current);
        /* xmpp_stanza_get_children allocates */
        char *v = xmpp_stanza_get_text(val);

        if (i + 1 >= allocated) {
            int new_allocated = allocated << 1;
            rv = realloc(rv, sizeof(char*) * new_allocated);
            assert(rv);
        }

        rv[i++] = v;

        current = xmpp_stanza_get_next(current);
    }

    /* List terminator */
    rv[i] = NULL;

    return rv;
}

static char **get_specific_form_values(xmpp_stanza_t *field, const char *var) {
    char **rv = NULL;

    while (field && strcmp(xmpp_stanza_get_attribute(field, "var"), var) != 0) {
        field = xmpp_stanza_get_next(field);
    }

    if (field) {
        rv = get_form_values(field);
    }

    return rv;
}

static kvpair_t *grok_form(xmpp_stanza_t *fields)
{
    kvpair_t *rv = NULL;

    /* walk the field peers and grab their values and stuff */
    while (fields) {
        if (xmpp_stanza_is_tag(fields)) {
            char* k = xmpp_stanza_get_attribute(fields, "var");
            char **vals = get_specific_form_values(fields, k);
            kvpair_t* thispair = mk_kvpair(k, vals);
            free_string_list(vals);
            thispair->next = rv;
            rv = thispair;
        }
        fields = xmpp_stanza_get_next(fields);
    }

    return rv;
}

static enum conflate_mgmt_cb_result process_serverlist(void *opaque,
                                                       conflate_handle_t *handle,
                                                       const char *cmd,
                                                       bool direct,
                                                       kvpair_t *conf,
                                                       conflate_form_result *r)
{
    /* If we have "config_is_private" set to "yes" we should only
       process this if it's direct (i.e. ignore pubsub) */
    if (!direct) {
        char *priv = conflate_get_private(handle, "config_is_private",
                                          handle->conf->save_path);

        if (priv && strcmp(priv, "yes") == 0) {
            CONFLATE_LOG(handle, INFO,
                         "Currently using a private config, ignoring update.");
            return RV_OK;
        }
        free(priv);
    }

    CONFLATE_LOG(handle, INFO, "Processing a serverlist");

    /* Persist the config lists */
    if (!save_kvpairs(handle, conf, handle->conf->save_path)) {
        CONFLATE_LOG(handle, ERROR, "Can not save config to %s",
                     handle->conf->save_path);
    }

    /* Send the config to the callback */
    handle->conf->new_config(handle->conf->userdata, conf);

    return RV_OK;
}

static void add_form_values(xmpp_ctx_t* ctx, xmpp_stanza_t *parent,
                            const char *key, const char **values)
{
    xmpp_stanza_t* field = xmpp_stanza_new(ctx);
    assert(field);

    xmpp_stanza_set_name(field, "field");
    xmpp_stanza_set_attribute(field, "var", key);
    add_and_release(parent, field);

    for (int i = 0; values[i]; i++) {
        xmpp_stanza_t* value = xmpp_stanza_new(ctx);
        xmpp_stanza_t* text = xmpp_stanza_new(ctx);
        assert(value);
        assert(text);

        xmpp_stanza_set_name(value, "value");
        add_and_release(field, value);

        xmpp_stanza_set_text(text, values[i]);
        add_and_release(value, text);
    }
}

static void add_cmd_error(xmpp_ctx_t *ctx,
                          xmpp_stanza_t * reply, const char *code,
                          const char *ns, const char *name,
                          const char *specificns, const char *specificcond)
{
    xmpp_stanza_set_attribute(reply, "type", "error");

    xmpp_stanza_t *error = xmpp_stanza_new(ctx);
    assert(error);

    xmpp_stanza_set_name(error, "error");
    xmpp_stanza_set_attribute(error, "type", "modify");
    xmpp_stanza_set_attribute(error, "code", code);

    add_and_release(reply, error);

    xmpp_stanza_t *etype = xmpp_stanza_new(ctx);
    assert(etype);

    xmpp_stanza_set_name(etype, name);
    xmpp_stanza_set_attribute(etype, "xmlns", ns);
    add_and_release(error, etype);

    if (specificns && specificcond) {
        xmpp_stanza_t *specific = xmpp_stanza_new(ctx);
        assert(specific);

        xmpp_stanza_set_name(specific, specificcond);
        xmpp_stanza_set_attribute(specific, "xmlns", specificns);

        add_and_release(error, specific);
    }
}

static xmpp_stanza_t* error_unknown_command(const char *cmd,
                                            xmpp_stanza_t * const cmd_stanza,
                                            xmpp_conn_t* const conn,
                                            xmpp_stanza_t * const stanza,
                                            void * const userdata,
                                            bool direct)
{
    conflate_handle_t *handle = (conflate_handle_t*) userdata;
    xmpp_ctx_t *ctx = handle->ctx;

    xmpp_stanza_t* reply = create_reply(ctx, stanza);
    add_and_release(reply, create_cmd_response(ctx, cmd_stanza));
    add_cmd_error(ctx, reply, "404",
                  "urn:ietf:params:xml:ns:xmpp-stanzas", "item-not-found",
                  NULL, NULL);
    return reply;
}

void conflate_init_form(conflate_form_result *r)
{
    if (!r->container) {
        r->container = xmpp_stanza_new(r->ctx);

        /* X data in the command response */
        xmpp_stanza_set_name(r->container, "x");
        xmpp_stanza_set_attribute(r->container, "xmlns", "jabber:x:data");
        xmpp_stanza_set_type(r->container, "result");
        add_and_release(r->cmd_res, r->container);
    }
}

void conflate_add_field_multi(conflate_form_result *r, const char *k,
                              const char **v)
{
    conflate_init_form(r);
    if (!r->current) {
        r->current = r->container;
    }
    if (k) {
        add_form_values(r->ctx, r->current, k, v);
    }
}

void conflate_add_field(conflate_form_result *r, const char *k, const char *v)
{
    const char *vals[2] = { v, NULL };
    conflate_add_field_multi(r, k, vals);
}

void conflate_next_fieldset(conflate_form_result *r)
{
        conflate_init_form(r);
        /* Fail if some k/v pairs were already added */
        assert(r->current == NULL || r->current != r->container);

        r->current = xmpp_stanza_new(r->ctx);
        assert(r->current);

        xmpp_stanza_set_name(r->current, "item");
        add_and_release(r->container, r->current);
}

static enum conflate_mgmt_cb_result process_set_private(void *opaque,
                                                        conflate_handle_t *handle,
                                                        const char *cmd,
                                                        bool direct,
                                                        kvpair_t *form,
                                                        conflate_form_result *r)
{
    /* Only direct stat requests are handled. */
    assert(direct);
    enum conflate_mgmt_cb_result rv = RV_ERROR;

    char *key = get_form_value(form, "key");
    char *value = get_form_value(form, "value");

    if (key && value) {
        if (conflate_save_private(handle, key, value,
                                  handle->conf->save_path)) {
            rv = RV_OK;
        }
    } else {
        rv = RV_BADARG;
    }

    free(key);
    free(value);

    return rv;
}

static enum conflate_mgmt_cb_result process_get_private(void *opaque,
                                                        conflate_handle_t *handle,
                                                        const char *cmd,
                                                        bool direct,
                                                        kvpair_t *form,
                                                        conflate_form_result *r)
{
    /* Only direct stat requests are handled. */
    assert(direct);
    enum conflate_mgmt_cb_result rv = RV_ERROR;

    char *key = get_form_value(form, "key");

    if (key) {
        /* Initialize the form so there's always one there */
        conflate_init_form(r);
        char *value = conflate_get_private(handle, key,
                                           handle->conf->save_path);
        if (value) {
            conflate_add_field(r, key, value);
            free(value);
        }

        rv = RV_OK;
    } else {
        rv = RV_BADARG;
    }

    free(key);

    return rv;
}

static enum conflate_mgmt_cb_result process_delete_private(void *opaque,
                                                           conflate_handle_t *handle,
                                                           const char *cmd,
                                                           bool direct,
                                                           kvpair_t *form,
                                                           conflate_form_result *r)
{
    /* Only direct stat requests are handled. */
    assert(direct);
    enum conflate_mgmt_cb_result rv = RV_ERROR;

    char *key = get_form_value(form, "key");

    if (key) {
        if (conflate_delete_private(handle, key,
                                    handle->conf->save_path)) {
            rv = RV_OK;
        }
    } else {
        rv = RV_BADARG;
    }

    free(key);

    return rv;
}

static char* cb_name(enum conflate_mgmt_cb_result r)<commit_msg>Use get_simple_kvpair_val in xmpp.c<commit_after>#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdarg.h>
#include <string.h>
#include <assert.h>

#include "conflate.h"
#include "conflate_internal.h"
#include "conflate_convenience.h"

/* Randomly generated by a fair dice roll */
#define INITIALIZATION_MAGIC 1422850115

/* The private key under which the JID to use is stored */
#define STORED_JID_KEY "stored_jid"

struct _conflate_form_result {
    xmpp_conn_t   *conn;
    xmpp_ctx_t    *ctx;
    xmpp_stanza_t *reply;
    xmpp_stanza_t *cmd_res;
    xmpp_stanza_t *container;
    xmpp_stanza_t *current;
};

struct command_def {
    char *name;
    char *description;
    conflate_mgmt_cb_t cb;
    struct command_def *next;
};

bool commands_initialized = false;
struct command_def *commands = NULL;

void conflate_register_mgmt_cb(const char *cmd, const char *desc,
                               conflate_mgmt_cb_t cb)
{
    struct command_def *c = calloc(1, sizeof(struct command_def));
    assert(c);

    c->name = safe_strdup(cmd);
    c->description = safe_strdup(desc);
    c->cb = cb;
    c->next = commands;

    commands = c;
}

static void add_and_release(xmpp_stanza_t* parent, xmpp_stanza_t* child)
{
    xmpp_stanza_add_child(parent, child);
    xmpp_stanza_release(child);
}

/* Copy an attribute from one stanza to another iff it exists in the
   src. */
static void copy_attr(xmpp_ctx_t *ctx,
                      xmpp_stanza_t* src, xmpp_stanza_t* dest,
                      const char* attr)
{
    assert(src);
    assert(dest);
    assert(attr);

    char *val = xmpp_stanza_get_attribute(src, attr);
    if (val) {
        xmpp_stanza_set_attribute(dest, attr, val);
    }
}

/* Create an IQ response */
static xmpp_stanza_t* create_reply(xmpp_ctx_t* ctx, xmpp_stanza_t* stanza)
{
    xmpp_stanza_t* reply = xmpp_stanza_new(ctx);

    assert(reply);

    xmpp_stanza_set_name(reply, "iq");
    xmpp_stanza_set_type(reply, "result");
    if (xmpp_stanza_get_id(stanza)) {
        xmpp_stanza_set_id(reply, xmpp_stanza_get_id(stanza));
    }
    xmpp_stanza_set_attribute(reply, "to",
                              xmpp_stanza_get_attribute(stanza, "from"));

    return reply;
}

/* Create an XMPP command response */
static xmpp_stanza_t* create_cmd_response(xmpp_ctx_t* ctx,
                                          xmpp_stanza_t* cmd_stanza)
{
    xmpp_stanza_t* cmd_res = xmpp_stanza_new(ctx);
    assert(cmd_res);

    xmpp_stanza_set_name(cmd_res, "command");
    copy_attr(ctx, cmd_stanza, cmd_res, "xmlns");
    copy_attr(ctx, cmd_stanza, cmd_res, "node");
    copy_attr(ctx, cmd_stanza, cmd_res, "sessionid");
    xmpp_stanza_set_attribute(cmd_res, "status", "completed");

    return cmd_res;
}

static int version_handler(xmpp_conn_t * const conn,
                           xmpp_stanza_t * const stanza,
                           void * const userdata)
{
    xmpp_stanza_t *reply, *query, *name, *version, *text;
    conflate_handle_t *handle = (conflate_handle_t*) userdata;
    xmpp_ctx_t *ctx = handle->ctx;
    char *ns;

    printf("Received version request from %s\n", xmpp_stanza_get_attribute(stanza, "from"));

    reply = xmpp_stanza_new(ctx);
    assert(reply);
    xmpp_stanza_set_name(reply, "iq");
    xmpp_stanza_set_type(reply, "result");
    xmpp_stanza_set_id(reply, xmpp_stanza_get_id(stanza));
    xmpp_stanza_set_attribute(reply, "to", xmpp_stanza_get_attribute(stanza, "from"));

    query = xmpp_stanza_new(ctx);
    assert(query);
    xmpp_stanza_set_name(query, "query");
    ns = xmpp_stanza_get_ns(xmpp_stanza_get_children(stanza));
    if (ns) {
        xmpp_stanza_set_ns(query, ns);
    }

    name = xmpp_stanza_new(ctx);
    assert(name);
    xmpp_stanza_set_name(name, "name");
    add_and_release(query, name);

    text = xmpp_stanza_new(ctx);
    assert(text);
    xmpp_stanza_set_text(text, handle->conf->software);
    add_and_release(name, text);

    version = xmpp_stanza_new(ctx);
    assert(version);
    xmpp_stanza_set_name(version, "version");
    add_and_release(query, version);

    text = xmpp_stanza_new(ctx);
    assert(text);
    xmpp_stanza_set_text(text, handle->conf->version);
    add_and_release(version, text);

    add_and_release(reply, query);

    xmpp_send(conn, reply);
    xmpp_stanza_release(reply);
    return 1;
}

static char **get_form_values(xmpp_stanza_t *t) {
    xmpp_stanza_t *current = NULL;
    int i = 0, allocated = 8;
    char **rv = calloc(allocated, sizeof(char*));
    assert(rv);

    current = xmpp_stanza_get_child_by_name(t, "value");
    while (current) {
        xmpp_stanza_t *val = xmpp_stanza_get_children(current);
        /* xmpp_stanza_get_children allocates */
        char *v = xmpp_stanza_get_text(val);

        if (i + 1 >= allocated) {
            int new_allocated = allocated << 1;
            rv = realloc(rv, sizeof(char*) * new_allocated);
            assert(rv);
        }

        rv[i++] = v;

        current = xmpp_stanza_get_next(current);
    }

    /* List terminator */
    rv[i] = NULL;

    return rv;
}

static char **get_specific_form_values(xmpp_stanza_t *field, const char *var) {
    char **rv = NULL;

    while (field && strcmp(xmpp_stanza_get_attribute(field, "var"), var) != 0) {
        field = xmpp_stanza_get_next(field);
    }

    if (field) {
        rv = get_form_values(field);
    }

    return rv;
}

static kvpair_t *grok_form(xmpp_stanza_t *fields)
{
    kvpair_t *rv = NULL;

    /* walk the field peers and grab their values and stuff */
    while (fields) {
        if (xmpp_stanza_is_tag(fields)) {
            char* k = xmpp_stanza_get_attribute(fields, "var");
            char **vals = get_specific_form_values(fields, k);
            kvpair_t* thispair = mk_kvpair(k, vals);
            free_string_list(vals);
            thispair->next = rv;
            rv = thispair;
        }
        fields = xmpp_stanza_get_next(fields);
    }

    return rv;
}

static enum conflate_mgmt_cb_result process_serverlist(void *opaque,
                                                       conflate_handle_t *handle,
                                                       const char *cmd,
                                                       bool direct,
                                                       kvpair_t *conf,
                                                       conflate_form_result *r)
{
    /* If we have "config_is_private" set to "yes" we should only
       process this if it's direct (i.e. ignore pubsub) */
    if (!direct) {
        char *priv = conflate_get_private(handle, "config_is_private",
                                          handle->conf->save_path);

        if (priv && strcmp(priv, "yes") == 0) {
            CONFLATE_LOG(handle, INFO,
                         "Currently using a private config, ignoring update.");
            return RV_OK;
        }
        free(priv);
    }

    CONFLATE_LOG(handle, INFO, "Processing a serverlist");

    /* Persist the config lists */
    if (!save_kvpairs(handle, conf, handle->conf->save_path)) {
        CONFLATE_LOG(handle, ERROR, "Can not save config to %s",
                     handle->conf->save_path);
    }

    /* Send the config to the callback */
    handle->conf->new_config(handle->conf->userdata, conf);

    return RV_OK;
}

static void add_form_values(xmpp_ctx_t* ctx, xmpp_stanza_t *parent,
                            const char *key, const char **values)
{
    xmpp_stanza_t* field = xmpp_stanza_new(ctx);
    assert(field);

    xmpp_stanza_set_name(field, "field");
    xmpp_stanza_set_attribute(field, "var", key);
    add_and_release(parent, field);

    for (int i = 0; values[i]; i++) {
        xmpp_stanza_t* value = xmpp_stanza_new(ctx);
        xmpp_stanza_t* text = xmpp_stanza_new(ctx);
        assert(value);
        assert(text);

        xmpp_stanza_set_name(value, "value");
        add_and_release(field, value);

        xmpp_stanza_set_text(text, values[i]);
        add_and_release(value, text);
    }
}

static void add_cmd_error(xmpp_ctx_t *ctx,
                          xmpp_stanza_t * reply, const char *code,
                          const char *ns, const char *name,
                          const char *specificns, const char *specificcond)
{
    xmpp_stanza_set_attribute(reply, "type", "error");

    xmpp_stanza_t *error = xmpp_stanza_new(ctx);
    assert(error);

    xmpp_stanza_set_name(error, "error");
    xmpp_stanza_set_attribute(error, "type", "modify");
    xmpp_stanza_set_attribute(error, "code", code);

    add_and_release(reply, error);

    xmpp_stanza_t *etype = xmpp_stanza_new(ctx);
    assert(etype);

    xmpp_stanza_set_name(etype, name);
    xmpp_stanza_set_attribute(etype, "xmlns", ns);
    add_and_release(error, etype);

    if (specificns && specificcond) {
        xmpp_stanza_t *specific = xmpp_stanza_new(ctx);
        assert(specific);

        xmpp_stanza_set_name(specific, specificcond);
        xmpp_stanza_set_attribute(specific, "xmlns", specificns);

        add_and_release(error, specific);
    }
}

static xmpp_stanza_t* error_unknown_command(const char *cmd,
                                            xmpp_stanza_t * const cmd_stanza,
                                            xmpp_conn_t* const conn,
                                            xmpp_stanza_t * const stanza,
                                            void * const userdata,
                                            bool direct)
{
    conflate_handle_t *handle = (conflate_handle_t*) userdata;
    xmpp_ctx_t *ctx = handle->ctx;

    xmpp_stanza_t* reply = create_reply(ctx, stanza);
    add_and_release(reply, create_cmd_response(ctx, cmd_stanza));
    add_cmd_error(ctx, reply, "404",
                  "urn:ietf:params:xml:ns:xmpp-stanzas", "item-not-found",
                  NULL, NULL);
    return reply;
}

void conflate_init_form(conflate_form_result *r)
{
    if (!r->container) {
        r->container = xmpp_stanza_new(r->ctx);

        /* X data in the command response */
        xmpp_stanza_set_name(r->container, "x");
        xmpp_stanza_set_attribute(r->container, "xmlns", "jabber:x:data");
        xmpp_stanza_set_type(r->container, "result");
        add_and_release(r->cmd_res, r->container);
    }
}

void conflate_add_field_multi(conflate_form_result *r, const char *k,
                              const char **v)
{
    conflate_init_form(r);
    if (!r->current) {
        r->current = r->container;
    }
    if (k) {
        add_form_values(r->ctx, r->current, k, v);
    }
}

void conflate_add_field(conflate_form_result *r, const char *k, const char *v)
{
    const char *vals[2] = { v, NULL };
    conflate_add_field_multi(r, k, vals);
}

void conflate_next_fieldset(conflate_form_result *r)
{
        conflate_init_form(r);
        /* Fail if some k/v pairs were already added */
        assert(r->current == NULL || r->current != r->container);

        r->current = xmpp_stanza_new(r->ctx);
        assert(r->current);

        xmpp_stanza_set_name(r->current, "item");
        add_and_release(r->container, r->current);
}

static enum conflate_mgmt_cb_result process_set_private(void *opaque,
                                                        conflate_handle_t *handle,
                                                        const char *cmd,
                                                        bool direct,
                                                        kvpair_t *form,
                                                        conflate_form_result *r)
{
    /* Only direct stat requests are handled. */
    assert(direct);
    enum conflate_mgmt_cb_result rv = RV_ERROR;

    char *key = get_simple_kvpair_val(form, "key");
    char *value = get_simple_kvpair_val(form, "value");

    if (key && value) {
        if (conflate_save_private(handle, key, value,
                                  handle->conf->save_path)) {
            rv = RV_OK;
        }
    } else {
        rv = RV_BADARG;
    }

    return rv;
}

static enum conflate_mgmt_cb_result process_get_private(void *opaque,
                                                        conflate_handle_t *handle,
                                                        const char *cmd,
                                                        bool direct,
                                                        kvpair_t *form,
                                                        conflate_form_result *r)
{
    /* Only direct stat requests are handled. */
    assert(direct);
    enum conflate_mgmt_cb_result rv = RV_ERROR;

    char *key = get_simple_kvpair_val(form, "key");

    if (key) {
        /* Initialize the form so there's always one there */
        conflate_init_form(r);
        char *value = conflate_get_private(handle, key,
                                           handle->conf->save_path);
        if (value) {
            conflate_add_field(r, key, value);
            free(value);
        }

        rv = RV_OK;
    } else {
        rv = RV_BADARG;
    }

    return rv;
}

static enum conflate_mgmt_cb_result process_delete_private(void *opaque,
                                                           conflate_handle_t *handle,
                                                           const char *cmd,
                                                           bool direct,
                                                           kvpair_t *form,
                                                           conflate_form_result *r)
{
    /* Only direct stat requests are handled. */
    assert(direct);
    enum conflate_mgmt_cb_result rv = RV_ERROR;

    char *key = get_simple_kvpair_val(form, "key");

    if (key) {
        if (conflate_delete_private(handle, key,
                                    handle->conf->save_path)) {
            rv = RV_OK;
        }
    } else {
        rv = RV_BADARG;
    }

    return rv;
}

static char* cb_name(enum conflate_mgmt_cb_result r)
{'fraction_non_alphanumeric': 0.10385840707964601, 'fraction_numerical': 0.001663716814159292, 'mean_word_length': 2.0315484494044425, 'pattern_counts': {'":': 0, '<': 19, '<?xml version=': 0, '>': 116, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 10}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 116, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3028066', 'n_tokens_mistral': 9489, 'n_tokens_neox': 9221, 'n_words': 1829}