From 7b37f3e5a92afe74a57547ff1b703f407a982bc7 Mon Sep 17 00:00:00 2001 From: Debarshi Ray Date: Sun, 27 Feb 2011 18:58:18 +0200 Subject: [PATCH 1/2] Implement ContactInfo GetContactInfo, RefreshContactInfo and SetContactInfo are no-ops. RequestContactInfo returns the real name, away message and idle time. Known problems: + ERR_NOSUCHSERVER is not handled. + RPL_TRYAGAIN is not handled. Fixes: https://bugs.freedesktop.org/34796 --- src/Makefile.am | 2 + src/idle-connection.c | 11 ++- src/idle-connection.h | 1 + src/idle-contact-info.c | 338 +++++++++++++++++++++++++++++++++++++++++++++++ src/idle-contact-info.h | 38 ++++++ 5 files changed, 388 insertions(+), 2 deletions(-) create mode 100644 src/idle-contact-info.c create mode 100644 src/idle-contact-info.h diff --git a/src/Makefile.am b/src/Makefile.am index e589406..712f5da 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -12,6 +12,8 @@ libidle_convenience_la_SOURCES = \ idle-connection.h \ idle-connection-manager.c \ idle-connection-manager.h \ + idle-contact-info.c \ + idle-contact-info.h \ idle-ctcp.c \ idle-ctcp.h \ idle-debug.c \ diff --git a/src/idle-connection.c b/src/idle-connection.c index 827f315..58b6087 100644 --- a/src/idle-connection.c +++ b/src/idle-connection.c @@ -36,6 +36,7 @@ #include #define IDLE_DEBUG_FLAG IDLE_DEBUG_CONNECTION +#include "idle-contact-info.h" #include "idle-ctcp.h" #include "idle-debug.h" #include "idle-handles.h" @@ -75,6 +76,7 @@ static void _renaming_iface_init(gpointer, gpointer); G_DEFINE_TYPE_WITH_CODE(IdleConnection, idle_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, idle_contact_info_iface_init); G_IMPLEMENT_INTERFACE(IDLE_TYPE_SVC_CONNECTION_INTERFACE_RENAMING, _renaming_iface_init)); typedef struct _IdleOutputPendingMsg IdleOutputPendingMsg; @@ -218,6 +220,8 @@ static GObject *idle_connection_constructor(GType type, guint n_params, GObjectC self->parser = g_object_new(IDLE_TYPE_PARSER, "connection", self, NULL); + idle_contact_info_init(self); + return (GObject *) self; } @@ -361,6 +365,8 @@ static void idle_connection_finalize (GObject *object) { IdleConnectionPrivate *priv = IDLE_CONNECTION_GET_PRIVATE(object); IdleOutputPendingMsg *msg; + idle_contact_info_finalize(object); + g_free(priv->nickname); g_free(priv->server); g_free(priv->password); @@ -380,6 +386,7 @@ static void idle_connection_finalize (GObject *object) { static const gchar * interfaces_always_present[] = { TP_IFACE_CONNECTION_INTERFACE_ALIASING, + TP_IFACE_CONNECTION_INTERFACE_CONTACT_INFO, IDLE_IFACE_CONNECTION_INTERFACE_RENAMING, TP_IFACE_CONNECTION_INTERFACE_REQUESTS, NULL}; @@ -442,6 +449,8 @@ static void idle_connection_class_init(IdleConnectionClass *klass) { param_spec = g_param_spec_boolean("password-prompt", "Password prompt", "Whether the connection should pop up a SASL channel if no password is given", FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT); g_object_class_install_property(object_class, PROP_PASSWORD_PROMPT, param_spec); + + idle_contact_info_class_init(klass); } static GPtrArray *_iface_create_channel_managers(TpBaseConnection *self) { @@ -936,8 +945,6 @@ _whois_user_handler(IdleParser *parser, IdleParserMessageCode code, GValueArray const char* host = g_value_get_string(g_value_array_get_nth(args, 2)); priv->relay_prefix = g_strdup_printf("%s!%s@%s", priv->nickname, user, host); IDLE_DEBUG("user host prefix = %s", priv->relay_prefix); - - return IDLE_PARSER_HANDLER_RESULT_HANDLED; } return IDLE_PARSER_HANDLER_RESULT_NOT_HANDLED; } diff --git a/src/idle-connection.h b/src/idle-connection.h index 3db763d..5d0d510 100644 --- a/src/idle-connection.h +++ b/src/idle-connection.h @@ -41,6 +41,7 @@ struct _IdleConnectionClass { struct _IdleConnection { TpBaseConnection parent; IdleParser *parser; + GQueue *contact_info_requests; }; GType idle_connection_get_type(void); diff --git a/src/idle-contact-info.c b/src/idle-contact-info.c new file mode 100644 index 0000000..f51bab4 --- /dev/null +++ b/src/idle-contact-info.c @@ -0,0 +1,338 @@ +/* + * This file is part of telepathy-idle + * + * Copyright (C) 2011 Debarshi Ray + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * This library 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 this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "idle-contact-info.h" + +#include +#include +#include +#include + +#define IDLE_DEBUG_FLAG IDLE_DEBUG_CONNECTION +#include "idle-debug.h" +#include "idle-parser.h" + +typedef struct _ContactInfoRequest ContactInfoRequest; + +struct _ContactInfoRequest { + guint handle; + const gchar *nick; + gboolean is_away; + GPtrArray *contact_info; + DBusGMethodInvocation *context; +}; + +/* + * _insert_contact_field: + * @contact_info: an array of Contact_Info_Field structures + * @field_name: a vCard field name in any case combination + * @field_params: a list of vCard type-parameters, typically of the form + * type=xxx; must be in lower-case if case-insensitive + * @field_values: for unstructured fields, an array containing one element; + * for structured fields, the elements of the field in order + */ +static void _insert_contact_field(GPtrArray *contact_info, const gchar *field_name, const gchar * const *field_params, const gchar * const *field_values) { + gchar *field_name_down = g_ascii_strdown (field_name, -1); + + g_ptr_array_add (contact_info, tp_value_array_build (3, + G_TYPE_STRING, field_name_down, + G_TYPE_STRV, field_params, + G_TYPE_STRV, field_values, + G_TYPE_INVALID)); + + g_free (field_name_down); +} + +static gboolean _is_valid_response(IdleConnection *conn, GValueArray *args) { + ContactInfoRequest *request; + TpHandle handle = g_value_get_uint(g_value_array_get_nth(args, 0)); + + if (g_queue_is_empty(conn->contact_info_requests)) + return FALSE; + + request = g_queue_peek_head(conn->contact_info_requests); + if (request->handle != handle) + return FALSE; + + return TRUE; +} + +static void idle_connection_get_contact_info(TpSvcConnectionInterfaceContactInfo *iface, const GArray *contacts, DBusGMethodInvocation *context) { + IdleConnection *self = IDLE_CONNECTION(iface); + TpBaseConnection *base = TP_BASE_CONNECTION(self); + TpHandleRepoIface *contact_handles = tp_base_connection_get_handles(base, TP_HANDLE_TYPE_CONTACT); + GHashTable *ret; + GError *error = NULL; + + TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED(TP_BASE_CONNECTION(iface), context); + + if (!tp_handles_are_valid(contact_handles, contacts, FALSE, &error)) { + dbus_g_method_return_error(context, error); + g_error_free(error); + return; + } + + ret = dbus_g_type_specialized_construct(TP_HASH_TYPE_CONTACT_INFO_MAP); + tp_svc_connection_interface_contact_info_return_from_get_contact_info(context, ret); + g_boxed_free(TP_HASH_TYPE_CONTACT_INFO_MAP, ret); +} + +static void idle_connection_refresh_contact_info(TpSvcConnectionInterfaceContactInfo *iface, const GArray *contacts, DBusGMethodInvocation *context) { + IdleConnection *self = IDLE_CONNECTION(iface); + TpBaseConnection *base = TP_BASE_CONNECTION(self); + TpHandleRepoIface *contact_handles = tp_base_connection_get_handles(base, TP_HANDLE_TYPE_CONTACT); + GError *error = NULL; + + TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED(TP_BASE_CONNECTION(iface), context); + + if (!tp_handles_are_valid(contact_handles, contacts, FALSE, &error)) { + dbus_g_method_return_error(context, error); + g_error_free(error); + return; + } + + tp_svc_connection_interface_contact_info_return_from_refresh_contact_info(context); +} + +static void _send_request_contact_info(IdleConnection *conn, ContactInfoRequest *request) { + gchar cmd[IRC_MSG_MAXLEN + 1]; + + g_snprintf(cmd, IRC_MSG_MAXLEN + 1, "WHOIS %s %s", request->nick, request->nick); + idle_connection_send(conn, cmd); +} + +static void _dequeue_request_contact_info(IdleConnection *conn) { + ContactInfoRequest *request = g_queue_pop_head(conn->contact_info_requests); + + if (request->contact_info != NULL) + g_boxed_free(TP_ARRAY_TYPE_CONTACT_INFO_FIELD_LIST, request->contact_info); + + g_free(request); + + if (g_queue_is_empty(conn->contact_info_requests)) + return; + + request = g_queue_peek_head(conn->contact_info_requests); + _send_request_contact_info(conn, request); +} + +static void _queue_request_contact_info(IdleConnection *conn, guint handle, const gchar *nick, DBusGMethodInvocation *context) { + ContactInfoRequest *request; + + request = g_new0(ContactInfoRequest, 1); + request->handle = handle; + request->nick = nick; + request->is_away = FALSE; + request->contact_info = NULL; + request->context = context; + + if (g_queue_is_empty(conn->contact_info_requests)) + _send_request_contact_info(conn, request); + + g_queue_push_tail(conn->contact_info_requests, request); +} + +static void _return_from_request_contact_info(IdleConnection *conn) { + ContactInfoRequest *request = g_queue_peek_head(conn->contact_info_requests); + + tp_svc_connection_interface_contact_info_return_from_request_contact_info(request->context, request->contact_info); + tp_svc_connection_interface_contact_info_emit_contact_info_changed(conn, request->handle, request->contact_info); + _dequeue_request_contact_info(conn); +} + +static void idle_connection_request_contact_info(TpSvcConnectionInterfaceContactInfo *iface, guint contact, DBusGMethodInvocation *context) { + IdleConnection *self = IDLE_CONNECTION(iface); + TpBaseConnection *base = TP_BASE_CONNECTION(self); + TpHandleRepoIface *contact_handles = tp_base_connection_get_handles(base, TP_HANDLE_TYPE_CONTACT); + const gchar *nick; + GError *error = NULL; + + TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED(base, context); + + if (!tp_handle_is_valid(contact_handles, contact, &error)) { + dbus_g_method_return_error(context, error); + g_error_free(error); + return; + } + + nick = tp_handle_inspect(contact_handles, contact); + + IDLE_DEBUG ("Queued contact info request for handle: %u (%s)", contact, nick); + _queue_request_contact_info(self, contact, nick, context); +} + +static void idle_connection_set_contact_info(TpSvcConnectionInterfaceContactInfo *iface, const GPtrArray *contact_info, DBusGMethodInvocation *context) { + TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED(TP_BASE_CONNECTION(iface), context); + tp_svc_connection_interface_contact_info_return_from_set_contact_info(context); +} + +static IdleParserHandlerResult _away_handler(IdleParser *parser, IdleParserMessageCode code, GValueArray *args, gpointer user_data) { + IdleConnection *conn = IDLE_CONNECTION(user_data); + ContactInfoRequest *request; + const gchar *msg; + const gchar *field_values[2] = {NULL, NULL}; + + if (!_is_valid_response(conn, args)) + return IDLE_PARSER_HANDLER_RESULT_NOT_HANDLED; + + request = g_queue_peek_head(conn->contact_info_requests); + + if (request->contact_info == NULL) + request->contact_info = dbus_g_type_specialized_construct(TP_ARRAY_TYPE_CONTACT_INFO_FIELD_LIST); + + field_values[0] = g_strdup_printf("%d", TP_CONNECTION_PRESENCE_TYPE_AWAY); + _insert_contact_field(request->contact_info, "x-presence-type", NULL, field_values); + g_free((gpointer) field_values[0]); + + field_values[0] = "away"; + _insert_contact_field(request->contact_info, "x-presence-status-identifier", NULL, field_values); + + msg = g_value_get_string(g_value_array_get_nth(args, 1)); + field_values[0] = msg; + _insert_contact_field(request->contact_info, "x-presence-status-message", NULL, field_values); + + request->is_away = TRUE; + + return IDLE_PARSER_HANDLER_RESULT_NOT_HANDLED; +} + +static IdleParserHandlerResult _end_of_whois_handler(IdleParser *parser, IdleParserMessageCode code, GValueArray *args, gpointer user_data) { + IdleConnection *conn = IDLE_CONNECTION(user_data); + ContactInfoRequest *request; + const gchar *field_values[2] = {NULL, NULL}; + + if (!_is_valid_response(conn, args)) + return IDLE_PARSER_HANDLER_RESULT_NOT_HANDLED; + + request = g_queue_peek_head(conn->contact_info_requests); + + if (request->contact_info == NULL) + request->contact_info = dbus_g_type_specialized_construct(TP_ARRAY_TYPE_CONTACT_INFO_FIELD_LIST); + + if (request->is_away == FALSE) { + field_values[0] = g_strdup_printf("%d", TP_CONNECTION_PRESENCE_TYPE_AVAILABLE); + _insert_contact_field(request->contact_info, "x-presence-type", NULL, field_values); + g_free((gpointer) field_values[0]); + + field_values[0] = "available"; + _insert_contact_field(request->contact_info, "x-presence-status-identifier", NULL, field_values); + + field_values[0] = ""; + _insert_contact_field(request->contact_info, "x-presence-status-message", NULL, field_values); + } + + _return_from_request_contact_info(conn); + return IDLE_PARSER_HANDLER_RESULT_NOT_HANDLED; +} + +static IdleParserHandlerResult _whois_idle_handler(IdleParser *parser, IdleParserMessageCode code, GValueArray *args, gpointer user_data) { + IdleConnection *conn = IDLE_CONNECTION(user_data); + ContactInfoRequest *request; + guint sec; + const gchar *field_values[2] = {NULL, NULL}; + + if (!_is_valid_response(conn, args)) + return IDLE_PARSER_HANDLER_RESULT_NOT_HANDLED; + + request = g_queue_peek_head(conn->contact_info_requests); + + if (request->contact_info == NULL) + request->contact_info = dbus_g_type_specialized_construct(TP_ARRAY_TYPE_CONTACT_INFO_FIELD_LIST); + + sec = g_value_get_uint(g_value_array_get_nth(args, 1)); + + field_values[0] = g_strdup_printf("%u", sec); + _insert_contact_field(request->contact_info, "x-idle-time", NULL, field_values); + g_free((gpointer) field_values[0]); + + return IDLE_PARSER_HANDLER_RESULT_NOT_HANDLED; +} + +static IdleParserHandlerResult _whois_user_handler(IdleParser *parser, IdleParserMessageCode code, GValueArray *args, gpointer user_data) { + IdleConnection *conn = IDLE_CONNECTION(user_data); + ContactInfoRequest *request; + const gchar *name; + const gchar *field_values[2] = {NULL, NULL}; + + if (!_is_valid_response(conn, args)) + return IDLE_PARSER_HANDLER_RESULT_NOT_HANDLED; + + request = g_queue_peek_head(conn->contact_info_requests); + + if (request->contact_info == NULL) + request->contact_info = dbus_g_type_specialized_construct(TP_ARRAY_TYPE_CONTACT_INFO_FIELD_LIST); + + name = g_value_get_string(g_value_array_get_nth(args, 3)); + field_values[0] = name; + _insert_contact_field(request->contact_info, "fn", NULL, field_values); + + return IDLE_PARSER_HANDLER_RESULT_NOT_HANDLED; +} + +static void idle_contact_info_properties_getter(GObject *object, GQuark interface, GQuark name, GValue *value, gpointer getter_data) { + GQuark q_supported_fields = g_quark_from_static_string("SupportedFields"); + + if (name == q_supported_fields) + g_value_set_static_boxed(value, getter_data); + else + g_value_set_uint(value, GPOINTER_TO_UINT(getter_data)); +} + +void idle_contact_info_finalize (GObject *object) { + IdleConnection *conn = IDLE_CONNECTION(object); + + g_queue_foreach(conn->contact_info_requests, (GFunc) g_free, NULL); + g_queue_free(conn->contact_info_requests); +} + +void idle_contact_info_class_init (IdleConnectionClass *klass) { + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + static TpDBusPropertiesMixinPropImpl props[] = { + {"ContactInfoFlags", GUINT_TO_POINTER(0), NULL}, + {"SupportedFields", NULL, NULL}, + {NULL} + }; + + tp_dbus_properties_mixin_implement_interface(object_class, + TP_IFACE_QUARK_CONNECTION_INTERFACE_CONTACT_INFO, + idle_contact_info_properties_getter, + NULL, + props); +} + +void idle_contact_info_init (IdleConnection *conn) { + conn->contact_info_requests = g_queue_new(); + idle_parser_add_handler(conn->parser, IDLE_PARSER_NUMERIC_WHOISUSER, _whois_user_handler, conn); + idle_parser_add_handler(conn->parser, IDLE_PARSER_NUMERIC_AWAY, _away_handler, conn); + idle_parser_add_handler(conn->parser, IDLE_PARSER_NUMERIC_WHOISIDLE, _whois_idle_handler, conn); + idle_parser_add_handler(conn->parser, IDLE_PARSER_NUMERIC_ENDOFWHOIS, _end_of_whois_handler, conn); +} + +void idle_contact_info_iface_init(gpointer g_iface, gpointer iface_data) { + TpSvcConnectionInterfaceContactInfoClass *klass = (TpSvcConnectionInterfaceContactInfoClass *) g_iface; + +#define IMPLEMENT(x) tp_svc_connection_interface_contact_info_implement_##x (\ + klass, idle_connection_##x) + IMPLEMENT(get_contact_info); + IMPLEMENT(refresh_contact_info); + IMPLEMENT(request_contact_info); + IMPLEMENT(set_contact_info); +#undef IMPLEMENT +} diff --git a/src/idle-contact-info.h b/src/idle-contact-info.h new file mode 100644 index 0000000..8baa198 --- /dev/null +++ b/src/idle-contact-info.h @@ -0,0 +1,38 @@ +/* + * This file is part of telepathy-idle + * + * Copyright (C) 2011 Debarshi Ray + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * This library 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 this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __IDLE_CONTACT_INFO_H__ +#define __IDLE_CONTACT_INFO_H__ + +#include +#include +#include + +#include "idle-connection.h" + +G_BEGIN_DECLS + +void idle_contact_info_finalize (GObject *object); +void idle_contact_info_class_init (IdleConnectionClass *klass); +void idle_contact_info_init (IdleConnection *conn); +void idle_contact_info_iface_init (gpointer g_iface, gpointer iface_data); + +G_END_DECLS + +#endif /* #ifndef __IDLE_CONTACT_INFO_H__ */ -- 1.7.4