diff --git a/.gitignore b/.gitignore index 1ffbdce..2007012 100644 --- a/.gitignore +++ b/.gitignore @@ -53,7 +53,6 @@ Makefile.in /src/matrix-http-client.c /src/matrix-event-presence.c /src/matrix-event-room-member.c -/src/matrix-event-state-base.c /src/matrix-event-room-message.c /src/namespace-info.vala /src/namespace-info.c diff --git a/src/Makefile.am b/src/Makefile.am index d57e990..39ad13f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -21,7 +21,6 @@ libmatrix_glib_0_0_la_VALA_SOURCES = \ matrix-client.vala \ matrix-http-api.vala \ matrix-http-client.vala \ - matrix-event-state-base.vala \ matrix-event-presence.vala \ matrix-event-room-member.vala \ matrix-event-room-message.vala \ @@ -113,6 +112,7 @@ INST_H_SRC_FILES = \ matrix-compacts.h \ matrix-event-base.h \ matrix-event-room-base.h \ + matrix-event-state-base.h \ utils.h \ matrix-profile.h \ $(NULL) @@ -137,6 +137,7 @@ libmatrix_glib_0_0_la_SOURCES = \ matrix-compacts.c \ matrix-event-base.c \ matrix-event-room-base.c \ + matrix-event-state-base.c \ matrix-profile.c \ utils.c \ matrix-enumtypes.c \ diff --git a/src/matrix-event-state-base.c b/src/matrix-event-state-base.c new file mode 100644 index 0000000..f93a074 --- /dev/null +++ b/src/matrix-event-state-base.c @@ -0,0 +1,353 @@ +/* + * This file is part of matrix-glib-sdk + * + * matrix-glib-sdk is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * matrix-glib-sdk is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with matrix-glib-sdk. If not, see + * . + */ + +#include "matrix-event-state-base.h" +#include "matrix-types.h" +#include "config.h" + +/** + * SECTION:matrix-event-state-base + * @short_description: abstract base class for state events + * @title: base class for state events + * + * #MatrixEventState is the base class for state events. State events are special room events + * with an extra `state_key` and `prev_content` event which are handled by this class. + */ + +enum { + PROP_0, + PROP_STATE_KEY, + PROP_PREV_CONTENT, + NUM_PROPS +}; + +static GParamSpec *matrix_event_state_properties[NUM_PROPS]; + +typedef struct { + JsonNode *_prev_content; + gchar *_state_key; +} MatrixEventStatePrivate; + +/** + * MatrixEventState: + * + * The only exposed field for such events is the state key. There are some events that may + * send the state key in a different field; handlers of such events can overwrite this + * property directly, but otherwise it’s descouraged. + */ +G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE(MatrixEventState, matrix_event_state, MATRIX_EVENT_TYPE_ROOM); + +static void +matrix_event_state_real_from_json(MatrixEventBase *matrix_event_base, JsonNode *json_data, GError **error) +{ + MatrixEventStatePrivate *priv; + JsonObject *root; + JsonNode *node; + GError *inner_error = NULL; + + g_return_if_fail(json_data != NULL); + + priv = matrix_event_state_get_instance_private(MATRIX_EVENT_STATE(matrix_event_base)); + root = json_node_get_object(json_data); + + if ((node = json_object_get_member(root, "state_key")) != NULL) { + g_free(priv->_state_key); + priv->_state_key = g_strdup(json_node_get_string(node)); + } else if (DEBUG) { + g_warning("state_key is not present in a State event"); + } + + if ((node = json_object_get_member(root, "prev_content")) != NULL) { + priv->_prev_content = node; + } + + MATRIX_EVENT_BASE_CLASS(matrix_event_state_parent_class)->from_json(matrix_event_base, json_data, &inner_error); + + json_node_unref(node); + json_object_unref(root); + + if (inner_error != NULL) { + g_propagate_error(error, inner_error); + } +} + +static void +matrix_event_state_real_to_json(MatrixEventBase *matrix_event_base, JsonNode *json_node, GError **error) +{ + MatrixEventState *matrix_event_state; + MatrixEventStatePrivate *priv; + JsonObject *root; + GError *inner_error = NULL; + + matrix_event_state = MATRIX_EVENT_STATE(matrix_event_base); + priv = matrix_event_state_get_instance_private(matrix_event_state); + + if (priv->_state_key == NULL) { + g_set_error(error, MATRIX_ERROR, MATRIX_ERROR_INCOMPLETE, + "Won't generate state events without state_key"); + + return; + } + + root = json_node_get_object(json_node); + + json_object_set_string_member(root, "state_key", priv->_state_key); + + if (priv->_prev_content != NULL) { + json_object_set_member(root, "prev_content", priv->_prev_content); + } + + MATRIX_EVENT_BASE_CLASS(matrix_event_state_parent_class)->to_json(MATRIX_EVENT_BASE(matrix_event_base), json_node, &inner_error); + + json_object_unref(root); + + if (inner_error != NULL) { + g_propagate_error(error, inner_error); + } +} + +/** + * matrix_event_state_get_stripped_node: + * @event: a #MatrixEventState derived object + * + * Get a stripped state event. + * + * Returns: (transfer full) (nullable): %NULL if the event is not allowed to be stripped, or + * the full JSON node otherwise + */ +JsonNode * +matrix_event_state_get_stripped_node(MatrixEventState *matrix_event_state) +{ + MatrixEventBase *matrix_event_base; + const gchar *event_type; + + g_return_val_if_fail(matrix_event_state != NULL, NULL); + + matrix_event_base = MATRIX_EVENT_BASE(matrix_event_state); + event_type = matrix_event_base_get_event_type(matrix_event_base); + + // TODO: this may be controlled by an object property instead. + if ((g_strcmp0(event_type, "m.room.join_rules") != 0) && + (g_strcmp0(event_type, "m.room.canonical_alias") != 0) && + (g_strcmp0(event_type, "m.room.avatar") != 0) && + (g_strcmp0(event_type, "m.room.name") != 0)) { + g_warning("Trying to strip down event that is not allowed to be stripped."); + + return NULL; + } + + return matrix_event_base_get_json(matrix_event_base); +} + +/** + * matrix_event_state_construct: + * @object_type: the #GType of the object to be created + * + * Returns: (transfer full): a new instance of @object_type + */ +MatrixEventState * +matrix_event_state_construct(GType object_type) +{ + return (MatrixEventState *)matrix_event_room_construct(object_type); +} + +/** + * matrix_event_state_get_state_key: + * @event: a #MatrixEventState derived object + * + * Get the state key of @event. + * + * The returned value is owned by @event and should not be freed. + * + * Returns: (transfer none) (nullable): the state key + */ +const gchar * +matrix_event_state_get_state_key(MatrixEventState *matrix_event_state) +{ + MatrixEventStatePrivate *priv = matrix_event_state_get_instance_private(matrix_event_state); + + g_return_val_if_fail(matrix_event_state != NULL, NULL); + + return priv->_state_key; +} + +/** + * matrix_event_state_set_state_key: + * @event: a #MatrixEventState derived object + * @state_key: a state key + * + * Set the state key of @event. + */ +void +matrix_event_state_set_state_key(MatrixEventState *matrix_event_state, const gchar *state_key) +{ + MatrixEventStatePrivate *priv = matrix_event_state_get_instance_private(matrix_event_state); + + g_return_if_fail(matrix_event_state != NULL); + + g_free(priv->_state_key); + priv->_state_key = g_strdup(state_key); + + g_object_notify_by_pspec((GObject *)matrix_event_state, matrix_event_state_properties[PROP_STATE_KEY]); +} + +/** + * matrix_event_state_get_prev_content: + * @event: a #MatrixEventState derived object + * + * Get the known previous content for the state represented by @event. + * + * The value returned is owned by @event and should not be freed. + * + * Returns: (transfer none) (nullable): a #JsonNode representing the last known state of @event + */ +JsonNode * +matrix_event_state_get_prev_content(MatrixEventState *matrix_event_state) +{ + MatrixEventStatePrivate *priv; + + g_return_val_if_fail(matrix_event_state != NULL, NULL); + + priv = matrix_event_state_get_instance_private(matrix_event_state); + + return priv->_prev_content; +} + +/** + * matrix_event_state_set_prev_content: + * @event: a #MatrixEventState derived object + * @prev_content: (transfer none) (nullable): the last known content of the state + * represented by @event + * + * Set the last known content of the state represented by @event. This is required to prevent + * the race condition when a client tries to overwrite a state that has been changed since then. + */ +void +matrix_event_state_set_prev_content(MatrixEventState *matrix_event_state, JsonNode *prev_content) +{ + MatrixEventStatePrivate *priv; + + g_return_if_fail(matrix_event_state != NULL); + + priv = matrix_event_state_get_instance_private(matrix_event_state); + + if (priv->_prev_content != prev_content) { + json_node_unref(priv->_prev_content); + priv->_prev_content = json_node_ref(prev_content); + + g_object_notify_by_pspec((GObject *)matrix_event_state, matrix_event_state_properties[PROP_PREV_CONTENT]); + } +} + +static void +matrix_event_state_finalize(GObject *gobject) +{ + MatrixEventState *matrix_event_state = MATRIX_EVENT_STATE(gobject); + MatrixEventStatePrivate *priv = matrix_event_state_get_instance_private(matrix_event_state); + + g_free(priv->_state_key); + json_node_unref(priv->_prev_content); + + G_OBJECT_CLASS(matrix_event_state_parent_class)->finalize(gobject); +} + +static void +matrix_event_state_get_property(GObject *gobject, guint property_id, GValue* value, GParamSpec* pspec) +{ + MatrixEventState *matrix_event_state = MATRIX_EVENT_STATE(gobject); + MatrixEventStatePrivate *priv = matrix_event_state_get_instance_private(matrix_event_state); + + switch (property_id) { + case PROP_STATE_KEY: + g_value_set_string(value, priv->_state_key); + + break; + case PROP_PREV_CONTENT: + g_value_set_boxed(value, priv->_prev_content); + + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(gobject, property_id, pspec); + + break; + } +} + +static void +matrix_event_state_set_property(GObject *gobject, guint property_id, const GValue* value, GParamSpec* pspec) +{ + MatrixEventState *matrix_event_state = MATRIX_EVENT_STATE(gobject); + + switch (property_id) { + case PROP_STATE_KEY: + matrix_event_state_set_state_key(matrix_event_state, g_value_get_string(value)); + + break; + case PROP_PREV_CONTENT: + matrix_event_state_set_prev_content(matrix_event_state, g_value_get_boxed(value)); + + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(gobject, property_id, pspec); + + break; + } +} + +static void +matrix_event_state_class_init(MatrixEventStateClass *klass) +{ + ((MatrixEventBaseClass *)klass)->from_json = matrix_event_state_real_from_json; + ((MatrixEventBaseClass *)klass)->to_json = matrix_event_state_real_to_json; + G_OBJECT_CLASS(klass)->get_property = matrix_event_state_get_property; + G_OBJECT_CLASS(klass)->set_property = matrix_event_state_set_property; + G_OBJECT_CLASS(klass)->finalize = matrix_event_state_finalize; + + /** + * MatrixEventState:state-key: + * + * The state key of the event. + */ + matrix_event_state_properties[PROP_STATE_KEY] = g_param_spec_string( + "state-key", "state-key", "state-key", + NULL, + G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE); + g_object_class_install_property(G_OBJECT_CLASS(klass), PROP_STATE_KEY, matrix_event_state_properties[PROP_STATE_KEY]); + + /** + * MatrixEventState:prev-content: + * + * The previous known content of the state + */ + matrix_event_state_properties[PROP_PREV_CONTENT] = g_param_spec_boxed( + "prev-content", "prev-content", "prev-content", + JSON_TYPE_NODE, + G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE); + g_object_class_install_property(G_OBJECT_CLASS(klass), PROP_PREV_CONTENT, matrix_event_state_properties[PROP_PREV_CONTENT]); +} + +static void +matrix_event_state_init(MatrixEventState *matrix_event_state) +{ + MatrixEventStatePrivate *priv; + + priv = matrix_event_state_get_instance_private(matrix_event_state); + + priv->_prev_content = NULL; + priv->_state_key = NULL; +} diff --git a/src/matrix-event-state-base.h b/src/matrix-event-state-base.h new file mode 100644 index 0000000..5df78fb --- /dev/null +++ b/src/matrix-event-state-base.h @@ -0,0 +1,53 @@ +/* + * This file is part of matrix-glib-sdk + * + * matrix-glib-sdk is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * matrix-glib-sdk is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with matrix-glib-sdk. If not, see + * . + */ + +#ifndef __MATRIX_GLIB_SDK_EVENT_STATE_BASE_H__ +# define __MATRIX_GLIB_SDK_EVENT_STATE_BASE_H__ + +# include +# include +# include "matrix-event-room-base.h" + +# define MATRIX_EVENT_TYPE_STATE matrix_event_state_get_type() +#define MATRIX_EVENT_STATE(o) (G_TYPE_CHECK_INSTANCE_CAST((o), MATRIX_EVENT_TYPE_STATE, MatrixEventState)) +#define MATRIX_EVENT_STATE_CLASS(c) (G_TYPE_CHECK_CLASS_CAST((c), MATRIX_EVENT_TYPE_STATE, MatrixEventStateClass)) +#define MATRIX_EVENT_IS_STATE(o) (G_TYPE_CHECK_INSTANCE_TYPE((o), MATRIX_EVENT_TYPE_STATE)) +#define MATRIX_EVENT_IS_STATE_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE((c), MATRIX_EVENT_TYPE_STATE)) +#define MATRIX_EVENT_STATE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS((o), MATRIX_EVENT_TYPE_STATE, MatrixEventStateClass)) + +typedef struct _MatrixEventStateClass MatrixEventStateClass; +typedef struct _MatrixEventState MatrixEventState; +G_DEFINE_AUTOPTR_CLEANUP_FUNC(MatrixEventState, g_object_unref) + +struct _MatrixEventStateClass { + MatrixEventRoomClass parent_class; +}; + +struct _MatrixEventState { + MatrixEventRoom parent_instance; +}; + +GType matrix_event_state_get_type(void); +JsonNode *matrix_event_state_get_stripped_node(MatrixEventState *event); +MatrixEventState *matrix_event_state_construct(GType object_type); +const gchar *matrix_event_state_get_state_key(MatrixEventState *event); +void matrix_event_state_set_state_key(MatrixEventState *event, const gchar *state_key); +JsonNode *matrix_event_state_get_prev_content(MatrixEventState *event); +void matrix_event_state_set_prev_content(MatrixEventState *event, JsonNode *prev_content); + +#endif /* __MATRIX_GLIB_SDK_EVENT_STATE_BASE_H__ */ diff --git a/src/matrix-event-state-base.vala b/src/matrix-event-state-base.vala deleted file mode 100644 index 587a66a..0000000 --- a/src/matrix-event-state-base.vala +++ /dev/null @@ -1,95 +0,0 @@ -/* - * This file is part of matrix-glib-sdk - * - * matrix-glib-sdk is free software: you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * matrix-glib-sdk is distributed in the hope that it will be - * useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with matrix-glib-sdk. If not, see - * . - */ - -public abstract class Matrix.Event.State : Matrix.Event.Room { - protected string? _state_key; - - public string? state_key { - get { - return _state_key; - } - - set { - _state_key = value; - } - - default = null; - } - public Json.Node? prev_content { get; set; default = null; } - - protected override void - from_json(Json.Node json_data) - throws Matrix.Error - { - var root = json_data.get_object(); - Json.Node? node; - - if ((node = root.get_member("state_key")) != null) { - _state_key = node.get_string(); - } else if (Config.DEBUG) { - warning("state_key is not present in a State event"); - } - - if ((node = root.get_member("prev_content")) != null) { - _prev_content = node; - } - - base.from_json(json_data); - } - - protected override void - to_json(Json.Node json_node) - throws Matrix.Error - { - if (_state_key == null) { - throw new Matrix.Error.INCOMPLETE( - "Won't generate state events without state_key"); - } - - var root = json_node.get_object(); - - root.set_string_member("state_key", state_key); - - if (_prev_content != null) { - root.set_member("prev_content", prev_content); - } - - base.to_json(json_node); - } - - /** - * Get a stripped state event. - * - * @return `null` if the event is not allowed to be stripped, or - * the full JSON node otherwise - */ - public Json.Node? - get_stripped_node() - { - if ((_event_type != "m.room.join_rules") - && (_event_type != "m.room.canonical_alias") - && (_event_type != "m.room.avatar") - && (_event_type != "m.room.name")) { - warning("Trying to strip down event that is not allowed to be stripped."); - - return null; - } - - return json; - } -} diff --git a/vapi/c-api.vapi b/vapi/c-api.vapi index 79dc644..0436354 100644 --- a/vapi/c-api.vapi +++ b/vapi/c-api.vapi @@ -507,5 +507,22 @@ namespace Matrix { protected override void to_json(Json.Node json_data) throws Matrix.Error; } + + [CCode (cheader_filename = "matrix-event-state-base.h")] + public abstract class State : Matrix.Event.Room { + protected string? _state_key; + public string? state_key { get; set; default = null; } + public Json.Node? prev_content { get; set; default = null; } + + public State(); + + protected override void from_json(Json.Node json_data) + throws Matrix.Error; + + protected override void to_json(Json.Node json_node) + throws Matrix.Error; + + public Json.Node? get_stripped_node(); + } } }