diff options
author | Mathias Hasselmann <hasselmm@gnome.org> | 2009-10-25 13:36:43 +0100 |
---|---|---|
committer | Mathias Hasselmann <hasselmm@gnome.org> | 2009-10-25 13:36:43 +0100 |
commit | bcea784a38f6bb23b51c83066eff1c5e222279ee (patch) | |
tree | 0b6fc5b489a7c31004cb4949d01e418dcdbc8988 | |
parent | 52123a8d88a88d32070d07e196c073ebbbe79c28 (diff) |
Implement presence interface for Facebook.
* src/facebook-connection.c (_presence_status_arguments,
_presence_statuses, G_DEFINE_TYPE_WITH_CODE, _get_profile,
_get_contact_alias, _get_contact_presence, _get_contact_status,
_update_friends, _aliasing_fill_contact_attributes,
_presence_get_contact_statuses, _presence_set_own_status,
_constructor,gruschler_facebook_connection_class_init):
Implement presence interface. Refactor profile utility functions.
Move GObject overrides to have better access on interface methods.
* src/facebook-connection.h (GruschlerFacebookPresenceStatus,
GruschlerFacebookConnection, GruschlerFacebookConnectionClass):
Add enumeration for Facebook's online presence states. Add presence
mixins to GruschlerFacebookConnection.
-rw-r--r-- | src/facebook-connection.c | 512 | ||||
-rw-r--r-- | src/facebook-connection.h | 18 |
2 files changed, 376 insertions, 154 deletions
diff --git a/src/facebook-connection.c b/src/facebook-connection.c index 3254668..3d9273f 100644 --- a/src/facebook-connection.c +++ b/src/facebook-connection.c @@ -32,6 +32,7 @@ #include <telepathy-glib/handle-repo-dynamic.h> #include <telepathy-glib/handle-repo-static.h> #include <telepathy-glib/interfaces.h> +#include <telepathy-glib/svc-generic.h> #include <string.h> @@ -41,18 +42,36 @@ enum { PROP_PASSWORD, }; -static const char *const _fixed_properties[] = { +static const char *const +_fixed_properties[] = { TP_IFACE_CHANNEL ".ChannelType", TP_IFACE_CHANNEL ".TargetHandleType", NULL }; -static const char *const _allowed_properties[] = { +static const char *const +_allowed_properties[] = { TP_IFACE_CHANNEL ".TargetHandle", TP_IFACE_CHANNEL ".TargetID", NULL }; +static const TpPresenceStatusOptionalArgumentSpec +_presence_status_arguments[] = { + { "message", "s", NULL, NULL }, + { NULL, NULL, NULL, NULL } +}; + +static const TpPresenceStatusSpec +_presence_statuses[] = { + { "unknown", TP_CONNECTION_PRESENCE_TYPE_UNKNOWN, FALSE, _presence_status_arguments, NULL, NULL }, + { "offline", TP_CONNECTION_PRESENCE_TYPE_OFFLINE, FALSE, _presence_status_arguments, NULL, NULL }, + { "error", TP_CONNECTION_PRESENCE_TYPE_ERROR, FALSE, _presence_status_arguments, NULL, NULL }, + { "idle", TP_CONNECTION_PRESENCE_TYPE_AWAY, FALSE, _presence_status_arguments, NULL, NULL }, + { "active", TP_CONNECTION_PRESENCE_TYPE_AVAILABLE, TRUE, _presence_status_arguments, NULL, NULL }, + { NULL, 0, FALSE, NULL, NULL, NULL } +}; + struct _GruschlerFacebookConnectionPrivate { RestProxy *facebook; SoupSession *session; @@ -82,97 +101,19 @@ G_DEFINE_TYPE_WITH_CODE (GruschlerFacebookConnection, G_IMPLEMENT_INTERFACE (TP_TYPE_CHANNEL_MANAGER, _channel_manager_iface_init); - + G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_DBUS_PROPERTIES, + tp_dbus_properties_mixin_iface_init); G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_ALIASING, _aliasing_iface_init); + G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_CONTACTS, + tp_contacts_mixin_iface_init); + G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_SIMPLE_PRESENCE, + tp_presence_mixin_simple_presence_iface_init); + G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_PRESENCE, + tp_presence_mixin_iface_init); ); static void -_set_property (GObject *object, - unsigned prop_id, - const GValue *value, - GParamSpec *pspec) -{ - GruschlerFacebookConnection *self; - - self = GRUSCHLER_FACEBOOK_CONNECTION (object); - - switch (prop_id) - { - case PROP_EMAIL: - g_free (self->priv->email); - self->priv->email = g_value_dup_string (value); - break; - - case PROP_PASSWORD: - g_free (self->priv->password); - self->priv->password = g_value_dup_string (value); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -_get_property (GObject *object, - unsigned prop_id, - GValue *value, - GParamSpec *pspec) -{ - GruschlerFacebookConnection *self; - - self = GRUSCHLER_FACEBOOK_CONNECTION (object); - - switch (prop_id) - { - case PROP_EMAIL: - g_value_set_string (value, self->priv->email); - break; - - case PROP_PASSWORD: - g_value_set_string (value, self->priv->password); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -_finalize (GObject *object) -{ - GObjectClass *object_class; - GruschlerFacebookConnection *self; - - self = GRUSCHLER_FACEBOOK_CONNECTION (object); - - if (self->priv->facebook) - g_object_unref (self->priv->facebook); - if (self->priv->session) - g_object_unref (self->priv->session); - if (self->priv->contacts) - g_object_unref (self->priv->contacts); - if (self->priv->groups) - g_object_unref (self->priv->groups); - if (self->priv->lists) - g_object_unref (self->priv->lists); - - g_hash_table_unref (self->priv->list_channels); - g_hash_table_unref (self->priv->group_channels); - g_hash_table_unref (self->priv->profiles); - - g_free (self->priv->email); - g_free (self->priv->password); - g_free (self->priv->token); - - object_class = G_OBJECT_CLASS (gruschler_facebook_connection_parent_class); - object_class->finalize (object); -} - -static void _create_handle_repos (TpBaseConnection *connection, TpHandleRepoIface *repos[NUM_TP_HANDLE_TYPES]) { @@ -343,27 +284,63 @@ _get_unique_connection_name (TpBaseConnection *connection) return tp_escape_as_identifier (self->priv->email); } -const char * +static const char * _get_self_uid (GruschlerFacebookConnection *self) { return tp_handle_inspect (self->priv->contacts, TP_BASE_CONNECTION (self)->self_handle); } -const char * +static RestXmlNode * +_get_profile (GruschlerFacebookConnection *self, + TpHandle handle) +{ + return g_hash_table_lookup (self->priv->profiles, + GUINT_TO_POINTER (handle)); +} + +static const char * _get_contact_alias (GruschlerFacebookConnection *self, TpHandle handle) { RestXmlNode *profile, *node = NULL; - profile = g_hash_table_lookup (self->priv->profiles, - GUINT_TO_POINTER (handle)); + if (NULL != (profile = _get_profile (self, handle))) + if (NULL != (node = rest_xml_node_find (profile, "name")) || + NULL != (node = rest_xml_node_find (profile, "uid"))) + return node->content; + + return NULL; +} + +static GruschlerFacebookPresenceStatus +_get_contact_presence (GruschlerFacebookConnection *self, + TpHandle handle) +{ + RestXmlNode *node = NULL; + unsigned i; + + if (NULL != (node = _get_profile (self, handle)) && + NULL != (node = rest_xml_node_find (node, "online_presence")) && + NULL != (node->content)) + { + for (i = 0; _presence_statuses[i].name; ++i) + if (!strcmp (_presence_statuses[i].name, node->content)) + return i; + } - if (!profile) - return NULL; + return GRUSCHLER_FACEBOOK_PRESENCE_UNKNOWN; +} - if (NULL != (node = rest_xml_node_find (profile, "name")) || - NULL != (node = rest_xml_node_find (profile, "uid"))) +static const char * +_get_contact_status (GruschlerFacebookConnection *self, + TpHandle handle) +{ + RestXmlNode *node = NULL; + + if (NULL != (node = _get_profile (self, handle)) && + NULL != (node = rest_xml_node_find (node, "status")) && + NULL != (node = rest_xml_node_find (node, "message"))) return node->content; return NULL; @@ -574,9 +551,9 @@ _update_friends (GruschlerFacebookConnection *self) g_string_append_printf (queries, "\"profiles\":" - "\"SELECT uid,name,profile_update_time " - "FROM user WHERE uid IN (SELECT uid2 FROM friend WHERE uid1=%s)\"", - _get_self_uid (self)); + "\"SELECT uid,name,profile_update_time,online_presence,status " + "FROM user WHERE uid=%s OR uid IN (SELECT uid2 FROM friend WHERE uid1=%s)\"", + _get_self_uid (self), _get_self_uid (self)); /* TODO: "AND profile_update_time > 0" */ g_string_append_c (queries, '}'); @@ -664,7 +641,8 @@ _post_login_data_cb (SoupSession *session, uri = soup_message_get_uri (message); g_debug ("status=%d length=%lld", message->status_code, message->response_body->length); -g_debug ("%s %s", uri->path, uri->query); +g_debug ("uri=%s query=%s", uri->path, uri->query); +g_file_set_contents ("/tmp/fb-login.html", message->response_body->data, message->response_body->length, NULL); if (!g_strcmp0 (uri->path, "/login.php")) { @@ -908,59 +886,6 @@ _shut_down (TpBaseConnection *connection) } static void -gruschler_facebook_connection_class_init (GruschlerFacebookConnectionClass *class) -{ - static const char *interfaces[] = { - TP_IFACE_CONNECTION_INTERFACE_ALIASING, -#if 0 - TP_IFACE_CONNECTION_INTERFACE_AVATARS, - TP_IFACE_CONNECTION_INTERFACE_SIMPLE_PRESENCE, - TP_IFACE_CONNECTION_INTERFACE_CONTACTS, - TP_IFACE_CONNECTION_INTERFACE_LOCATION, - RTCOM_TP_IFACE_CONNECTION_INTERFACE_CONTACT_INFO, -#endif - NULL - }; - - GParamSpec *pspec; - GObjectClass *object_class; - TpBaseConnectionClass *connection_class; - - object_class = G_OBJECT_CLASS (class); - object_class->set_property = _set_property; - object_class->get_property = _get_property; - object_class->finalize = _finalize; - - connection_class = TP_BASE_CONNECTION_CLASS (class); - connection_class->create_handle_repos = _create_handle_repos; - connection_class->create_channel_managers = _create_channel_managers; - connection_class->get_unique_connection_name = _get_unique_connection_name; - connection_class->start_connecting = _start_connecting; - connection_class->shut_down = _shut_down; - connection_class->interfaces_always_present = interfaces; - - pspec = g_param_spec_string ("email", - "Email", - "Email address for accessing Facebook", - GRUSCHLER_FACEBOOK_DEFAULT_EMAIL, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | - G_PARAM_CONSTRUCT_ONLY); - - g_object_class_install_property (object_class, PROP_EMAIL, pspec); - - pspec = g_param_spec_string ("password", - "Password", - "Password for accessing Facebook", - GRUSCHLER_FACEBOOK_DEFAULT_PASSWORD, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | - G_PARAM_CONSTRUCT_ONLY); - - g_object_class_install_property (object_class, PROP_PASSWORD, pspec); - - g_type_class_add_private (class, sizeof (GruschlerFacebookConnectionPrivate)); -} - -static void _foreach_channel (TpChannelManager *manager, TpExportableChannelFunc callback, gpointer user_data) @@ -1182,6 +1107,31 @@ _aliasing_set_aliases (TpSvcConnectionInterfaceAliasing *aliasing, } static void +_aliasing_fill_contact_attributes (GObject *object, + const GArray *contacts, + GHashTable *attributes) +{ + GruschlerFacebookConnection *self; + unsigned i; + + self = GRUSCHLER_FACEBOOK_CONNECTION (object); + + for (i = 0; i < contacts->len; ++i) + { + TpHandle handle; + GValue *value; + + value = tp_g_value_slice_new (G_TYPE_STRING); + handle = g_array_index (contacts, TpHandle, i); + g_value_set_string (value, _get_contact_alias (self, handle)); + + tp_contacts_mixin_set_contact_attribute (attributes, handle, + TP_IFACE_CONNECTION_INTERFACE_ALIASING "/alias", + value); + } +} + +static void _aliasing_iface_init (TpSvcConnectionInterfaceAliasingClass *iface) { #define IMPLEMENT(x) \ @@ -1193,6 +1143,262 @@ _aliasing_iface_init (TpSvcConnectionInterfaceAliasingClass *iface) #undef IMPLEMENT } +static GHashTable * +_presence_get_contact_statuses (GObject *object, + const GArray *contacts, + GError **error) +{ + GruschlerFacebookConnection *self; + TpBaseConnection *base; + GHashTable *statuses; + unsigned i; + + statuses = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, + (GDestroyNotify) tp_presence_status_free); + + self = GRUSCHLER_FACEBOOK_CONNECTION (object); + base = TP_BASE_CONNECTION (self); + + for (i = 0; i < contacts->len; i++) + { + TpPresenceStatus *presence; + GruschlerFacebookPresenceStatus status = GRUSCHLER_FACEBOOK_PRESENCE_UNKNOWN; + const char *message = NULL; + GHashTable *parameters; + TpHandle handle; + + handle = g_array_index (contacts, TpHandle, i); + status = _get_contact_presence (self, handle); + message = _get_contact_status (self, handle); + parameters = tp_asv_new (NULL, NULL); + + if (message) + tp_asv_set_string (parameters, "message", message); + + presence = tp_presence_status_new (status, parameters); + g_hash_table_unref (parameters); + + g_hash_table_insert (statuses, GUINT_TO_POINTER (handle), presence); + } + + return statuses; +} + +static gboolean +_presence_set_own_status (GObject *object, + const TpPresenceStatus *status, + GError **error) +{ +/* FIXME: implement this */ + g_warning ("%s: not implemented yet :-(", G_STRFUNC); + g_set_error (error, TP_ERRORS, TP_ERROR_NOT_IMPLEMENTED, + "Setting own presence status is not implemented yet"); + return FALSE; +} + +static GObject * +_constructor (GType type, + guint n_props, + GObjectConstructParam *props) +{ + GruschlerFacebookConnection *self; + GObject *object; + GObjectClass *parent_class; + gsize offset; + + parent_class = G_OBJECT_CLASS (gruschler_facebook_connection_parent_class); + object = parent_class->constructor (type, n_props, props); + self = GRUSCHLER_FACEBOOK_CONNECTION (object); + + offset = G_STRUCT_OFFSET (GruschlerFacebookConnection, contacts); + tp_contacts_mixin_init (object, offset); + + offset = G_STRUCT_OFFSET (GruschlerFacebookConnection, presence); + tp_presence_mixin_init (object, offset); + + tp_base_connection_register_with_contacts_mixin (TP_BASE_CONNECTION (self)); + tp_presence_mixin_simple_presence_register_with_contacts_mixin (object); + + tp_contacts_mixin_add_contact_attributes_iface (object, + TP_IFACE_CONNECTION_INTERFACE_ALIASING, + _aliasing_fill_contact_attributes); + + return object; +} + +static void +_set_property (GObject *object, + unsigned prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GruschlerFacebookConnection *self; + + self = GRUSCHLER_FACEBOOK_CONNECTION (object); + + switch (prop_id) + { + case PROP_EMAIL: + g_free (self->priv->email); + self->priv->email = g_value_dup_string (value); + break; + + case PROP_PASSWORD: + g_free (self->priv->password); + self->priv->password = g_value_dup_string (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +_get_property (GObject *object, + unsigned prop_id, + GValue *value, + GParamSpec *pspec) +{ + GruschlerFacebookConnection *self; + + self = GRUSCHLER_FACEBOOK_CONNECTION (object); + + switch (prop_id) + { + case PROP_EMAIL: + g_value_set_string (value, self->priv->email); + break; + + case PROP_PASSWORD: + g_value_set_string (value, self->priv->password); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +_finalize (GObject *object) +{ + GObjectClass *object_class; + GruschlerFacebookConnection *self; + + self = GRUSCHLER_FACEBOOK_CONNECTION (object); + + tp_contacts_mixin_finalize (object); + tp_presence_mixin_finalize (object); + + if (self->priv->facebook) + g_object_unref (self->priv->facebook); + if (self->priv->session) + g_object_unref (self->priv->session); + if (self->priv->contacts) + g_object_unref (self->priv->contacts); + if (self->priv->groups) + g_object_unref (self->priv->groups); + if (self->priv->lists) + g_object_unref (self->priv->lists); + + g_hash_table_unref (self->priv->list_channels); + g_hash_table_unref (self->priv->group_channels); + g_hash_table_unref (self->priv->profiles); + + g_free (self->priv->email); + g_free (self->priv->password); + g_free (self->priv->token); + + object_class = G_OBJECT_CLASS (gruschler_facebook_connection_parent_class); + object_class->finalize (object); +} + +static void +gruschler_facebook_connection_class_init (GruschlerFacebookConnectionClass *class) +{ + static const char *interfaces[] = { + TP_IFACE_CONNECTION_INTERFACE_ALIASING, + TP_IFACE_CONNECTION_INTERFACE_CONTACTS, + TP_IFACE_CONNECTION_INTERFACE_PRESENCE, + TP_IFACE_CONNECTION_INTERFACE_SIMPLE_PRESENCE, +#if 0 + TP_IFACE_CONNECTION_INTERFACE_AVATARS, + TP_IFACE_CONNECTION_INTERFACE_LOCATION, + RTCOM_TP_IFACE_CONNECTION_INTERFACE_CONTACT_INFO, +#endif + NULL + }; + + static TpDBusPropertiesMixinIfaceImpl prop_interfaces[] = { +#if 0 + { TP_IFACE_CONNECTION_INTERFACE_LOCATION, + conn_location_properties_getter, + conn_location_properties_setter, + location_props, + }, + { TP_IFACE_CONNECTION_INTERFACE_AVATARS, + conn_avatars_properties_getter, + NULL, + avatar_props, + }, +#endif + { NULL, } + }; + + GParamSpec *pspec; + GObjectClass *object_class; + TpBaseConnectionClass *connection_class; + gsize offset; + + object_class = G_OBJECT_CLASS (class); + object_class->constructor = _constructor; + object_class->set_property = _set_property; + object_class->get_property = _get_property; + object_class->finalize = _finalize; + + connection_class = TP_BASE_CONNECTION_CLASS (class); + connection_class->create_handle_repos = _create_handle_repos; + connection_class->create_channel_managers = _create_channel_managers; + connection_class->get_unique_connection_name = _get_unique_connection_name; + connection_class->start_connecting = _start_connecting; + connection_class->shut_down = _shut_down; + connection_class->interfaces_always_present = interfaces; + + pspec = g_param_spec_string ("email", + "Email", + "Email address for accessing Facebook", + GRUSCHLER_FACEBOOK_DEFAULT_EMAIL, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | + G_PARAM_CONSTRUCT_ONLY); + + g_object_class_install_property (object_class, PROP_EMAIL, pspec); + + pspec = g_param_spec_string ("password", + "Password", + "Password for accessing Facebook", + GRUSCHLER_FACEBOOK_DEFAULT_PASSWORD, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | + G_PARAM_CONSTRUCT_ONLY); + + g_object_class_install_property (object_class, PROP_PASSWORD, pspec); + + class->properties_class.interfaces = prop_interfaces; + offset = G_STRUCT_OFFSET (GruschlerFacebookConnectionClass, properties_class); + tp_dbus_properties_mixin_class_init (object_class, offset); + + offset = G_STRUCT_OFFSET (GruschlerFacebookConnectionClass, contacts_class); + tp_contacts_mixin_class_init (object_class, offset); + + offset = G_STRUCT_OFFSET (GruschlerFacebookConnectionClass, presence_class); + tp_presence_mixin_class_init (object_class, offset, NULL, + _presence_get_contact_statuses, + _presence_set_own_status, _presence_statuses); + tp_presence_mixin_simple_presence_init_dbus_properties (object_class); + + g_type_class_add_private (class, sizeof (GruschlerFacebookConnectionPrivate)); +} + static void gruschler_facebook_connection_init (GruschlerFacebookConnection *self) { diff --git a/src/facebook-connection.h b/src/facebook-connection.h index 2e389aa..80b7e45 100644 --- a/src/facebook-connection.h +++ b/src/facebook-connection.h @@ -19,6 +19,9 @@ #define __GRUSCHLER_FACEBOOK_CONNECTION_H__ #include <telepathy-glib/base-connection-manager.h> +#include <telepathy-glib/dbus-properties-mixin.h> +#include <telepathy-glib/contacts-mixin.h> +#include <telepathy-glib/presence-mixin.h> G_BEGIN_DECLS @@ -34,13 +37,26 @@ typedef struct _GruschlerFacebookConnectionClass GruschlerFacebookConnectionCl typedef struct _GruschlerFacebookConnectionPrivate GruschlerFacebookConnectionPrivate; typedef struct _GruschlerFacebookConnectionParams GruschlerFacebookConnectionParams; +typedef enum { + GRUSCHLER_FACEBOOK_PRESENCE_UNKNOWN, + GRUSCHLER_FACEBOOK_PRESENCE_OFFLINE, + GRUSCHLER_FACEBOOK_PRESENCE_ERROR, + GRUSCHLER_FACEBOOK_PRESENCE_IDLE, + GRUSCHLER_FACEBOOK_PRESENCE_ACTIVE, +} GruschlerFacebookPresenceStatus; + struct _GruschlerFacebookConnection { TpBaseConnection parent_instance; + TpContactsMixin contacts; + TpPresenceMixin presence; GruschlerFacebookConnectionPrivate *priv; }; struct _GruschlerFacebookConnectionClass { - TpBaseConnectionClass parent_class; + TpBaseConnectionClass parent_class; + TpContactsMixinClass contacts_class; + TpPresenceMixinClass presence_class; + TpDBusPropertiesMixinClass properties_class; }; struct _GruschlerFacebookConnectionParams { |