[Libreoffice-commits] core.git: 2 commits - configmgr/source

Stephan Bergmann sbergman at redhat.com
Tue Sep 1 09:58:40 PDT 2015


 configmgr/source/components.cxx    |    4 
 configmgr/source/dconf.cxx         |  816 ++++++++++++++++++++++++++++++++-----
 configmgr/source/dconf.hxx         |    7 
 configmgr/source/modifications.hxx |    2 
 4 files changed, 730 insertions(+), 99 deletions(-)

New commits:
commit e96b8bd4d4fd4bce48bacf69bed5d5fe10237dec
Author: Stephan Bergmann <sbergman at redhat.com>
Date:   Tue Sep 1 18:55:30 2015 +0200

    configmgr: support writing back to dconf (WIP)
    
    Needs to be enabled manually for now by changing CONFIGURATION_LAYERS's tail
    from " user:..." to " user:*... dconf:!".
    
    Change-Id: I31cd806f21d2ded376832faf98f49167b7243d1c

diff --git a/configmgr/source/components.cxx b/configmgr/source/components.cxx
index 1b9066a..a51c5ac 100644
--- a/configmgr/source/components.cxx
+++ b/configmgr/source/components.cxx
@@ -291,7 +291,9 @@ void Components::writeModifications() {
             }
             break;
         case ModificationTarget::Dconf:
-            //TODO
+#if ENABLE_DCONF
+            dconf::writeModifications(*this, data_);
+#endif
             break;
         }
     }
diff --git a/configmgr/source/dconf.cxx b/configmgr/source/dconf.cxx
index 331545a..ce871a3 100644
--- a/configmgr/source/dconf.cxx
+++ b/configmgr/source/dconf.cxx
@@ -12,12 +12,19 @@
 #include <cassert>
 #include <cstddef>
 #include <cstring>
+#include <forward_list>
 #include <limits>
 #include <type_traits>
+#include <vector>
 
+extern "C" {
+    // <https://bugzilla.gnome.org/show_bug.cgi?id=754245>
+    // "common/dconf-changeset.h etc. lack extern "C" wrapper for C++"
 #include <dconf/dconf.h>
+}
 
 #include <com/sun/star/uno/Sequence.hxx>
+#include <rtl/ustrbuf.hxx>
 
 #include <data.hxx>
 #include <dconf.hxx>
@@ -128,7 +135,7 @@ private:
 
 class GVariantHolder {
 public:
-    explicit GVariantHolder(GVariant * variant): variant_(variant) {}
+    explicit GVariantHolder(GVariant * variant = nullptr): variant_(variant) {}
 
     ~GVariantHolder() { unref(); }
 
@@ -137,6 +144,8 @@ public:
         variant_ = variant;
     }
 
+    void release() { variant_ = nullptr; }
+
     GVariant * get() const { return variant_; }
 
 private:
@@ -152,6 +161,25 @@ private:
     GVariant * variant_;
 };
 
+class GVariantTypeHolder {
+public:
+    explicit GVariantTypeHolder(GVariantType * type): type_(type) {}
+
+    ~GVariantTypeHolder() {
+        if (type_ != nullptr) {
+            g_variant_type_free(type_);
+        }
+    }
+
+    GVariantType * get() const { return type_; }
+
+private:
+    GVariantTypeHolder(GVariantTypeHolder &) = delete;
+    void operator =(GVariantTypeHolder) = delete;
+
+    GVariantType * type_;
+};
+
 class StringArrayHolder {
 public:
     explicit StringArrayHolder(gchar ** array): array_(array) {}
@@ -167,6 +195,27 @@ private:
     gchar ** array_;
 };
 
