diff --git a/.gitignore b/.gitignore index 7d721f2..0647576 100644 --- a/.gitignore +++ b/.gitignore @@ -56,6 +56,10 @@ src/telepathy-matrix.8 core cscope.out tags +GPATH +GRTAGS +GSYMS +GTAGS tests/test-ctcp-kill-blingbling tests/test-ctcp-tokenize diff --git a/src/Makefile.am b/src/Makefile.am index d1196ce..379b756 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -10,6 +10,10 @@ libmatrix_convenience_la_SOURCES = \ matrix-protocol.h \ matrix-debug.c \ matrix-debug.h \ + matrix-handles.c \ + matrix-handles.h \ + matrix-connection.c \ + matrix-connection.h \ $(NULL) nodist_libmatrix_convenience_la_SOURCES = \ diff --git a/src/matrix-connection.c b/src/matrix-connection.c new file mode 100644 index 0000000..495fd0e --- /dev/null +++ b/src/matrix-connection.c @@ -0,0 +1,415 @@ +/* + * This file is part of telepathy-matrix. + * + * telepathy-matrix is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * telepathy-matrix 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with telepathy-matrix. If not, see + * . + */ + +#include "matrix-connection.h" + +static void _aliasing_iface_init(gpointer, gpointer); + +struct _MatrixConnectionPrivate { + gchar *matrix_id; + gchar *homeserver; + gchar *password; + GHashTable *aliases; + gboolean dispose_has_run; + TpSimplePasswordManager *password_manager; +}; + +enum { + PROP_0, + PROP_MATRIX_ID, + PROP_HOMESERVER, + PROP_PASSWORD, + PROP_COUNT +}; + +G_DEFINE_TYPE_WITH_CODE( + MatrixConnection, matrix_connection, TP_TYPE_BASE_CONNECTION, + G_IMPLEMENT_INTERFACE( + TP_TYPE_SVC_CONNECTION_INTERFACE_ALIASING, + _aliasing_iface_init); +/* + G_IMPLEMENT_INTERFACE( + TP_TYPE_SVC_CONNECTION_INTERFACE_CONTACT_INFO, + matrix_contact_info_iface_init); + G_IMPLEMENT_INTERFACE( + MATRIX_TYPE_SVC_CONNECTION_INTERFACE_RENAMING, + _renaming_interface_init); +*/ + G_IMPLEMENT_INTERFACE( + TP_TYPE_SVC_CONNECTION_INTERFACE_CONTACTS, + tp_contacts_mixin_iface_init); + ); + +static void +matrix_connection_set_property(GObject *obj, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + MatrixConnection *connection = MATRIX_CONNECTION(obj); + MatrixConnectionPrivate *priv = connection->priv; + + switch (prop_id) { + case PROP_MATRIX_ID: + g_free(priv->matrix_id); + priv->matrix_id = g_value_dup_string(value); + + break; + + case PROP_HOMESERVER: + g_free(priv->homeserver); + priv->homeserver = g_value_dup_string(value); + + break; + + case PROP_PASSWORD: + g_free(priv->password); + priv->password = g_value_dup_string(value); + + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, pspec); + + break; + } +} + +static void +matrix_connection_get_property(GObject *obj, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + MatrixConnection *connection = MATRIX_CONNECTION(obj); + MatrixConnectionPrivate *priv = connection->priv; + + switch (prop_id) { + case PROP_MATRIX_ID: + g_value_set_string(value, priv->matrix_id); + + break; + + case PROP_HOMESERVER: + g_value_set_string(value, priv->homeserver); + + break; + + case PROP_PASSWORD: + g_value_set_string(value, priv->password); + + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, pspec); + + break; + } +} + +static void +matrix_connection_get_alias_flags(TpSvcConnectionInterfaceAliasing *iface, + DBusGMethodInvocation *context) +{ + tp_svc_connection_interface_aliasing_return_from_get_alias_flags(context, 0); +} + +static const gchar * +gimme_an_alias(MatrixConnection *connection, + TpHandleRepoIface *repo, + TpHandle handle) +{ + const gchar *alias = g_hash_table_lookup(connection->priv->aliases, + GUINT_TO_POINTER(handle)); + + if (alias != NULL) { + return alias; + } else { + return tp_handle_inspect(repo, handle); + } +} + +static void +matrix_connection_get_aliases(TpSvcConnectionInterfaceAliasing *iface, + const GArray *handles, + DBusGMethodInvocation *context) +{ + MatrixConnection *self = MATRIX_CONNECTION(iface); + TpHandleRepoIface *repo = tp_base_connection_get_handles( + TP_BASE_CONNECTION(iface), + TP_HANDLE_TYPE_CONTACT); + GError *err = NULL; + GHashTable *aliases; + + if (!tp_handles_are_valid(repo, handles, FALSE, &err)) { + dbus_g_method_return_error(context, err); + g_error_free(err); + + return; + } + + aliases = g_hash_table_new(NULL, NULL); + + for (guint i = 0; i < handles->len; i++) { + TpHandle handle = g_array_index(handles, TpHandle, i); + + g_hash_table_insert(aliases, + GUINT_TO_POINTER(handle), + (gpointer)gimme_an_alias(self, repo, handle)); + } + + tp_svc_connection_interface_aliasing_return_from_get_aliases(context, aliases); + g_hash_table_unref(aliases); +} + +static void +matrix_connection_request_aliases(TpSvcConnectionInterfaceAliasing *iface, + const GArray *handles, + DBusGMethodInvocation *context) +{ + MatrixConnection *connection = MATRIX_CONNECTION(iface); + TpHandleRepoIface *repo = tp_base_connection_get_handles( + TP_BASE_CONNECTION(iface), TP_HANDLE_TYPE_CONTACT); + GError *err = NULL; + const gchar **aliases; + + if (!tp_handles_are_valid(repo, handles, FALSE, &err)) { + dbus_g_method_return_error(context, err); + g_error_free(err); + + return; + } + + aliases = g_new0(const gchar *, handles->len + 1); + + for (guint i = 0; i < handles->len; i++) { + TpHandle handle = g_array_index(handles, TpHandle, i); + + aliases[i] = gimme_an_alias(connection, repo, handle); + } + + tp_svc_connection_interface_aliasing_return_from_request_aliases(context, aliases); + g_free(aliases); +} + +static void +_aliasing_iface_init(gpointer g_iface, gpointer iface_data) +{ + TpSvcConnectionInterfaceAliasingClass *klass = (TpSvcConnectionInterfaceAliasingClass *)g_iface; + +#define IMPLEMENT(x) tp_svc_connection_interface_aliasing_implement_##x(klass, matrix_connection_##x) + IMPLEMENT(get_alias_flags); + IMPLEMENT(get_aliases); + IMPLEMENT(request_aliases); +#undef IMPLEMENT +} + +static void +matrix_connection_finalize(GObject *gobject) +{ + g_signal_handlers_destroy(gobject); + G_OBJECT_CLASS(matrix_connection_parent_class)->finalize(gobject); +} + +static void +_aliasing_fill_contact_attributes(GObject *obj, + const GArray *contacts, + GHashTable *attributes_hash) +{ + MatrixConnection *connection = MATRIX_CONNECTION(obj); + TpHandleRepoIface *repo = tp_base_connection_get_handles( + TP_BASE_CONNECTION(connection), + TP_HANDLE_TYPE_CONTACT); + + for (guint i = 0; i < contacts->len; i++) { + TpHandle handle = g_array_index(contacts, TpHandle, i); + const gchar *alias = gimme_an_alias(connection, repo, handle); + + g_assert(alias != NULL); + + tp_contacts_mixin_set_contact_attribute( + attributes_hash, + handle, + TP_IFACE_CONNECTION_INTERFACE_ALIASING"/alias", + tp_g_value_slice_new_string(alias)); + } +} + +static void +matrix_connection_constructed(GObject *obj) +{ + //MatrixConnection *connection = MATRIX_CONNECTION(obj); + + //matrix_contact_info_init(connection); + tp_contacts_mixin_add_contact_attributes_iface(obj, TP_IFACE_CONNECTION_INTERFACE_ALIASING, _aliasing_fill_contact_attributes); +} + +static void +matrix_connection_dispose(GObject *obj) +{ + MatrixConnection *connection = MATRIX_CONNECTION(obj); + MatrixConnectionPrivate *priv = connection->priv; + + if (priv->dispose_has_run) { + return; + } + + priv->dispose_has_run = TRUE; + + tp_clear_pointer(&priv->aliases, g_hash_table_unref); + + if (G_OBJECT_CLASS(matrix_connection_parent_class)->dispose) { + G_OBJECT_CLASS(matrix_connection_parent_class)->dispose(obj); + } +} + +static void +_iface_create_handle_repos(TpBaseConnection *connection, + TpHandleRepoIface **repos) +{ + for (int i = 0; i < NUM_TP_HANDLE_TYPES; i++) { + repos[i] = NULL; + } + +// TODO matrix_handle_repos_init(repos); +} + +static gchar * +_iface_get_unique_connection_name(TpBaseConnection *base) +{ + MatrixConnection *connection = MATRIX_CONNECTION(base); + MatrixConnectionPrivate *priv = connection->priv; + + return g_strdup_printf( + "@%s:%s%p", + priv->matrix_id, priv->homeserver, connection); +} + +static GPtrArray * +_iface_create_channel_managers(TpBaseConnection *base) +{ + MatrixConnection *connection = MATRIX_CONNECTION(base); + MatrixConnectionPrivate *priv = connection->priv; + GPtrArray *managers = g_ptr_array_sized_new(1); + //GObject *manager; + + /* + manager = g_object_new(MATRIX_TYPE_IM_MANAGER, + "connection", connection, + NULL); + g_ptr_array_add(managers, manager); + + manager = g_object_new(MATRIX_TYPE_IM_MUC_MANAGER, + "connection", connection, + NULL); + */ + + priv->password_manager = tp_simple_password_manager_new(base); + g_ptr_array_add(managers, priv->password_manager); + + /* + manager = g_object_new(MATRIX_TYPE_ROOMLIST_MANAGER, + "connection", connection, + NULL); + g_ptr_array_add(managers, manager); + + priv->tls_manager = g_object_new(IDLE_TYPE_SERVER_TLS_MANAGER, + "connection", connection, + NULL); + g_ptr_array_add(managers, manager); + */ + + return managers; +} + +static void +_iface_disconnected(TpBaseConnection *base) +{} + +static void +matrix_connection_class_init(MatrixConnectionClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS(klass); + TpBaseConnectionClass *parent_class = TP_BASE_CONNECTION_CLASS(klass); + GParamSpec *param_spec; + + g_type_class_add_private(klass, sizeof(MatrixConnectionPrivate)); + + gobject_class->constructed = matrix_connection_constructed; + gobject_class->set_property = matrix_connection_set_property; + gobject_class->get_property = matrix_connection_get_property; + gobject_class->dispose = matrix_connection_dispose; + gobject_class->finalize = matrix_connection_finalize; + + parent_class->create_handle_repos = _iface_create_handle_repos; + parent_class->get_unique_connection_name = _iface_get_unique_connection_name; + parent_class->create_channel_factories = NULL; + parent_class->create_channel_managers = _iface_create_channel_managers; + parent_class->connecting = NULL; + parent_class->connected = NULL; + parent_class->disconnected = _iface_disconnected; + /* + parent_class->shut_down = _iface_shut_down; + parent_class->start_connecting = _iface_start_connecting; + parent_class->get_interfaces_always_present = get_interfaces_always_present; + */ + + param_spec = g_param_spec_string( + "matrix-id", + "Matrix ID", + "Your registered Matrix ID", + NULL, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property(gobject_class, PROP_MATRIX_ID, param_spec); + + param_spec = g_param_spec_string( + "homeserver", + "Homeserver", + "The home server to connect to", + NULL, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property(gobject_class, PROP_HOMESERVER, param_spec); + + param_spec = g_param_spec_string( + "password", + "Password", + "Password to authenticate to the server with", + NULL, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property(gobject_class, PROP_PASSWORD, param_spec); + + tp_contacts_mixin_class_init(gobject_class, G_STRUCT_OFFSET(MatrixConnectionClass, contacts)); + //matrix_contact_info_class_init(klass); +} + +static void +matrix_connection_init(MatrixConnection *connection) +{ + MatrixConnectionPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE( + connection, + MATRIX_TYPE_CONNECTION, MatrixConnectionPrivate); + + connection->priv = priv; + priv->aliases = g_hash_table_new_full(NULL, NULL, NULL, g_free); + + tp_contacts_mixin_init( + G_OBJECT(connection), + G_STRUCT_OFFSET(MatrixConnection, contacts)); + tp_base_connection_register_with_contacts_mixin( + (TpBaseConnection *)connection); +} diff --git a/src/matrix-connection.h b/src/matrix-connection.h new file mode 100644 index 0000000..4338c01 --- /dev/null +++ b/src/matrix-connection.h @@ -0,0 +1,38 @@ +#ifndef __MATRIX_CONNECTION_H__ +#define __MATRIX_CONNECTION_H__ + +#include +#include + +G_BEGIN_DECLS + +#define MATRIX_TYPE_CONNECTION (matrix_connection_get_type()) +#define MATRIX_CONNECTION(o) (G_TYPE_CHECK_INSTANCE_CAST((o), MATRIX_TYPE_CONNECTION, MatrixConnection)) +#define MATRIX_CONNECTION_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), MATRIX_TYPE_CONNECTION, MatrixConnectionClass)) +#define MATRIX_IS_CONNECTION(o) (G_TYPE_CHECK_INSTANCE_TYPE((o), MATRIX_TYPE_CONNECTION)) +#define MATRIX_IS_CONNECTION_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE((k), MATRIX_TYPE_CONNECTION)) +#define MATRIX_CONNECTION_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS((o), MATRIX_TYPE_CONNECTION, MatrixConnectionClass)) + +typedef struct _MatrixConnection MatrixConnection; +typedef struct _MatrixConnectionClass MatrixConnectionClass; +typedef struct _MatrixConnectionPrivate MatrixConnectionPrivate; + +struct _MatrixConnection { + /* Parent instance structure */ + TpBaseConnection parent_instance; + TpContactsMixin contacts; + + /* Instance members */ + MatrixConnectionPrivate *priv; +}; + +struct _MatrixConnectionClass { + TpBaseConnectionClass parent_class; + TpContactsMixinClass contacts; +}; + +GType matrix_connection_get_type(void) G_GNUC_CONST; + +G_END_DECLS + +#endif /* __MATRIX_CONNECTION_H__ */ diff --git a/src/matrix-debug.h b/src/matrix-debug.h index c94abab..594140f 100644 --- a/src/matrix-debug.h +++ b/src/matrix-debug.h @@ -16,8 +16,8 @@ * . */ -#ifndef __MATRIX_DEBUG_H_ -#define __MATRIX_DEBUG_H_ +#ifndef __MATRIX_DEBUG_H__ +#define __MATRIX_DEBUG_H__ #include @@ -38,12 +38,10 @@ void matrix_debug(MatrixDebugFlags flag, const gchar *format, ...) G_GNUC_PRINTF void matrix_debug_free (void); -#endif +#endif /* __MATRIX_DEBUG_H__ */ #ifdef MATRIX_DEBUG_FLAG - -#undef MATRIX_DEBUG -#define MATRIX_DEBUG(format, ...) \ +# undef MATRIX_DEBUG +# define MATRIX_DEBUG(format, ...) \ matrix_debug(MATRIX_DEBUG_FLAG, "%s: " format, G_STRFUNC, ##__VA_ARGS__) - -#endif +#endif /* MATRIX_DEBUG_FLAG */ diff --git a/src/matrix-handles.c b/src/matrix-handles.c new file mode 100644 index 0000000..f0b688c --- /dev/null +++ b/src/matrix-handles.c @@ -0,0 +1,42 @@ +/* + * This file is part of telepathy-matrix. + * + * telepathy-matrix is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * telepathy-matrix 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with telepathy-matrix. If not, see + * . + */ + +#include "matrix-handles.h" + +#define MATRIX_DEBUG_FLAG MATRIX_DEBUG_PARSER +#include "matrix-debug.h" + +gboolean +matrix_id_is_valid(const gchar *matrix_id, gboolean strict_mode) +{ + MATRIX_DEBUG( + "Validating Matrix ID '%s' with strict mode %d", + matrix_id, strict_mode); + + if (!matrix_id || *matrix_id == '\0') { + return FALSE; + } + + if (!g_utf8_validate(matrix_id, -1, NULL)) { + return FALSE; + } + + /* TODO: Do the actual validation */ + + return TRUE; +} diff --git a/src/matrix-handles.h b/src/matrix-handles.h new file mode 100644 index 0000000..9f94b34 --- /dev/null +++ b/src/matrix-handles.h @@ -0,0 +1,26 @@ +/* + * This file is part of telepathy-matrix. + * + * telepathy-matrix is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * telepathy-matrix 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with telepathy-matrix. If not, see + * . + */ + +#ifndef __MATRIX_HANDLES_H__ +#define __MATRIX_HANDLES_H__ + +#include + +gboolean matrix_id_is_valid(const gchar *matrix_id, gboolean strict_mode); + +#endif /* __MATRIX_HANDLES_H__ */ diff --git a/src/matrix-protocol.c b/src/matrix-protocol.c index 533c5f1..7337c0e 100644 --- a/src/matrix-protocol.c +++ b/src/matrix-protocol.c @@ -17,24 +17,86 @@ */ #include "matrix-protocol.h" +#include "matrix-handles.h" +#include "matrix-connection.h" + +#include #define PROTOCOL_NAME "matrix" +static gboolean +filter_account(const TpCMParamSpec *paramspec, GValue *value, GError **err) +{ + const gchar *matrixid = g_value_get_string(value); + + g_assert(matrixid); + g_assert(G_VALUE_HOLDS_STRING(value)); + + if (!matrix_id_is_valid(matrixid, TRUE)) { + g_set_error(err, + TP_ERROR, TP_ERROR_INVALID_HANDLE, + "Invalid account name '%s'", matrixid); + + return FALSE; + } + + return TRUE; +} + +static const TpCMParamSpec matrix_params[] = { + { + "account", + DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, + TP_CONN_MGR_PARAM_FLAG_REQUIRED, + NULL, 0, filter_account + }, + { + "homeserver", + DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, + TP_CONN_MGR_PARAM_FLAG_REQUIRED + }, + { + "password", + DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, + TP_CONN_MGR_PARAM_FLAG_SECRET + }, + {NULL, NULL, 0, 0, NULL, 0} +}; + G_DEFINE_TYPE(MatrixProtocol, matrix_protocol, TP_TYPE_BASE_PROTOCOL); -static void -matrix_protocol_finalize(GObject *gobject) +static const TpCMParamSpec * +get_parameters(TpBaseProtocol *self G_GNUC_UNUSED) { - g_signal_handlers_destroy(gobject); - G_OBJECT_CLASS(matrix_protocol_parent_class)->finalize(gobject); + return matrix_params; +} + +static TpBaseConnection * +new_connection(TpBaseProtocol *protocol G_GNUC_UNUSED, + GHashTable *params, + GError **err G_GNUC_UNUSED) +{ + return g_object_new(MATRIX_TYPE_CONNECTION, + "homeserver", tp_asv_get_string(params, "homeserver"), + "matrix_id", tp_asv_get_string(params, "account"), + "password", tp_asv_get_string(params, "password"), + NULL); } static void matrix_protocol_class_init(MatrixProtocolClass *klass) { - GObjectClass *gobject_class = G_OBJECT_CLASS(klass); + TpBaseProtocolClass *base_class = (TpBaseProtocolClass *)klass; - gobject_class->finalize = matrix_protocol_finalize; + base_class->get_parameters = get_parameters; + base_class->new_connection = new_connection; + /* + base_class->normalize_contact = normalize_contact; + base_class->identify_account = identify_account; + base_class->get_interfaces_array = get_interfaces_array; + base_class->get_connection_details = get_connection_details; + base_class->dup_authentication_types = dup_authentication_types; + */ } static void