+class ChangesetHolder {
+public:
+    explicit ChangesetHolder(DConfChangeset * changeset):
+        changeset_(changeset)
+    {}
+
+    ~ChangesetHolder() {
+        if (changeset_ != nullptr) {
+            dconf_changeset_unref(changeset_);
+        }
+    }
+
+    DConfChangeset * get() const { return changeset_; }
+
+private:
+    ChangesetHolder(ChangesetHolder &) = delete;
+    void operator =(ChangesetHolder) = delete;
+
+    DConfChangeset * changeset_;
+};
+
 bool decode(OUString * string, bool slash) {
     for (sal_Int32 i = 0;; ++i) {
         i = string->indexOf('\\', i);
@@ -990,6 +1039,509 @@ void readDir(
     }
 }
 
+OString encodeSegment(OUString const & name, bool setElement) {
+    if (!setElement) {
+        return name.toUtf8();
+    }
+    OUStringBuffer buf;
+    for (sal_Int32 i = 0; i != name.getLength(); ++i) {
+        sal_Unicode c = name[i];
+        switch (c) {
+        case '\0':
+            buf.append("\\00");
+            break;
+        case '/':
+            buf.append("\\2F");
+            break;
+        case '\\':
+            buf.append("\\5C");
+            break;
+        default:
+            buf.append(c);
+        }
+    }
+    return buf.makeStringAndClear().toUtf8();
+}
+
+OString encodeString(OUString const & value) {
+    OUStringBuffer buf;
+    for (sal_Int32 i = 0; i != value.getLength(); ++i) {
+        sal_Unicode c = value[i];
+        switch (c) {
+        case '\0':
+            buf.append("\\00");
+            break;
+        case '\\':
+            buf.append("\\5C");
+            break;
+        default:
+            buf.append(c);
+        }
+    }
+    return buf.makeStringAndClear().toUtf8();
+}
+
+bool addProperty(
+    ChangesetHolder const & changeset, OString const & pathRepresentation,
+    Type type, bool nillable, css::uno::Any const & value)
+{
+    Type dynType = getDynamicType(value);
+    assert(dynType != TYPE_ERROR);
+    if (type == TYPE_ANY) {
+        type = dynType;
+    }
+    GVariantHolder v;
+    std::forward_list<GVariantHolder> children;
+    if (dynType == TYPE_NIL) {
+        switch (type) {
+        case TYPE_BOOLEAN:
+            v.reset(g_variant_new_maybe(G_VARIANT_TYPE_BOOLEAN, nullptr));
+            break;
+        case TYPE_SHORT:
+            v.reset(g_variant_new_maybe(G_VARIANT_TYPE_INT16, nullptr));
+            break;
+        case TYPE_INT:
+            v.reset(g_variant_new_maybe(G_VARIANT_TYPE_INT32, nullptr));
+            break;
+        case TYPE_LONG:
+            v.reset(g_variant_new_maybe(G_VARIANT_TYPE_INT64, nullptr));
+            break;
+        case TYPE_DOUBLE:
+            v.reset(g_variant_new_maybe(G_VARIANT_TYPE_DOUBLE, nullptr));
+            break;
+        case TYPE_STRING:
+            v.reset(g_variant_new_maybe(G_VARIANT_TYPE_STRING, nullptr));
+            break;
+        case TYPE_HEXBINARY:
+        case TYPE_BOOLEAN_LIST:
+        case TYPE_SHORT_LIST:
+        case TYPE_INT_LIST:
+        case TYPE_LONG_LIST:
+        case TYPE_DOUBLE_LIST:
+        case TYPE_STRING_LIST:
+        case TYPE_HEXBINARY_LIST:
+            {
+                static char const * const typeString[
+                    TYPE_HEXBINARY_LIST - TYPE_HEXBINARY + 1]
+                    = { "ay", "ab", "an", "ai", "ax", "ad", "as", "aay" };
+                GVariantTypeHolder ty(
+                    g_variant_type_new(typeString[type - TYPE_HEXBINARY]));
+                if (ty.get() == nullptr) {
+                    SAL_WARN("configmgr.dconf", "g_variant_type_new failed");
+                    return false;
+                }
+                v.reset(g_variant_new_maybe(ty.get(), nullptr));
+                break;
+            }
+        default:
+            assert(false); // this cannot happen
+            break;
+        }
+        if (v.get() == nullptr) {
+            SAL_WARN("configmgr.dconf", "g_variant_new_maybe failed");
+            return false;
+        }
+    } else {
+        switch (type) {
+        case TYPE_BOOLEAN:
+            v.reset(g_variant_new_boolean(value.get<bool>()));
+            break;
+        case TYPE_SHORT:
+            v.reset(g_variant_new_int16(value.get<sal_Int16>()));
+            break;
+        case TYPE_INT:
+            v.reset(g_variant_new_int32(value.get<sal_Int32>()));
+            break;
+        case TYPE_LONG:
+            v.reset(g_variant_new_int64(value.get<sal_Int64>()));
+            break;
+        case TYPE_DOUBLE:
+            v.reset(g_variant_new_double(value.get<double>()));
+            break;
+        case TYPE_STRING:
+            v.reset(
+                g_variant_new_string(
+                    encodeString(value.get<OUString>()).getStr()));
+            break;
+        case TYPE_HEXBINARY:
+            {
+                css::uno::Sequence<sal_Int8> seq(
+                    value.get<css::uno::Sequence<sal_Int8>>());
+                static_assert(
+                    std::numeric_limits<sal_Int32>::max() <= G_MAXSIZE,
+                    "G_MAXSIZE too small");
+                static_assert(
+                    sizeof (sal_Int8) == sizeof (guchar), "size mismatch");
+                v.reset(
+                    g_variant_new_fixed_array(
+                        G_VARIANT_TYPE_BYTE, seq.getConstArray(),
+                        seq.getLength(), sizeof (sal_Int8)));
+                break;
+            }
+        case TYPE_BOOLEAN_LIST:
+            {
+                css::uno::Sequence<sal_Bool> seq(
+                    value.get<css::uno::Sequence<sal_Bool>>());
+                static_assert(
+                    std::numeric_limits<sal_Int32>::max() <= G_MAXSIZE,
+                    "G_MAXSIZE too small");
+                static_assert(sizeof (sal_Bool) == 1, "size mismatch");
+                v.reset(
+                    g_variant_new_fixed_array(
+                        G_VARIANT_TYPE_BOOLEAN, seq.getConstArray(),
+                        seq.getLength(), sizeof (sal_Bool)));
+                break;
+            }
+        case TYPE_SHORT_LIST:
+            {
+                css::uno::Sequence<sal_Int16> seq(
+                    value.get<css::uno::Sequence<sal_Int16>>());
+                static_assert(
+                    std::numeric_limits<sal_Int32>::max() <= G_MAXSIZE,
+                    "G_MAXSIZE too small");
+                static_assert(
+                    sizeof (sal_Int16) == sizeof (gint16), "size mismatch");
+                v.reset(
+                    g_variant_new_fixed_array(
+                        G_VARIANT_TYPE_INT16, seq.getConstArray(),
+                        seq.getLength(), sizeof (sal_Int16)));
+                    //TODO: endian-ness?
+                break;
+            }
+        case TYPE_INT_LIST:
+            {
+                css::uno::Sequence<sal_Int32> seq(
+                    value.get<css::uno::Sequence<sal_Int32>>());
+                static_assert(
+                    std::numeric_limits<sal_Int32>::max() <= G_MAXSIZE,
+                    "G_MAXSIZE too small");
+                static_assert(
+                    sizeof (sal_Int32) == sizeof (gint32), "size mismatch");
+                v.reset(
+                    g_variant_new_fixed_array(
+                        G_VARIANT_TYPE_INT32, seq.getConstArray(),
+                        seq.getLength(), sizeof (sal_Int32)));
+                    //TODO: endian-ness?
+                break;
+            }
+        case TYPE_LONG_LIST:
+            {
+                css::uno::Sequence<sal_Int64> seq(
+                    value.get<css::uno::Sequence<sal_Int64>>());
+                static_assert(
+                    std::numeric_limits<sal_Int32>::max() <= G_MAXSIZE,
+                    "G_MAXSIZE too small");
+                static_assert(
+                    sizeof (sal_Int64) == sizeof (gint64), "size mismatch");
+                v.reset(
+                    g_variant_new_fixed_array(
+                        G_VARIANT_TYPE_INT64, seq.getConstArray(),
+                        seq.getLength(), sizeof (sal_Int64)));
+                    //TODO: endian-ness?
+                break;
+            }
+        case TYPE_DOUBLE_LIST:
+            {
+                css::uno::Sequence<double> seq(
+                    value.get<css::uno::Sequence<double>>());
+                static_assert(
+                    std::numeric_limits<sal_Int32>::max() <= G_MAXSIZE,
+                    "G_MAXSIZE too small");
+                static_assert(
+                    sizeof (double) == sizeof (gdouble), "size mismatch");
+                v.reset(
+                    g_variant_new_fixed_array(
+                        G_VARIANT_TYPE_DOUBLE, seq.getConstArray(),
+                        seq.getLength(), sizeof (double)));
+                    //TODO: endian-ness?
+                break;
+            }
+        case TYPE_STRING_LIST:
+            {
+                css::uno::Sequence<OUString> seq(
+                    value.get<css::uno::Sequence<OUString>>());
+                std::vector<GVariant *> vs;
+                for (sal_Int32 i = 0; i != seq.getLength(); ++i) {
+                    children.emplace_front(
+                        g_variant_new_string(encodeString(seq[i]).getStr()));
+                    if (children.front().get() == nullptr) {
+                        SAL_WARN(
+                            "configmgr.dconf", "g_variant_new_string failed");
+                        return false;
+                    }
+                    vs.push_back(children.front().get());
+                }
+                static_assert(
+                    std::numeric_limits<sal_Int32>::max() <= G_MAXSIZE,
+                    "G_MAXSIZE too small");
+                v.reset(
+                    g_variant_new_array(
+                        G_VARIANT_TYPE_STRING, vs.data(), seq.getLength()));
+                break;
+            }
+        case TYPE_HEXBINARY_LIST:
+            {
+                css::uno::Sequence<css::uno::Sequence<sal_Int8>> seq(
+                    value.get<
+                        css::uno::Sequence<css::uno::Sequence<sal_Int8>>>());
+                std::vector<GVariant *> vs;
+                for (sal_Int32 i = 0; i != seq.getLength(); ++i) {
+                    static_assert(
+                        std::numeric_limits<sal_Int32>::max() <= G_MAXSIZE,
+                        "G_MAXSIZE too small");
+                    static_assert(
+                        sizeof (sal_Int8) == sizeof (guchar), "size mismatch");
+                    children.emplace_front(
+                        g_variant_new_fixed_array(
+                            G_VARIANT_TYPE_BYTE, seq[i].getConstArray(),
+                            seq[i].getLength(), sizeof (sal_Int8)));
+                    if (children.front().get() == nullptr) {
+                        SAL_WARN(
+                            "configmgr.dconf",
+                            "g_variant_new_fixed_array failed");
+                        return false;
+                    }
+                    vs.push_back(children.front().get());
+                }
+                GVariantTypeHolder ty(g_variant_type_new("aay"));
+                if (ty.get() == nullptr) {
+                    SAL_WARN("configmgr.dconf", "g_variant_type_new failed");
+                    return false;
+                }
+                static_assert(
+                    std::numeric_limits<sal_Int32>::max() <= G_MAXSIZE,
+                    "G_MAXSIZE too small");
+                v.reset(
+                    g_variant_new_array(ty.get(), vs.data(), seq.getLength()));
+                break;
+            }
+        default:
+            assert(false); // this cannot happen
+            break;
+        }
+        if (v.get() == nullptr) {
+            SAL_WARN("configmgr.dconf", "GVariant creation failed");
+            return false;
+        }
+        if (nillable) {
+            GVariantHolder v1(g_variant_new_maybe(nullptr, v.get()));
+            if (v1.get() == nullptr) {
+                SAL_WARN("configmgr.dconf", "g_variant_new_maybe failed");
+                return false;
+            }
+            v.release();
+            v.reset(v1.get());
+            v1.release();
+        }
+    }
+    dconf_changeset_set(
+        changeset.get(), pathRepresentation.getStr(), v.get());
+    for (auto & i: children) {
+        i.release();
+    }
+    v.release();
+    return true;
+}
+
+bool addNode(
+    Components & components, ChangesetHolder const & changeset,
+    rtl::Reference<Node> const & parent, OString const & pathRepresentation,
+    rtl::Reference<Node> const & node)
+{
+    switch (node->kind()) {
+    case Node::KIND_PROPERTY:
+        {
+            PropertyNode * prop = static_cast<PropertyNode *>(node.get());
+            if (!addProperty(
+                    changeset, pathRepresentation, prop->getStaticType(),
+                    prop->isNillable(), prop->getValue(components)))
+            {
+                return false;
+            }
+            break;
+        }
+    case Node::KIND_LOCALIZED_VALUE:
+        {
+            //TODO: name.isEmpty()?
+            LocalizedPropertyNode * locprop
+                = static_cast<LocalizedPropertyNode *>(parent.get());
+            if (!addProperty(
+                    changeset, pathRepresentation,
+                    locprop->getStaticType(), locprop->isNillable(),
+                    static_cast<LocalizedValueNode *>(node.get())->getValue()))
+            {
+                return false;
+            }
+            break;
+        }
+    case Node::KIND_LOCALIZED_PROPERTY:
+    case Node::KIND_GROUP:
+    case Node::KIND_SET:
+        for (auto const & i: node->getMembers()) {
+            OUString templ(i.second->getTemplateName());
+            OString path(
+                pathRepresentation + "/"
+                + encodeSegment(i.first, !templ.isEmpty()));
+            if (!templ.isEmpty()) {
+                path += "/";
+                GVariantHolder v(g_variant_new_string("replace"));
+                if (v.get() == nullptr) {
+                    SAL_WARN("configmgr.dconf", "g_variant_new_string failed");
+                    return false;
+                }
+                dconf_changeset_set(
+                    changeset.get(), OString(path + "op").getStr(), v.get());
+                v.release();
+                v.reset(g_variant_new_string(encodeString(templ).getStr()));
+                if (v.get() == nullptr) {
+                    SAL_WARN("configmgr.dconf", "g_variant_new_string failed");
+                    return false;
+                }
+                dconf_changeset_set(
+                    changeset.get(), OString(path + "template").getStr(),
+                    v.get());
+                v.release();
+                path += "content";
+            }
+            if (!addNode(components, changeset, parent, path, i.second)) {
+                return false;
+            }
+        }
+        break;
+    case Node::KIND_ROOT:
+        assert(false); // this cannot happen
+        break;
+    }
+    return true;
+}
+
+bool addModifications(
+    Components & components, ChangesetHolder const & changeset,
+    OString const & parentPathRepresentation,
+    rtl::Reference<Node> const & parent, OUString const & nodeName,
+    rtl::Reference<Node> const & node,
+    Modifications::Node const & modifications)
+{
+    // It is never necessary to write oor:finalized or oor:mandatory attributes,
+    // as they cannot be set via the UNO API.
+    if (modifications.children.empty()) {
+        assert(parent.is());
+            // components themselves have no parent but must have children
+        if (node.is()) {
+            OUString templ(node->getTemplateName());
+            OString path(
+                parentPathRepresentation + "/"
+                + encodeSegment(nodeName, !templ.isEmpty()));
+            if (!templ.isEmpty()) {
+                path += "/";
+                GVariantHolder v(g_variant_new_string("replace"));
+                if (v.get() == nullptr) {
+                    SAL_WARN("configmgr.dconf", "g_variant_new_string failed");
+                    return false;
+                }
+                dconf_changeset_set(
+                    changeset.get(), OString(path + "op").getStr(), v.get());
+                v.release();
+                v.reset(g_variant_new_string(encodeString(templ).getStr()));
+                if (v.get() == nullptr) {
+                    SAL_WARN("configmgr.dconf", "g_variant_new_string failed");
+                    return false;
+                }
+                dconf_changeset_set(
+                    changeset.get(), OString(path + "template").getStr(),
+                    v.get());
+                v.release();
+                path += "content";
+            }
+            if (!addNode(components, changeset, parent, path, node)) {
+                return false;
+            }
+        } else {
+            switch (parent->kind()) {
+            case Node::KIND_LOCALIZED_PROPERTY:
+            case Node::KIND_GROUP:
+                {
+                    GVariantHolder v(g_variant_new_tuple(nullptr, 0));
+                    if (v.get() == nullptr) {
+                        SAL_WARN(
+                            "configmgr.dconf", "g_variant_new_tuple failed");
+                        return false;
+                    }
+                    OString path(parentPathRepresentation);
+                    if (!nodeName.isEmpty()) { // KIND_LOCALIZED_PROPERTY
+                        path += "/" + encodeSegment(nodeName, false);
+                    }
+                    dconf_changeset_set(
+                        changeset.get(), path.getStr(), v.get());
+                    v.release();
+                    break;
+                }
+            case Node::KIND_SET:
+                {
+                    OString path(
+                        parentPathRepresentation + "/"
+                        + encodeSegment(nodeName, true) + "/");
+                    GVariantHolder v(g_variant_new_string("remove"));
+                    if (v.get() == nullptr) {
+                        SAL_WARN(
+                            "configmgr.dconf", "g_variant_new_string failed");
+                        return false;
+                    }
+                    dconf_changeset_set(
+                        changeset.get(), OString(path + "op").getStr(),
+                        v.get());
+                    v.release();
+                    dconf_changeset_set(
+                        changeset.get(), OString(path + "template").getStr(),
+                        nullptr);
+                    dconf_changeset_set(
+                        changeset.get(), OString(path + "content/").getStr(),
+                        nullptr);
+                    break;
+                }
+            default:
+                assert(false); // this cannot happen
+                break;
+            }
+        }
+    } else {
+        assert(node.is());
+        OUString templ(node->getTemplateName());
+        OString path(
+            parentPathRepresentation + "/"
+            + encodeSegment(nodeName, !templ.isEmpty()));
+        if (!templ.isEmpty()) {
+            path += "/";
+            GVariantHolder v(g_variant_new_string("fuse"));
+            if (v.get() == nullptr) {
+                SAL_WARN("configmgr.dconf", "g_variant_new_string failed");
+                return false;
+            }
+            dconf_changeset_set(
+                changeset.get(), OString(path + "op").getStr(), v.get());
+            v.release();
+            v.reset(g_variant_new_string(encodeString(templ).getStr()));
+            if (v.get() == nullptr) {
+                SAL_WARN("configmgr.dconf", "g_variant_new_string failed");
+                return false;
+            }
+            dconf_changeset_set(
+                changeset.get(), OString(path + "template").getStr(), v.get());
+            v.release();
+            path += "content";
+        }
+        for (auto const & i: modifications.children) {
+            if (!addModifications(
+                    components, changeset, path, node, i.first,
+                    node->getMember(i.first), i.second))
+            {
+                return false;
+            }
+        }
+    }
+    return true;
+}
+
 }
 
 void readLayer(Data & data, int layer) {
@@ -1003,6 +1555,36 @@ void readLayer(Data & data, int layer) {
         "/org/libreoffice/registry/");
 }
 
+void writeModifications(Components & components, Data & data) {
+    GObjectHolder<DConfClient> client(dconf_client_new());
+    if (client.get() == nullptr) {
+        SAL_WARN("configmgr.dconf", "dconf_client_new failed");
+    }
+    ChangesetHolder cs(dconf_changeset_new());
+    if (cs.get() == nullptr) {
+        SAL_WARN("configmgr.dconf", "dconf_changeset_new failed");
+        return;
+    }
+    for (auto const & i: data.modifications.getRoot().children) {
+        if (!addModifications(
+                components, cs, "/org/libreoffice/registry",
+                rtl::Reference<Node>(), i.first,
+                data.getComponents().findNode(Data::NO_LAYER, i.first),
+                i.second))
+        {
+            return;
+        }
+    }
+    if (!dconf_client_change_sync(
+            client.get(), cs.get(), nullptr, nullptr, nullptr))
+    {
+        //TODO: GError
+        SAL_WARN("configmgr.dconf", "dconf_client_change_sync failed");
+        return;
+    }
+    data.modifications.clear();
+}
+
 } }
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/configmgr/source/dconf.hxx b/configmgr/source/dconf.hxx
index 0f63e04..f7f5970 100644
--- a/configmgr/source/dconf.hxx
+++ b/configmgr/source/dconf.hxx
@@ -12,12 +12,17 @@
 
 #include <sal/config.h>
 
-namespace configmgr { struct Data; }
+namespace configmgr {
+    class Components;
+    struct Data;
+}
 
 namespace configmgr { namespace dconf {
 
 void readLayer(Data & data, int layer);
 
+void writeModifications(Components & components, Data & data);
+
 } }
 
 #endif
diff --git a/configmgr/source/modifications.hxx b/configmgr/source/modifications.hxx
index 04ad5c3..098992d 100644
--- a/configmgr/source/modifications.hxx
+++ b/configmgr/source/modifications.hxx
@@ -45,6 +45,8 @@ public:
 
     void remove(Path const & path);
 
+    void clear() { root_.children.clear(); }
+
     bool empty() const { return root_.children.empty(); }
 
     Node const & getRoot() const { return root_;}
commit 004ede28a23ab1326ba553e0bf358e98c19a53e8
Author: Stephan Bergmann <sbergman at redhat.com>
Date:   Tue Sep 1 16:32:55 2015 +0200

    dconf: Change the way set elements are encoded
    
    ...to avoid having multiple dconf paths for the same set element (with different
    template names or operations encoded into the dconf path segment), esp. when
    introducing write-back into dconf.
    
    Change-Id: Ieebad3b1024dd7607022abbfa963850b05c26d65

diff --git a/configmgr/source/dconf.cxx b/configmgr/source/dconf.cxx
index ca94a9b..331545a 100644
--- a/configmgr/source/dconf.cxx
+++ b/configmgr/source/dconf.cxx
@@ -34,9 +34,8 @@
 //   "org.openoffice.Setup") maps to dconf paths underneath
 //   "/org/libreoffice/registry/".
 //
-// * Component, group, set, and localized property nodes map to dconf dirs
-//   (except for removal of set elements, see below), while property and
-//   localized value nodes map to dconf keys.
+// * Component, group, set, and localized property nodes map to dconf dirs,
+//   while property and localized value nodes map to dconf keys.
 //
 // * The names of nodes that are not set elements are used directly as dconf
 //   path segments.  (The syntax for node names is any non-empty sequences of
@@ -45,31 +44,30 @@
 //   it'd be nice if you used path separators instead of dots though, they have
 //   meaning in dconf/gvdb world :-)"?)
 //
-// * Set element "fuse" and "replace" operations are encoded as dconf path
-//   segments as concatenations
+// * The names of set element nodes are encoded as dconf path segments as
+//   follows: each occurrence of U+0000 NULL is replace by the three characters
+//   "\00", each occurrence of U+002F SOLIDUS is replaced by the three
+//   characters "\2F", and each ocurrence of U+005C REVERSE SOLIDUS is replaced
+//   by the three characters "\5C".
 //
-//     N ; T ; O
+// * Set elements (which must themselves be either sets or groups) map to
+//   "indirection" dconf dirs as follows:
 //
-//   where ";" represents U+003B SEMICOLON; N is an encoding of the node name,
-//   where each occurrence of U+0000 NULL is replace by the three characters
-//   "\00", each occurrence of U+002F SOLIDUS is replaced by the three
-//   characters "\2F", each occurrence of U+003B SEMICOLON is replaced by the
-//   three characters "\3B", and each ocurrence of U+005C REVERSE SOLIDUS is
-//   replaced by the three characters "\5C"; T is an encoding of the full
-//   template name, where each occurrence of U+002F SOLIDUS is replaced by the
-//   three characters "\2F", each occurrence of U+003B SEMICOLON is replaced by
-//   the three characters "\3B", and each ocurrence of U+005C REVERSE SOLIDUS is
-//   replaced by the three characters "\5C"; and O is "fuse" or "replace",
-//   respectively.
+// ** The dir must contain a key named "op" of string type, with a value of
+//    "fuse", "replace", or "remove".
+//
+// ** If "op" is "fuse" or "replace", the dir must contain exactly the following
+//    further keys and dirs:
+//
+// *** The dir must contain a key named "template" of string type, containing
+//     the full template name, encoded as follows: each occurrence of U+0000
+//     NULL is replace by the three characters "\00" and each occurrence of
+//     U+005C REVERSE SOLIDUS is replaced by the three characters "\5C".
+//
+// *** The dir must contain a dir named "content" that contains the set
+//     element's (i.e., set or group node's) real content.
 //
-// * Set element and property "remove" operations are encoded as dconf key path
-//   segments as follows, and the associated value being a GVariant of empty
-//   tuple type.  For set elements, the dconf key path segment consists of an
-//   encoding of the node name, where each occurrence of U+0000 NULL is replace
-//   by the three characters "\00", each occurrence of U+002F SOLIDUS is
-//   replaced by the three characters "\2F", and each ocurrence of U+005C
-//   REVERSE SOLIDUS is replaced by the three characters "\5C".  For properties,
-//   the dconf key path segment directly uses the node name.
+// ** If "op" is "remove", the dir must contain no further keys or dirs.
 //
 // * Property and localized property value "fuse" operations map to GVariant
 //   instances as follows:
@@ -95,6 +93,8 @@
 //
 // ** Nillable values recursively map to GVariant maybe instances.
 //
+// * Property "remove" operations map to GVariant instances of empty tuple type.
+//
 // Finalization:  The component-update.dtd allows for finalization of
 // oor:component-data, node, and prop elements, while dconf allows for locking
 // of individual keys.  That does not match, but just mark the individual Node
@@ -167,18 +167,16 @@ private:
     gchar ** array_;
 };
 
-bool decode(OUString * string, bool nul, bool slash, bool semicolon) {
+bool decode(OUString * string, bool slash) {
     for (sal_Int32 i = 0;; ++i) {
         i = string->indexOf('\\', i);
         if (i == -1) {
             return true;
         }
-        if (nul && string->match("00", i + 1)) {
+        if (string->match("00", i + 1)) {
             *string = string->replaceAt(i, 3, OUString(sal_Unicode(0)));
         } else if (slash && string->match("2F", i + 1)) {
             *string = string->replaceAt(i, 3, "/");
-        } else if (semicolon && string->match("3B", i + 1)) {
-            *string = string->replaceAt(i, 3, ";");
         } else if (string->match("5C", i + 1)) {
             *string = string->replaceAt(i + 1, 2, "");
         } else {
@@ -285,7 +283,7 @@ bool getStringValue(
         SAL_WARN("configmgr.dconf", "non--UTF-8 string value for key " << key);
         return false;
     }
-    return decode(value, true, false, false);
+    return decode(value, false);
 }
 
 bool getString(
@@ -721,9 +719,9 @@ void readDir(
         }
         OString s(*p, static_cast<sal_Int32>(n));
         OString path(dir + s);
-        OUString seg;
+        OUString name;
         if (!rtl_convertStringToUString(
-                &seg.pData, s.getStr(), s.getLength(), RTL_TEXTENCODING_UTF8,
+                &name.pData, s.getStr(), s.getLength(), RTL_TEXTENCODING_UTF8,
                 (RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR
                  | RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_ERROR
                  | RTL_TEXTTOUNICODE_FLAGS_INVALID_ERROR)))
@@ -731,87 +729,129 @@ void readDir(
             SAL_WARN("configmgr.dconf", "non--UTF-8 dir/key in dir " << dir);
             continue;
         }
-        bool isDir = seg.endsWith("/", &seg);
-        bool remove;
-        OUString name;
+        bool isDir = name.endsWith("/", &name);
         OUString templ;
+        bool remove;
         bool replace;
         if (node.is() && node->kind() == Node::KIND_SET) {
-            if (isDir) {
-                remove = false;
-                sal_Int32 i1 = seg.indexOf(';');
-                if (i1 == -1) {
-                    SAL_WARN(
-                        "configmgr.dconf", "bad set element syntax " << path);
-                    continue;
-                }
-                name = seg.copy(0, i1);
-                if (!decode(&name, true, true, true)) {
-                    continue;
-                }
-                ++i1;
-                sal_Int32 i2 = seg.indexOf(';', i1);
-                if (i2 == -1) {
-                    SAL_WARN(
-                        "configmgr.dconf", "bad set element syntax " << path);
-                    continue;
-                }
-                templ = seg.copy(i1, i2 - i1);
-                if (!decode(&templ, false, true, true)) {
-                    continue;
-                }
-                ++i2;
-                if (rtl_ustr_asciil_reverseCompare_WithLength(
-                        seg.getStr() + i2, seg.getLength() - i2, "fuse",
-                        std::strlen("fuse"))
-                    == 0)
-                {
-                    replace = false;
-                } else if (rtl_ustr_asciil_reverseCompare_WithLength(
-                               seg.getStr() + i2, seg.getLength() - i2,
-                               "replace", std::strlen("replace"))
-                           == 0)
-                {
-                    replace = true;
+            if (!isDir) {
+                SAL_WARN(
+                    "configmgr.dconf",
+                    "bad key " << path << " does not match set element");
+                continue;
+            }
+            if (!decode(&name, true)) {
+                continue;
+            }
+            enum class Op { None, Fuse, Replace, Remove };
+            Op op = Op::None;
+            bool content = false;
+            bool bad = false;
+            StringArrayHolder a2(
+                dconf_client_list(client.get(), path.getStr(), nullptr));
+            for (char const * const * p2 = a2.get(); *p2 != nullptr; ++p2) {
+                if (std::strcmp(*p2, "op") == 0) {
+                    OString path2(path + "op");
+                    GVariantHolder v(
+                        dconf_client_read(client.get(), path2.getStr()));
+                    if (v.get() == nullptr) {
+                        SAL_WARN(
+                            "configmgr.dconf", "cannot read key " << path2);
+                        bad = true;
+                        break;
+                    }
+                    OUString ops;
+                    if (!getStringValue(path2, v, &ops)) {
+                        bad = true;
+                        break;
+                    }
+                    if (ops == "fuse") {
+                        op = Op::Fuse;
+                    } else if (ops == "replace") {
+                        op = Op::Replace;
+                    } else if (ops == "remove") {
+                        op = Op::Remove;
+                    } else {
+                        SAL_WARN(
+                            "configmgr.dconf",
+                            "bad key " << path2 << " value " << ops);
+                        bad = true;
+                        break;
+                    }
+                } else if (std::strcmp(*p2, "template") == 0) {
+                    OString path2(path + "template");
+                    GVariantHolder v(
+                        dconf_client_read(client.get(), path2.getStr()));
+                    if (v.get() == nullptr) {
+                        SAL_WARN(
+                            "configmgr.dconf", "cannot read key " << path2);
+                        bad = true;
+                        break;
+                    }
+                    if (!getStringValue(path2, v, &templ)) {
+                        bad = true;
+                        break;
+                    }
+                    if (!static_cast<SetNode *>(node.get())->
+                        isValidTemplate(templ))
+                    {
+                        SAL_WARN(
+                            "configmgr.dconf",
+                            "bad key " << path2 << " value " << templ
+                                << " denotes unsupported set element template");
+                        bad = true;
+                        break;
+                    }
+                } else if (std::strcmp(*p2, "content/") == 0) {
+                    content = true;
                 } else {
                     SAL_WARN(
-                        "configmgr.dconf", "bad set element syntax " << path);
-                    continue;
+                        "configmgr.dconf",
+                        "bad dir/key " << p2
+                            << " in set element indirection dir " << path);
+                    bad = true;
+                    break;
                 }
-                rtl::Reference<SetNode> set(static_cast<SetNode *>(node.get()));
-                if (!set->isValidTemplate(templ)) {
+            }
+            if (bad) {
+                continue;
+            }
+            switch (op) {
+            case Op::None:
+                SAL_WARN(
+                    "configmgr.dconf",
+                    "bad set element indirection dir " << path
+                        << " missing \"op\" key");
+                continue;
+            case Op::Fuse:
+            case Op::Replace:
+                if (templ.isEmpty() || !content) {
                     SAL_WARN(
                         "configmgr.dconf",
-                        "bad " << path
-                            << " denotes unsupported set element template");
+                        "missing \"content\" and/or \"template\" dir/key in "
+                            "\"op\" = \"fuse\"/\"remove\" set element"
+                            " indirection dir " << path);
                     continue;
                 }
-            } else {
-                remove = true;
-                name = seg;
-                if (!decode(&name, true, true, false)) {
-                    continue;
-                }
-                replace = false;
-                assert(!path.endsWith("/"));
-                GVariantHolder v(
-                    dconf_client_read(client.get(), path.getStr()));
-                if (v.get() == nullptr) {
-                    SAL_WARN("configmgr.dconf", "cannot read key " << path);
-                    continue;
-                }
-                if (std::strcmp(g_variant_get_type_string(v.get()), "()") != 0)
-                {
+                path += "content/";
+                remove = false;
+                replace = op == Op::Replace;
+                break;
+            case Op::Remove:
+                if (!templ.isEmpty() || content) {
                     SAL_WARN(
                         "configmgr.dconf",
-                        "bad " << path
-                            << " does not denote set element removal");
+                        "bad \"content\" and/or \"template\" dir/key in \"op\" "
+                            "= \"remove\" set element indirection dir "
+                            << path);
                     continue;
                 }
+                remove = true;
+                replace = false;
+                break;
             }
         } else {
             remove = false;
-            name = seg;
             replace = false;
         }
         rtl::Reference<Node> member(members.findNode(layer, name));


More information about the Libreoffice-commits mailing list