diff options
author | Will Thompson <will.thompson@collabora.co.uk> | 2011-11-07 17:46:04 +0000 |
---|---|---|
committer | Will Thompson <will.thompson@collabora.co.uk> | 2011-11-07 17:46:11 +0000 |
commit | df3882d34528ccb320835fafbad74034a589dc64 (patch) | |
tree | 35637b42b4e45ca0049ed4270d4767fbd814df90 | |
parent | 6ef3ca940f0b4a4ae1aedb2c03ddc794fe659582 (diff) | |
parent | 6895340badaff9f97683230848ae8e0f9f6f99d8 (diff) |
Merge branch 'facebook-own-message'
Reviewed-by: Jonny Lamb <jonny.lamb@collabora.co.uk>
-rw-r--r-- | src/connection.c | 4 | ||||
-rw-r--r-- | src/disco.c | 2 | ||||
-rw-r--r-- | src/ft-channel.c | 2 | ||||
-rw-r--r-- | src/im-channel.c | 190 | ||||
-rw-r--r-- | src/im-channel.h | 12 | ||||
-rw-r--r-- | src/im-factory.c | 286 | ||||
-rw-r--r-- | src/jingle-session.c | 2 | ||||
-rw-r--r-- | src/media-factory.c | 2 | ||||
-rw-r--r-- | src/muc-factory.c | 194 | ||||
-rw-r--r-- | src/namespaces.h | 8 | ||||
-rw-r--r-- | src/roster.c | 2 | ||||
-rw-r--r-- | src/util.h | 2 | ||||
-rw-r--r-- | src/vcard-manager.c | 2 | ||||
-rw-r--r-- | tests/twisted/Makefile.am | 1 | ||||
-rw-r--r-- | tests/twisted/constants.py | 8 | ||||
-rw-r--r-- | tests/twisted/text/facebook-own-message.py | 70 | ||||
-rw-r--r-- | tests/twisted/tools/exec-with-log.sh.in | 4 |
17 files changed, 508 insertions, 283 deletions
diff --git a/src/connection.c b/src/connection.c index eeec1cd16..606e398fb 100644 --- a/src/connection.c +++ b/src/connection.c @@ -25,8 +25,6 @@ #include <stdio.h> #include <string.h> -#define DBUS_API_SUBJECT_TO_CHANGE - #include <dbus/dbus-glib.h> #include <dbus/dbus-glib-lowlevel.h> #include <glib-object.h> @@ -3814,7 +3812,7 @@ gabble_connection_send_presence (GabbleConnection *conn, lm_message_node_add_own_nick ( wocky_stanza_get_top_node (message), conn); - if (!CHECK_STR_EMPTY(status)) + if (!tp_str_empty (status)) lm_message_node_add_child ( wocky_stanza_get_top_node (message), "status", status); diff --git a/src/disco.c b/src/disco.c index ba29a0fb7..21a15423b 100644 --- a/src/disco.c +++ b/src/disco.c @@ -26,8 +26,6 @@ #include <string.h> -#define DBUS_API_SUBJECT_TO_CHANGE - #include <dbus/dbus-glib.h> #include <dbus/dbus-glib-lowlevel.h> #include <telepathy-glib/dbus.h> diff --git a/src/ft-channel.c b/src/ft-channel.c index 70b73f7ec..cc2eb57c4 100644 --- a/src/ft-channel.c +++ b/src/ft-channel.c @@ -1471,7 +1471,7 @@ gabble_file_transfer_channel_offer_file (GabbleFileTransferChannel *self, gboolean jingle_share = FALSE; const gchar *si_resource = NULL; const gchar *share_resource = NULL; - g_assert (!CHECK_STR_EMPTY (self->priv->filename)); + g_assert (!tp_str_empty (self->priv->filename)); g_assert (self->priv->size != GABBLE_UNDEFINED_FILE_SIZE); g_return_val_if_fail (self->priv->bytestream == NULL, FALSE); g_return_val_if_fail (self->priv->gtalk_file_collection == NULL, FALSE); diff --git a/src/im-channel.c b/src/im-channel.c index 40b3a368f..dba3aedba 100644 --- a/src/im-channel.c +++ b/src/im-channel.c @@ -176,6 +176,13 @@ gabble_im_channel_fill_immutable_properties (TpBaseChannel *chan, NULL); } +static gchar * +gabble_im_channel_get_object_path_suffix (TpBaseChannel *chan) +{ + return g_strdup_printf ("ImChannel%u", + tp_base_channel_get_target_handle (chan)); +} + static void gabble_im_channel_class_init (GabbleIMChannelClass *gabble_im_channel_class) { @@ -196,6 +203,7 @@ gabble_im_channel_class_init (GabbleIMChannelClass *gabble_im_channel_class) base_class->close = gabble_im_channel_close; base_class->fill_immutable_properties = gabble_im_channel_fill_immutable_properties; + base_class->get_object_path_suffix = gabble_im_channel_get_object_path_suffix; tp_message_mixin_init_dbus_properties (object_class); } @@ -384,118 +392,154 @@ _gabble_im_channel_send_message (GObject *object, priv->send_nick = FALSE; } -/** - * _gabble_im_channel_receive +static TpMessage * +build_message ( + GabbleIMChannel *self, + TpChannelTextMessageType type, + time_t timestamp, + const char *text) +{ + TpBaseChannel *base_chan = (TpBaseChannel *) self; + TpBaseConnection *base_conn = tp_base_channel_get_connection (base_chan); + TpMessage *msg = tp_cm_message_new (base_conn, 2); + + if (type != TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL) + tp_message_set_uint32 (msg, 0, "message-type", type); + + if (timestamp != 0) + tp_message_set_int64 (msg, 0, "message-sent", timestamp); + + /* Body */ + tp_message_set_string (msg, 1, "content-type", "text/plain"); + tp_message_set_string (msg, 1, "content", text); + + return msg; +} + +/* + * _gabble_im_channel_receive: + * @chan: a channel + * @type: the message type + * @from: the full JID we received the message from + * @timestamp: the time at which the message was sent (not the time it was + * received) + * @id: the id='' attribute from the <message/> stanza, if any + * @text: the plaintext body of the message + * @send_error: the reason why sending @text to @sender failed, or + * GABBLE_TEXT_CHANNEL_SEND_NO_ERROR if this call is not to report + * a failure to send. + * @delivery_status: if @send_error is GABBLE_TEXT_CHANNEL_SEND_NO_ERROR, + * ignored; else the delivery status to attach to the report. + * @state: a #TpChannelChatState, or -1 if there was no chat state in the + * message. * + * Shoves an incoming message into @chan, possibly updating the chat state at + * the same time; or maybe this is a delivery report? Who knows! It's a magical + * adventure. */ void _gabble_im_channel_receive (GabbleIMChannel *chan, TpChannelTextMessageType type, - TpHandle sender, const char *from, time_t timestamp, const gchar *id, const char *text, - TpChannelTextSendError send_error, - TpDeliveryStatus delivery_status, gint state) { GabbleIMChannelPrivate *priv; TpBaseChannel *base_chan; - TpBaseConnection *base_conn; + TpHandle peer; TpMessage *msg; - gchar *tmp; g_assert (GABBLE_IS_IM_CHANNEL (chan)); priv = chan->priv; base_chan = (TpBaseChannel *) chan; - base_conn = tp_base_channel_get_connection (base_chan); + peer = tp_base_channel_get_target_handle (base_chan); - if (send_error == GABBLE_TEXT_CHANNEL_SEND_NO_ERROR) + /* update peer's full JID if it's changed */ + if (tp_strdiff (from, priv->peer_jid)) { - /* update peer's full JID if it's changed */ - if (tp_strdiff (from, priv->peer_jid)) - { - g_free (priv->peer_jid); - priv->peer_jid = g_strdup (from); - } - - if (state == -1) - { - priv->chat_states_supported = CHAT_STATES_NOT_SUPPORTED; - } - else - { - _gabble_im_channel_state_receive (chan, state); - } + g_free (priv->peer_jid); + priv->peer_jid = g_strdup (from); } - else - { - /* strip off the resource (if any), since we just failed to send to it */ - char *slash = strchr (priv->peer_jid, '/'); - if (slash != NULL) - *slash = '\0'; + if (state == -1) + priv->chat_states_supported = CHAT_STATES_NOT_SUPPORTED; + else + _gabble_im_channel_state_receive (chan, state); - priv->chat_states_supported = CHAT_STATES_UNKNOWN; - } + msg = build_message (chan, type, timestamp, text); + tp_cm_message_set_sender (msg, peer); + tp_message_set_int64 (msg, 0, "message-received", time (NULL)); - msg = tp_cm_message_new (base_conn, 2); + if (id != NULL) + tp_message_set_string (msg, 0, "message-token", id); - /* Header */ - if (type != TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL) - tp_message_set_uint32 (msg, 0, "message-type", type); + tp_message_mixin_take_received (G_OBJECT (chan), msg); +} - if (timestamp != 0) - tp_message_set_int64 (msg, 0, "message-sent", timestamp); +void +_gabble_im_channel_report_delivery ( + GabbleIMChannel *self, + TpChannelTextMessageType type, + time_t timestamp, + const gchar *id, + const char *text, + TpChannelTextSendError send_error, + TpDeliveryStatus delivery_status) +{ + GabbleIMChannelPrivate *priv; + TpBaseChannel *base_chan = (TpBaseChannel *) self; + TpBaseConnection *base_conn; + TpHandle peer; + TpMessage *msg, *delivery_report; + gchar *tmp; - /* Body */ - tp_message_set_string (msg, 1, "content-type", "text/plain"); - tp_message_set_string (msg, 1, "content", text); + g_return_if_fail (GABBLE_IS_IM_CHANNEL (self)); + priv = self->priv; + peer = tp_base_channel_get_target_handle (base_chan); + base_conn = tp_base_channel_get_connection (base_chan); - if (send_error == GABBLE_TEXT_CHANNEL_SEND_NO_ERROR) + if (send_error != GABBLE_TEXT_CHANNEL_SEND_NO_ERROR) { - tp_cm_message_set_sender (msg, sender); - tp_message_set_int64 (msg, 0, "message-received", time (NULL)); + /* strip off the resource (if any), since we just failed to send to it */ + char *slash = strchr (priv->peer_jid, '/'); - if (id != NULL) - tp_message_set_string (msg, 0, "message-token", id); + if (slash != NULL) + *slash = '\0'; - tp_message_mixin_take_received (G_OBJECT (chan), msg); + priv->chat_states_supported = CHAT_STATES_UNKNOWN; } - else - { - TpMessage *delivery_report = tp_cm_message_new (base_conn, 1); - - tp_message_set_uint32 (delivery_report, 0, "message-type", - TP_CHANNEL_TEXT_MESSAGE_TYPE_DELIVERY_REPORT); - tp_cm_message_set_sender (delivery_report, sender); - tp_message_set_int64 (delivery_report, 0, "message-received", - time (NULL)); - tmp = gabble_generate_id (); - tp_message_set_string (delivery_report, 0, "message-token", tmp); - g_free (tmp); + msg = build_message (self, type, timestamp, text); + delivery_report = tp_cm_message_new (base_conn, 1); + tp_message_set_uint32 (delivery_report, 0, "message-type", + TP_CHANNEL_TEXT_MESSAGE_TYPE_DELIVERY_REPORT); + tp_cm_message_set_sender (delivery_report, peer); + tp_message_set_int64 (delivery_report, 0, "message-received", + time (NULL)); - tp_message_set_uint32 (delivery_report, 0, "delivery-status", - delivery_status); - tp_message_set_uint32 (delivery_report, 0, "delivery-error", send_error); + tmp = gabble_generate_id (); + tp_message_set_string (delivery_report, 0, "message-token", tmp); + g_free (tmp); - if (id != NULL) - tp_message_set_string (delivery_report, 0, "delivery-token", id); + tp_message_set_uint32 (delivery_report, 0, "delivery-status", + delivery_status); + tp_message_set_uint32 (delivery_report, 0, "delivery-error", send_error); - /* We're getting a send error, so the original sender of the echoed - * message must be us! */ - tp_cm_message_set_sender (msg, base_conn->self_handle); + if (id != NULL) + tp_message_set_string (delivery_report, 0, "delivery-token", id); - /* Since this is a send error, we can trust the id on the message. */ - if (id != NULL) - tp_message_set_string (msg, 0, "message-token", id); + /* This is a delivery report, so the original sender of the echoed message + * must be us! */ + tp_cm_message_set_sender (msg, base_conn->self_handle); - tp_cm_message_take_message (delivery_report, 0, "delivery-echo", msg); + /* Since this is a delivery report, we can trust the id on the message. */ + if (id != NULL) + tp_message_set_string (msg, 0, "message-token", id); - tp_message_mixin_take_received (G_OBJECT (chan), delivery_report); - } + tp_cm_message_take_message (delivery_report, 0, "delivery-echo", msg); + tp_message_mixin_take_received (G_OBJECT (self), delivery_report); } /** diff --git a/src/im-channel.h b/src/im-channel.h index 17d6271b0..fdbae19bf 100644 --- a/src/im-channel.h +++ b/src/im-channel.h @@ -66,17 +66,23 @@ GType gabble_im_channel_get_type (void); void _gabble_im_channel_receive (GabbleIMChannel *chan, TpChannelTextMessageType type, - TpHandle sender, const char *from, time_t timestamp, const char *id, const char *text, - TpChannelTextSendError send_error, - TpDeliveryStatus delivery_status, gint state); void _gabble_im_channel_state_receive (GabbleIMChannel *chan, TpChannelChatState state); +void _gabble_im_channel_report_delivery ( + GabbleIMChannel *self, + TpChannelTextMessageType type, + time_t timestamp, + const gchar *id, + const char *text, + TpChannelTextSendError send_error, + TpDeliveryStatus delivery_status); + G_END_DECLS #endif /* #ifndef __GABBLE_IM_CHANNEL_H__*/ diff --git a/src/im-factory.c b/src/im-factory.c index be518b276..b695a601d 100644 --- a/src/im-factory.c +++ b/src/im-factory.c @@ -20,8 +20,6 @@ #include "config.h" #include "im-factory.h" -#define DBUS_API_SUBJECT_TO_CHANGE - #include <string.h> #include <dbus/dbus-glib.h> @@ -31,6 +29,7 @@ #include <telepathy-glib/dbus.h> #include <telepathy-glib/gtypes.h> #include <telepathy-glib/interfaces.h> +#include <wocky/wocky-c2s-porter.h> #define DEBUG_FLAG GABBLE_DEBUG_IM @@ -42,6 +41,7 @@ #include "disco.h" #include "im-channel.h" #include "message-util.h" +#include "namespaces.h" static void channel_manager_iface_init (gpointer, gpointer); static void caps_channel_manager_iface_init (gpointer, gpointer); @@ -70,11 +70,6 @@ struct _GabbleImFactoryPrivate gboolean dispose_has_run; }; -#define GABBLE_IM_FACTORY_GET_PRIVATE(o) \ - (G_TYPE_INSTANCE_GET_PRIVATE ((o), GABBLE_TYPE_IM_FACTORY,\ - GabbleImFactoryPrivate)) - - static void gabble_im_factory_init (GabbleImFactory *self) { @@ -93,20 +88,24 @@ gabble_im_factory_init (GabbleImFactory *self) static void connection_status_changed_cb (GabbleConnection *conn, guint status, guint reason, GabbleImFactory *self); +static void porter_available_cb ( + GabbleConnection *conn, + WockyPorter *porter, + gpointer user_data); - -static GObject * -gabble_im_factory_constructor (GType type, guint n_props, - GObjectConstructParam *props) +static void +gabble_im_factory_constructed (GObject *obj) { - GObject *obj = G_OBJECT_CLASS (gabble_im_factory_parent_class)-> - constructor (type, n_props, props); GabbleImFactory *self = GABBLE_IM_FACTORY (obj); + GObjectClass *parent_class = gabble_im_factory_parent_class; + + if (parent_class->constructed != NULL) + parent_class->constructed (obj); self->priv->status_changed_id = g_signal_connect (self->priv->conn, "status-changed", (GCallback) connection_status_changed_cb, obj); - - return obj; + tp_g_signal_connect_object (self->priv->conn, + "porter-available", (GCallback) porter_available_cb, obj, 0); } @@ -117,7 +116,7 @@ static void gabble_im_factory_dispose (GObject *object) { GabbleImFactory *fac = GABBLE_IM_FACTORY (object); - GabbleImFactoryPrivate *priv = GABBLE_IM_FACTORY_GET_PRIVATE (fac); + GabbleImFactoryPrivate *priv = fac->priv; if (priv->dispose_has_run) return; @@ -139,7 +138,7 @@ gabble_im_factory_get_property (GObject *object, GParamSpec *pspec) { GabbleImFactory *fac = GABBLE_IM_FACTORY (object); - GabbleImFactoryPrivate *priv = GABBLE_IM_FACTORY_GET_PRIVATE (fac); + GabbleImFactoryPrivate *priv = fac->priv; switch (property_id) { case PROP_CONNECTION: @@ -158,7 +157,7 @@ gabble_im_factory_set_property (GObject *object, GParamSpec *pspec) { GabbleImFactory *fac = GABBLE_IM_FACTORY (object); - GabbleImFactoryPrivate *priv = GABBLE_IM_FACTORY_GET_PRIVATE (fac); + GabbleImFactoryPrivate *priv = fac->priv; switch (property_id) { case PROP_CONNECTION: @@ -179,7 +178,7 @@ gabble_im_factory_class_init (GabbleImFactoryClass *gabble_im_factory_class) g_type_class_add_private (gabble_im_factory_class, sizeof (GabbleImFactoryPrivate)); - object_class->constructor = gabble_im_factory_constructor; + object_class->constructed = gabble_im_factory_constructed; object_class->dispose = gabble_im_factory_dispose; object_class->get_property = gabble_im_factory_get_property; @@ -193,11 +192,10 @@ gabble_im_factory_class_init (GabbleImFactoryClass *gabble_im_factory_class) } -static GabbleIMChannel *new_im_channel (GabbleImFactory *fac, - TpHandle handle, TpHandle initiator, gpointer request_token); - -static void im_channel_closed_cb (GabbleIMChannel *chan, gpointer user_data); - +static GabbleIMChannel *get_channel_for_incoming_message ( + GabbleImFactory *self, + const gchar *jid, + gboolean create_if_missing); /** * im_factory_message_cb: @@ -211,18 +209,14 @@ im_factory_message_cb (LmMessageHandler *handler, gpointer user_data) { GabbleImFactory *fac = GABBLE_IM_FACTORY (user_data); - GabbleImFactoryPrivate *priv = GABBLE_IM_FACTORY_GET_PRIVATE (fac); - TpBaseConnection *conn = (TpBaseConnection *) priv->conn; - TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (conn, - TP_HANDLE_TYPE_CONTACT); const gchar *from, *body, *id; time_t stamp; TpChannelTextMessageType msgtype; - TpHandle handle; GabbleIMChannel *chan; gint state; TpChannelTextSendError send_error; TpDeliveryStatus delivery_status; + gboolean create_if_missing; if (!gabble_message_util_parse_incoming_message (message, &from, &stamp, &msgtype, &id, &body, &state, &send_error, &delivery_status)) @@ -233,62 +227,46 @@ im_factory_message_cb (LmMessageHandler *handler, return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; } - handle = tp_handle_ensure (contact_repo, from, NULL, NULL); - if (handle == 0) + /* We don't want to open up a channel for the sole purpose of reporting a + * send error, nor if this is just a chat state notification. + */ + create_if_missing = + (send_error == GABBLE_TEXT_CHANNEL_SEND_NO_ERROR) && + (body != NULL); + chan = get_channel_for_incoming_message (fac, from, create_if_missing); + if (chan == NULL) { - STANZA_DEBUG (message, "ignoring message node from malformed jid"); + if (create_if_missing) + STANZA_DEBUG (message, "ignoring message from non-contact JID"); + else + DEBUG ("ignoring message error or chat state notification from '%s': " + "no existing channel", from); + return LM_HANDLER_RESULT_REMOVE_MESSAGE; } - chan = g_hash_table_lookup (priv->channels, GUINT_TO_POINTER (handle)); - - if (chan == NULL) + if (send_error != GABBLE_TEXT_CHANNEL_SEND_NO_ERROR) { - if (send_error != GABBLE_TEXT_CHANNEL_SEND_NO_ERROR) - { - DEBUG ("ignoring message error; no sending channel"); - tp_handle_unref (contact_repo, handle); - return LM_HANDLER_RESULT_REMOVE_MESSAGE; - } - if (body == NULL) { - /* don't create a new channel if all we have is a chat state */ - DEBUG ("ignoring message without body; no existing channel"); - tp_handle_unref (contact_repo, handle); + DEBUG ("ignoring error sending chat state to %s", from); return LM_HANDLER_RESULT_REMOVE_MESSAGE; } - DEBUG ("found no IM channel, creating one"); + DEBUG ("got error sending to %s, msgtype %u, body:\n%s", + from, msgtype, body); - chan = new_im_channel (fac, handle, handle, NULL); + _gabble_im_channel_report_delivery (chan, msgtype, stamp, id, body, + send_error, delivery_status); } - - g_assert (chan != NULL); - - /* now the channel is referencing the handle, so if we unref it, that's - * not a problem */ - tp_handle_unref (contact_repo, handle); - - if (send_error != GABBLE_TEXT_CHANNEL_SEND_NO_ERROR) + else if (body != NULL) { - if (body == NULL) - { - DEBUG ("ignoring error sending chat state to %s (handle %u)", from, - handle); - return LM_HANDLER_RESULT_REMOVE_MESSAGE; - } - - DEBUG ("got error sending to %s (handle %u), msgtype %u, body:\n%s", - from, handle, msgtype, body); + _gabble_im_channel_receive (chan, msgtype, from, stamp, id, body, state); + } + else if (state != -1) + { + _gabble_im_channel_state_receive (chan, (TpChannelChatState) state); } - - if (body != NULL) - _gabble_im_channel_receive (chan, msgtype, handle, from, stamp, id, body, - send_error, delivery_status, state); - else if (state != -1 && send_error == GABBLE_TEXT_CHANNEL_SEND_NO_ERROR) - _gabble_im_channel_state_receive (chan, (TpChannelChatState) state); - return LM_HANDLER_RESULT_REMOVE_MESSAGE; } @@ -305,7 +283,7 @@ static void im_channel_closed_cb (GabbleIMChannel *chan, gpointer user_data) { GabbleImFactory *self = GABBLE_IM_FACTORY (user_data); - GabbleImFactoryPrivate *priv = GABBLE_IM_FACTORY_GET_PRIVATE (self); + GabbleImFactoryPrivate *priv = self->priv; TpHandle contact_handle; gboolean really_destroyed; @@ -338,40 +316,43 @@ im_channel_closed_cb (GabbleIMChannel *chan, gpointer user_data) } } -/** - * new_im_channel +/* + * new_im_channel: + * @fac: the factory + * @handle: a contact handle, for whom a channel must not yet exist + * @request_token: if the channel is being created in response to a channel + * request, the associated request token; otherwise, NULL. + * + * Creates a new 1-1 text channel to a contact. Must only be called when no 1-1 + * text channel is already open to that contact. + * + * Returns: (transfer none): a freshly-constructed channel */ static GabbleIMChannel * new_im_channel (GabbleImFactory *fac, TpHandle handle, - TpHandle initiator, gpointer request_token) { - GabbleImFactoryPrivate *priv; - TpBaseConnection *conn; + GabbleImFactoryPrivate *priv = fac->priv; + TpBaseConnection *conn = (TpBaseConnection *) priv->conn; GabbleIMChannel *chan; - char *object_path; GSList *request_tokens; + TpHandle initiator; - g_return_val_if_fail (GABBLE_IS_IM_FACTORY (fac), NULL); g_return_val_if_fail (handle != 0, NULL); - g_return_val_if_fail (initiator != 0, NULL); - priv = GABBLE_IM_FACTORY_GET_PRIVATE (fac); - conn = (TpBaseConnection *) priv->conn; + if (request_token != NULL) + initiator = conn->self_handle; + else + initiator = handle; - object_path = g_strdup_printf ("%s/ImChannel%u", - conn->object_path, handle); chan = g_object_new (GABBLE_TYPE_IM_CHANNEL, "connection", priv->conn, - "object-path", object_path, "handle", handle, "initiator-handle", initiator, "requested", (handle != initiator), NULL); - DEBUG ("object path %s", object_path); tp_base_channel_register ((TpBaseChannel *) chan); - g_free (object_path); g_signal_connect (chan, "closed", (GCallback) im_channel_closed_cb, fac); @@ -390,6 +371,48 @@ new_im_channel (GabbleImFactory *fac, return chan; } +/* + * get_channel_for_incoming_message: + * @self: a factory + * @jid: a contact's JID + * @create_if_missing: if %TRUE, a new channel will be created if there is no + * existing channel to @jid + * + * Retrieves a 1-1 text channel to a particular contact. If no channel is open + * to @jid, it will be created only if @create_if_missing is %TRUE. If @jid is + * not of the form 'user@domain' (optionally with a resource), no channel will + * be opened. + * + * Returns: an IM channel to @jid, or %NULL + */ +static GabbleIMChannel * +get_channel_for_incoming_message ( + GabbleImFactory *self, + const gchar *jid, + gboolean create_if_missing) +{ + GabbleImFactoryPrivate *priv = self->priv; + TpBaseConnection *conn = (TpBaseConnection *) priv->conn; + TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (conn, + TP_HANDLE_TYPE_CONTACT); + TpHandle handle; + GabbleIMChannel *chan; + + g_return_val_if_fail (jid != NULL, NULL); + + handle = tp_handle_ensure (contact_repo, jid, NULL, NULL); + if (handle == 0) + return NULL; + + chan = g_hash_table_lookup (priv->channels, GUINT_TO_POINTER (handle)); + if (chan != NULL) + return chan; + else if (create_if_missing) + return new_im_channel (self, handle, NULL); + else + return NULL; +} + static void gabble_im_factory_close_all (GabbleImFactory *self) { @@ -441,6 +464,92 @@ connection_status_changed_cb (GabbleConnection *conn, } } +static gboolean +im_factory_own_message_cb ( + WockyPorter *porter, + WockyStanza *stanza, + gpointer user_data) +{ + GabbleImFactory *self = GABBLE_IM_FACTORY (user_data); + WockyNode *own_message, *body; + const gchar *to; + gboolean sent_locally; + GabbleIMChannel *chan; + + /* Our stanza filter should guarantee that these are present. */ + own_message = wocky_node_get_child (wocky_stanza_get_top_node (stanza), + "own-message"); + g_return_val_if_fail (own_message != NULL, FALSE); + body = wocky_node_get_child (own_message, "body"); + g_return_val_if_fail (body != NULL, FALSE); + + to = wocky_node_get_attribute (own_message, "to"); + if (to == NULL) + { + DEBUG ("own-message missing to='' attribute; ignoring"); + return FALSE; + } + + /* If self='true', the message was sent by the local user on this machine, + * rather than by the local user on some other machine. We don't really have + * a good way to show this in Messages. Also we don't get told the id='' of + * the original message, which is annoying. + */ + sent_locally = !tp_strdiff ("true", + wocky_node_get_attribute (own_message, "self")); + DEBUG ("this report is for a message to '%s', sent %s", + to, sent_locally ? "locally" : "remotely"); + + /* Don't create a channel for the sole purpose of reporting an own-message. + * This is consistent with not creating a channel to report send errors + * (given that both are delivery reports). + */ + chan = get_channel_for_incoming_message (self, to, FALSE); + if (chan != NULL) + _gabble_im_channel_report_delivery (chan, + TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL, 0, NULL, body->content, + GABBLE_TEXT_CHANNEL_SEND_NO_ERROR, TP_DELIVERY_STATUS_ACCEPTED); + else + DEBUG ("no channel for '%s'; not spawning one just for the delivery report", + to); + + wocky_porter_acknowledge_iq (porter, stanza, NULL); + return TRUE; +} + +static void +porter_available_cb ( + GabbleConnection *conn, + WockyPorter *porter, + gpointer user_data) +{ + GabbleImFactory *self = GABBLE_IM_FACTORY (user_data); + gchar *stream_server; + + g_object_get (conn, "stream-server", &stream_server, NULL); + + if (!tp_strdiff (stream_server, "chat.facebook.com")) + { + wocky_porter_register_handler_from ( + porter, WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_SET, + /* We could use _from_server() if that accepted messages from our + * JID's domain, not just from bare JID, full JID, or no sender + * specified—which would allow the extension to work on other servers + * too—but it doesn't. + */ + stream_server, + WOCKY_PORTER_HANDLER_PRIORITY_NORMAL, + im_factory_own_message_cb, self, + '(', + "own-message", ':', NS_FACEBOOK_MESSAGES, + '(', "body", ')', + ')', NULL); + } + + g_free (stream_server); +} + + static void gabble_im_factory_get_contact_caps (GabbleCapsChannelManager *manager, TpHandle handle, @@ -570,7 +679,6 @@ gabble_im_factory_requestotron (GabbleImFactory *self, GHashTable *request_properties, gboolean require_new) { - TpBaseConnection *base_conn = (TpBaseConnection *) self->priv->conn; TpHandle handle; GError *error = NULL; TpExportableChannel *channel; @@ -598,7 +706,7 @@ gabble_im_factory_requestotron (GabbleImFactory *self, if (channel == NULL) { - new_im_channel (self, handle, base_conn->self_handle, request_token); + new_im_channel (self, handle, request_token); return TRUE; } diff --git a/src/jingle-session.c b/src/jingle-session.c index 2981e3e83..d65721cfa 100644 --- a/src/jingle-session.c +++ b/src/jingle-session.c @@ -2092,7 +2092,7 @@ gabble_jingle_session_terminate (GabbleJingleSession *sess, lm_message_node_add_child (r, reason_elt, NULL); - if (!CHECK_STR_EMPTY(text)) + if (!tp_str_empty (text)) lm_message_node_add_child (r, "text", text); } diff --git a/src/media-factory.c b/src/media-factory.c index e8ec99081..575534089 100644 --- a/src/media-factory.c +++ b/src/media-factory.c @@ -20,8 +20,6 @@ #include "config.h" #include "media-factory.h" -#define DBUS_API_SUBJECT_TO_CHANGE - #include <stdlib.h> #include <string.h> diff --git a/src/muc-factory.c b/src/muc-factory.c index 971244454..c8275a25e 100644 --- a/src/muc-factory.c +++ b/src/muc-factory.c @@ -20,8 +20,6 @@ #include "config.h" #include "muc-factory.h" -#define DBUS_API_SUBJECT_TO_CHANGE - #include <string.h> #include <dbus/dbus-glib.h> @@ -99,17 +97,14 @@ struct _GabbleMucFactoryPrivate gboolean dispose_has_run; }; -#define GABBLE_MUC_FACTORY_GET_PRIVATE(o) \ - (G_TYPE_INSTANCE_GET_PRIVATE ((o), GABBLE_TYPE_MUC_FACTORY, \ - GabbleMucFactoryPrivate)) - static GObject *gabble_muc_factory_constructor (GType type, guint n_props, GObjectConstructParam *props); static void gabble_muc_factory_init (GabbleMucFactory *fac) { - GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (fac); + GabbleMucFactoryPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (fac, + GABBLE_TYPE_MUC_FACTORY, GabbleMucFactoryPrivate); fac->priv = priv; @@ -150,7 +145,7 @@ static void gabble_muc_factory_dispose (GObject *object) { GabbleMucFactory *fac = GABBLE_MUC_FACTORY (object); - GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (fac); + GabbleMucFactoryPrivate *priv = fac->priv; if (priv->dispose_has_run) return; @@ -179,7 +174,7 @@ gabble_muc_factory_get_property (GObject *object, GParamSpec *pspec) { GabbleMucFactory *fac = GABBLE_MUC_FACTORY (object); - GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (fac); + GabbleMucFactoryPrivate *priv = fac->priv; switch (property_id) { case PROP_CONNECTION: @@ -198,7 +193,7 @@ gabble_muc_factory_set_property (GObject *object, GParamSpec *pspec) { GabbleMucFactory *fac = GABBLE_MUC_FACTORY (object); - GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (fac); + GabbleMucFactoryPrivate *priv = fac->priv; switch (property_id) { case PROP_CONNECTION: @@ -242,7 +237,7 @@ static void muc_channel_closed_cb (GabbleMucChannel *chan, gpointer user_data) { GabbleMucFactory *fac = GABBLE_MUC_FACTORY (user_data); - GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (fac); + GabbleMucFactoryPrivate *priv = fac->priv; TpHandle room_handle; tp_channel_manager_emit_channel_closed_for_object (fac, @@ -265,7 +260,7 @@ muc_ready_cb (GabbleMucChannel *text_chan, gpointer data) { GabbleMucFactory *fac = GABBLE_MUC_FACTORY (data); - GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (fac); + GabbleMucFactoryPrivate *priv = fac->priv; GabbleTubesChannel *tubes_chan; GSList *requests_satisfied_text, *requests_satisfied_tubes = NULL; gboolean text_requested; @@ -354,7 +349,7 @@ muc_join_error_cb (GabbleMucChannel *chan, gpointer data) { GabbleMucFactory *fac = GABBLE_MUC_FACTORY (data); - GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (fac); + GabbleMucFactoryPrivate *priv = fac->priv; GabbleTubesChannel *tubes_chan; GSList *requests_satisfied; GSList *iter; @@ -455,7 +450,7 @@ new_muc_channel (GabbleMucFactory *fac, char **initial_ids, const char *room_name) { - GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (fac); + GabbleMucFactoryPrivate *priv = fac->priv; TpBaseConnection *conn = (TpBaseConnection *) priv->conn; GabbleMucChannel *chan; char *object_path; @@ -531,7 +526,7 @@ do_invite (GabbleMucFactory *fac, TpHandle inviter_handle, const gchar *reason) { - GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (fac); + GabbleMucFactoryPrivate *priv = fac->priv; TpHandleRepoIface *room_repo = tp_base_connection_get_handles ( (TpBaseConnection *) priv->conn, TP_HANDLE_TYPE_ROOM); TpHandle room_handle; @@ -583,7 +578,7 @@ obsolete_invite_disco_cb (GabbleDisco *self, struct DiscoInviteData *data = (struct DiscoInviteData *) user_data; GabbleMucFactory *fac = GABBLE_MUC_FACTORY (data->factory); - GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (fac); + GabbleMucFactoryPrivate *priv = fac->priv; TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( (TpBaseConnection *) priv->conn, TP_HANDLE_TYPE_CONTACT); LmMessageNode *identity; @@ -628,7 +623,7 @@ process_muc_invite (GabbleMucFactory *fac, const gchar *from, TpChannelTextSendError send_error) { - GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (fac); + GabbleMucFactoryPrivate *priv = fac->priv; TpBaseConnection *conn = (TpBaseConnection *) priv->conn; TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (conn, TP_HANDLE_TYPE_CONTACT); @@ -702,7 +697,7 @@ process_obsolete_invite (GabbleMucFactory *fac, const gchar *body, TpChannelTextSendError send_error) { - GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (fac); + GabbleMucFactoryPrivate *priv = fac->priv; TpBaseConnection *conn = (TpBaseConnection *) priv->conn; TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (conn, TP_HANDLE_TYPE_CONTACT); @@ -790,7 +785,7 @@ muc_factory_message_cb (LmMessageHandler *handler, gpointer user_data) { GabbleMucFactory *fac = GABBLE_MUC_FACTORY (user_data); - GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (fac); + GabbleMucFactoryPrivate *priv = fac->priv; const gchar *from, *body, *id; time_t stamp; @@ -826,7 +821,7 @@ muc_factory_message_cb (LmMessageHandler *handler, void gabble_muc_factory_broadcast_presence (GabbleMucFactory *self) { - GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (self); + GabbleMucFactoryPrivate *priv = self->priv; GHashTableIter iter; gpointer channel = NULL; @@ -844,7 +839,7 @@ gabble_muc_factory_associate_request (GabbleMucFactory *self, gpointer channel, gpointer request) { - GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (self); + GabbleMucFactoryPrivate *priv = self->priv; GSList *list = g_hash_table_lookup (priv->queued_requests, channel); g_assert (TP_IS_EXPORTABLE_CHANNEL (channel)); @@ -882,7 +877,7 @@ cancel_queued_requests (gpointer k, static void gabble_muc_factory_close_all (GabbleMucFactory *self) { - GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (self); + GabbleMucFactoryPrivate *priv = self->priv; DEBUG ("closing channels"); @@ -937,7 +932,7 @@ connection_status_changed_cb (GabbleConnection *conn, guint reason, GabbleMucFactory *self) { - GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (self); + GabbleMucFactoryPrivate *priv = self->priv; switch (status) { @@ -966,7 +961,7 @@ gabble_muc_factory_constructor (GType type, guint n_props, GObject *obj = G_OBJECT_CLASS (gabble_muc_factory_parent_class)-> constructor (type, n_props, props); GabbleMucFactory *self = GABBLE_MUC_FACTORY (obj); - GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (self); + GabbleMucFactoryPrivate *priv = self->priv; priv->status_changed_id = g_signal_connect (priv->conn, "status-changed", (GCallback) connection_status_changed_cb, obj); @@ -1011,7 +1006,7 @@ gabble_muc_factory_foreach_channel (TpChannelManager *manager, gpointer user_data) { GabbleMucFactory *fac = GABBLE_MUC_FACTORY (manager); - GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (fac); + GabbleMucFactoryPrivate *priv = fac->priv; struct _ForeachData data; data.user_data = user_data; @@ -1065,7 +1060,7 @@ gabble_muc_factory_handle_si_stream_request (GabbleMucFactory *self, const gchar *stream_id, LmMessage *msg) { - GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (self); + GabbleMucFactoryPrivate *priv = self->priv; TpHandleRepoIface *room_repo = tp_base_connection_get_handles ( (TpBaseConnection *) priv->conn, TP_HANDLE_TYPE_ROOM); GabbleMucChannel *gmuc = NULL; @@ -1095,15 +1090,15 @@ GabbleMucChannel * gabble_muc_factory_find_text_channel (GabbleMucFactory *self, TpHandle handle) { - GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (self); + GabbleMucFactoryPrivate *priv = self->priv; return g_hash_table_lookup (priv->text_channels, GUINT_TO_POINTER (handle)); } static const gchar * const muc_channel_fixed_properties[] = { - TP_IFACE_CHANNEL ".ChannelType", - TP_IFACE_CHANNEL ".TargetHandleType", + TP_PROP_CHANNEL_CHANNEL_TYPE, + TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, NULL }; @@ -1111,20 +1106,20 @@ static const gchar * const * muc_tubes_channel_fixed_properties = muc_channel_fixed_properties; static const gchar * const muc_channel_allowed_properties[] = { - TP_IFACE_CHANNEL ".TargetHandle", - TP_IFACE_CHANNEL ".TargetID", - TP_IFACE_CHANNEL_INTERFACE_CONFERENCE ".InitialChannels", - TP_IFACE_CHANNEL_INTERFACE_CONFERENCE ".InitialInviteeHandles", - TP_IFACE_CHANNEL_INTERFACE_CONFERENCE ".InitialInviteeIDs", - TP_IFACE_CHANNEL_INTERFACE_CONFERENCE ".InvitationMessage", + TP_PROP_CHANNEL_TARGET_HANDLE, + TP_PROP_CHANNEL_TARGET_ID, + TP_PROP_CHANNEL_INTERFACE_CONFERENCE_INITIAL_CHANNELS, + TP_PROP_CHANNEL_INTERFACE_CONFERENCE_INITIAL_INVITEE_HANDLES, + TP_PROP_CHANNEL_INTERFACE_CONFERENCE_INITIAL_INVITEE_IDS, + TP_PROP_CHANNEL_INTERFACE_CONFERENCE_INVITATION_MESSAGE, TP_PROP_CHANNEL_INTERFACE_ROOM_ROOM_NAME, TP_PROP_CHANNEL_INTERFACE_ROOM_SERVER, NULL }; static const gchar * const muc_tubes_channel_allowed_properties[] = { - TP_IFACE_CHANNEL ".TargetHandle", - TP_IFACE_CHANNEL ".TargetID", + TP_PROP_CHANNEL_TARGET_HANDLE, + TP_PROP_CHANNEL_TARGET_ID, NULL }; @@ -1139,12 +1134,12 @@ gabble_muc_factory_type_foreach_channel_class (GType type, channel_type_value = tp_g_value_slice_new (G_TYPE_STRING); /* no string value yet - we'll change it for each channel class */ - g_hash_table_insert (table, TP_IFACE_CHANNEL ".ChannelType", + g_hash_table_insert (table, TP_PROP_CHANNEL_CHANNEL_TYPE, channel_type_value); handle_type_value = tp_g_value_slice_new (G_TYPE_UINT); g_value_set_uint (handle_type_value, TP_HANDLE_TYPE_ROOM); - g_hash_table_insert (table, TP_IFACE_CHANNEL ".TargetHandleType", + g_hash_table_insert (table, TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, handle_type_value); /* Channel.Type.Text */ @@ -1186,7 +1181,7 @@ ensure_tubes_channel (GabbleMucFactory *self, GabbleTubesChannel **tubes_chan, gboolean requested) { - GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (self); + GabbleMucFactoryPrivate *priv = self->priv; TpBaseConnection *base_conn = (TpBaseConnection *) priv->conn; GabbleMucChannel *text_chan; TpHandle initiator = base_conn->self_handle; @@ -1212,7 +1207,7 @@ handle_text_channel_request (GabbleMucFactory *self, TpHandle room, GError **error) { - GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (self); + GabbleMucFactoryPrivate *priv = self->priv; TpBaseConnection *conn = TP_BASE_CONNECTION (priv->conn); GabbleMucChannel *text_chan; TpHandleSet *handles; @@ -1239,16 +1234,16 @@ handle_text_channel_request (GabbleMucFactory *self, return FALSE; initial_channels = tp_asv_get_boxed (request_properties, - TP_IFACE_CHANNEL_INTERFACE_CONFERENCE ".InitialChannels", + TP_PROP_CHANNEL_INTERFACE_CONFERENCE_INITIAL_CHANNELS, TP_ARRAY_TYPE_OBJECT_PATH_LIST); initial_handles = tp_asv_get_boxed (request_properties, - TP_IFACE_CHANNEL_INTERFACE_CONFERENCE ".InitialInviteeHandles", + TP_PROP_CHANNEL_INTERFACE_CONFERENCE_INITIAL_INVITEE_HANDLES, DBUS_TYPE_G_UINT_ARRAY); initial_ids = tp_asv_get_boxed (request_properties, - TP_IFACE_CHANNEL_INTERFACE_CONFERENCE ".InitialInviteeIDs", + TP_PROP_CHANNEL_INTERFACE_CONFERENCE_INITIAL_INVITEE_IDS, G_TYPE_STRV); invite_msg = tp_asv_get_string (request_properties, - TP_IFACE_CHANNEL_INTERFACE_CONFERENCE ".InvitationMessage"); + TP_PROP_CHANNEL_INTERFACE_CONFERENCE_INVITATION_MESSAGE); room_name = tp_asv_get_string (request_properties, TP_PROP_CHANNEL_INTERFACE_ROOM_ROOM_NAME); @@ -1558,7 +1553,7 @@ handle_tubes_channel_request (GabbleMucFactory *self, TpHandle handle, GError **error) { - GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (self); + GabbleMucFactoryPrivate *priv = self->priv; GabbleTubesChannel *tube = NULL; GabbleMucChannel *gmuc = NULL; @@ -1615,7 +1610,7 @@ handle_tube_channel_request (GabbleMucFactory *self, GError **error) { - GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (self); + GabbleMucFactoryPrivate *priv = self->priv; gboolean can_announce_now = TRUE; gboolean tubes_channel_created = FALSE; GabbleTubesChannel *tube = NULL; @@ -1703,12 +1698,12 @@ handle_stream_tube_channel_request (GabbleMucFactory *self, /* "Service" is a mandatory, not-fixed property */ service = tp_asv_get_string (request_properties, - TP_IFACE_CHANNEL_TYPE_STREAM_TUBE ".Service"); + TP_PROP_CHANNEL_TYPE_STREAM_TUBE_SERVICE); if (service == NULL) { g_set_error (error, TP_ERRORS, TP_ERROR_NOT_IMPLEMENTED, "Request does not contain the mandatory property '%s'", - TP_IFACE_CHANNEL_TYPE_STREAM_TUBE ".Service"); + TP_PROP_CHANNEL_TYPE_STREAM_TUBE_SERVICE); return FALSE; } @@ -1734,12 +1729,12 @@ handle_dbus_tube_channel_request (GabbleMucFactory *self, /* "ServiceName" is a mandatory, not-fixed property */ service = tp_asv_get_string (request_properties, - TP_IFACE_CHANNEL_TYPE_DBUS_TUBE ".ServiceName"); + TP_PROP_CHANNEL_TYPE_DBUS_TUBE_SERVICE_NAME); if (service == NULL) { g_set_error (error, TP_ERRORS, TP_ERROR_NOT_IMPLEMENTED, "Request does not contain the mandatory property '%s'", - TP_IFACE_CHANNEL_TYPE_DBUS_TUBE ".ServiceName"); + TP_PROP_CHANNEL_TYPE_DBUS_TUBE_SERVICE_NAME); return FALSE; } @@ -1781,7 +1776,7 @@ handle_call_channel_request (GabbleMucFactory *self, TpHandle handle, GError **error) { - GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (self); + GabbleMucFactoryPrivate *priv = self->priv; gboolean initial_audio, initial_video; GabbleMucChannel *muc; GabbleCallMucChannel *call; @@ -1844,6 +1839,27 @@ error: return FALSE; } +typedef gboolean (*ChannelTypeHandlerFunc) ( + GabbleMucFactory *self, + gpointer request_token, + GHashTable *request_properties, + gboolean require_new, + TpHandle room, + GError **error); + +typedef struct { + const gchar *channel_type; + ChannelTypeHandlerFunc f; +} ChannelTypeHandler; + +static ChannelTypeHandler channel_type_handlers[] = { + { TP_IFACE_CHANNEL_TYPE_TEXT, handle_text_channel_request }, + { TP_IFACE_CHANNEL_TYPE_TUBES, handle_tubes_channel_request }, + { TP_IFACE_CHANNEL_TYPE_STREAM_TUBE, handle_stream_tube_channel_request }, + { TP_IFACE_CHANNEL_TYPE_DBUS_TUBE, handle_dbus_tube_channel_request }, + { TPY_IFACE_CHANNEL_TYPE_CALL, handle_call_channel_request }, + { NULL } +}; static gboolean gabble_muc_factory_request (GabbleMucFactory *self, @@ -1856,21 +1872,22 @@ gabble_muc_factory_request (GabbleMucFactory *self, TpHandle handle; gboolean conference, room; const gchar *channel_type; + ChannelTypeHandler *h; handle_type = tp_asv_get_uint32 (request_properties, - TP_IFACE_CHANNEL ".TargetHandleType", NULL); + TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, NULL); channel_type = tp_asv_get_string (request_properties, - TP_IFACE_CHANNEL ".ChannelType"); + TP_PROP_CHANNEL_CHANNEL_TYPE); /* Conference channels can be anonymous (HandleTypeNone) */ conference = (handle_type == TP_HANDLE_TYPE_NONE && !tp_strdiff (channel_type, TP_IFACE_CHANNEL_TYPE_TEXT) && (g_hash_table_lookup (request_properties, - TP_IFACE_CHANNEL_INTERFACE_CONFERENCE ".InitialChannels") || + TP_PROP_CHANNEL_INTERFACE_CONFERENCE_INITIAL_CHANNELS) || g_hash_table_lookup (request_properties, - TP_IFACE_CHANNEL_INTERFACE_CONFERENCE ".InitialInviteeHandles") || + TP_PROP_CHANNEL_INTERFACE_CONFERENCE_INITIAL_INVITEE_HANDLES) || g_hash_table_lookup (request_properties, - TP_IFACE_CHANNEL_INTERFACE_CONFERENCE ".InitialInviteeIDs"))); + TP_PROP_CHANNEL_INTERFACE_CONFERENCE_INITIAL_INVITEE_IDS))); room = (handle_type == TP_HANDLE_TYPE_NONE && !tp_strdiff (channel_type, TP_IFACE_CHANNEL_TYPE_TEXT) @@ -1881,58 +1898,29 @@ gabble_muc_factory_request (GabbleMucFactory *self, if (handle_type != TP_HANDLE_TYPE_ROOM && !conference && !room) return FALSE; - if (tp_strdiff (channel_type, TP_IFACE_CHANNEL_TYPE_TEXT) && - tp_strdiff (channel_type, TP_IFACE_CHANNEL_TYPE_TUBES) && - tp_strdiff (channel_type, TP_IFACE_CHANNEL_TYPE_STREAM_TUBE) && - tp_strdiff (channel_type, TP_IFACE_CHANNEL_TYPE_DBUS_TUBE) && - tp_strdiff (channel_type, TPY_IFACE_CHANNEL_TYPE_CALL)) - return FALSE; - /* validity already checked by TpBaseConnection */ handle = tp_asv_get_uint32 (request_properties, - TP_IFACE_CHANNEL ".TargetHandle", NULL); + TP_PROP_CHANNEL_TARGET_HANDLE, NULL); g_assert (conference || room || handle != 0); - if (!tp_strdiff (channel_type, TP_IFACE_CHANNEL_TYPE_TEXT)) - { - if (handle_text_channel_request (self, request_token, - request_properties, require_new, handle, &error)) - return TRUE; - } - else if (!tp_strdiff (channel_type, TP_IFACE_CHANNEL_TYPE_TUBES)) - { - if (handle_tubes_channel_request (self, request_token, - request_properties, require_new, handle, &error)) - return TRUE; - } - else if (!tp_strdiff (channel_type, TP_IFACE_CHANNEL_TYPE_STREAM_TUBE)) + for (h = channel_type_handlers; h->channel_type != NULL; h++) { - if (handle_stream_tube_channel_request (self, request_token, - request_properties, require_new, handle, &error)) - return TRUE; - } - else if (!tp_strdiff (channel_type, TP_IFACE_CHANNEL_TYPE_DBUS_TUBE)) - { - if (handle_dbus_tube_channel_request (self, request_token, - request_properties, require_new, handle, &error)) - return TRUE; - } - else if (!tp_strdiff (channel_type, TPY_IFACE_CHANNEL_TYPE_CALL)) - { - if (handle_call_channel_request (self, request_token, - request_properties, require_new, handle, &error)) - return TRUE; - } - else - { - g_assert_not_reached (); + if (tp_strdiff (channel_type, h->channel_type)) + continue; + + if (!h->f (self, request_token, request_properties, require_new, + handle, &error)) + { + tp_channel_manager_emit_request_failed (self, request_token, + error->domain, error->code, error->message); + g_error_free (error); + } + + /* We've handled the request one way or another. */ + return TRUE; } - /* Something failed */ - tp_channel_manager_emit_request_failed (self, request_token, - error->domain, error->code, error->message); - g_error_free (error); - return TRUE; + return FALSE; } @@ -1975,7 +1963,7 @@ gboolean gabble_muc_factory_handle_jingle_session (GabbleMucFactory *self, GabbleJingleSession *session) { - GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (self); + GabbleMucFactoryPrivate *priv = self->priv; TpHandleRepoIface *room_repo = tp_base_connection_get_handles ( (TpBaseConnection *) priv->conn, TP_HANDLE_TYPE_ROOM); TpHandle room; diff --git a/src/namespaces.h b/src/namespaces.h index bacbb2ac5..59aded50a 100644 --- a/src/namespaces.h +++ b/src/namespaces.h @@ -123,4 +123,12 @@ #define NS_TEMPPRES "urn:xmpp:temppres:0" #define NS_GOOGLE_SHARED_STATUS "google:shared-status" +/* This is used by the extension Facebook uses to push you messages you send + * using other devices (or the website). + * + * http://www.youtube.com/watch?v=rSnXE2791yg is not the song I was looking + * for, but it's not bad. + */ +#define NS_FACEBOOK_MESSAGES "http://www.facebook.com/xmpp/messages" + #endif /* __GABBLE_NAMESPACES__H__ */ diff --git a/src/roster.c b/src/roster.c index 85b654cda..c7caa043b 100644 --- a/src/roster.c +++ b/src/roster.c @@ -22,8 +22,6 @@ #include "config.h" #include "roster.h" -#define DBUS_API_SUBJECT_TO_CHANGE - #include <string.h> #include <dbus/dbus-glib.h> diff --git a/src/util.h b/src/util.h index 9727e60de..fc83ee3b6 100644 --- a/src/util.h +++ b/src/util.h @@ -35,8 +35,6 @@ #include "types.h" -#define CHECK_STR_EMPTY(x) ((x) == NULL || (x)[0] == '\0') - typedef GSList * NodeIter; #define node_iter(node) (node->children) #define node_iter_next(i) (g_slist_next (i)) diff --git a/src/vcard-manager.c b/src/vcard-manager.c index ba8e834f7..71c3b2b08 100644 --- a/src/vcard-manager.c +++ b/src/vcard-manager.c @@ -847,7 +847,7 @@ observe_vcard (GabbleConnection *conn, { const gchar *fn = lm_message_node_get_value (fn_node); - if (!CHECK_STR_EMPTY(fn)) + if (!tp_str_empty (fn)) { field = "<FN>"; alias = g_strdup (fn); diff --git a/tests/twisted/Makefile.am b/tests/twisted/Makefile.am index debb6591b..50cf68313 100644 --- a/tests/twisted/Makefile.am +++ b/tests/twisted/Makefile.am @@ -105,6 +105,7 @@ TWISTED_TESTS = \ test-register.py \ text/destroy.py \ text/ensure.py \ + text/facebook-own-message.py \ text/initiate.py \ text/initiate-requestotron.py \ text/respawn.py \ diff --git a/tests/twisted/constants.py b/tests/twisted/constants.py index bbb10caa7..2a879d810 100644 --- a/tests/twisted/constants.py +++ b/tests/twisted/constants.py @@ -444,6 +444,14 @@ DELIVERY_REPORTING_SUPPORT_FLAGS_RECEIVE_SUCCESSES = 2 DELIVERY_REPORTING_SUPPORT_FLAGS_RECEIVE_READ = 4 DELIVERY_REPORTING_SUPPORT_FLAGS_RECEIVE_DELETED = 8 +DELIVERY_STATUS_UNKNOWN = 0 +DELIVERY_STATUS_DELIVERED = 1 +DELIVERY_STATUS_TEMPORARILY_FAILED = 2 +DELIVERY_STATUS_PERMANENTLY_FAILED = 3 +DELIVERY_STATUS_ACCEPTED = 4 +DELIVERY_STATUS_READ = 5 +DELIVERY_STATUS_DELETED = 6 + MEDIA_STREAM_ERROR_UNKNOWN = 0 MEDIA_STREAM_ERROR_EOS = 1 MEDIA_STREAM_ERROR_CODEC_NEGOTIATION_FAILED = 2 diff --git a/tests/twisted/text/facebook-own-message.py b/tests/twisted/text/facebook-own-message.py new file mode 100644 index 000000000..a695b3d92 --- /dev/null +++ b/tests/twisted/text/facebook-own-message.py @@ -0,0 +1,70 @@ +""" +Tests exposing Facebook's own-message extension via delivery reports. + +I would say that this has been reverse-engineered, but reading the completely +trivial protocol out of debug logs is hardly reverse-engineering. It isn't +documented anywhere I can find, mind you. +""" +from servicetest import ( + assertEquals, assertLength, assertContains, wrap_channel, EventPattern, + sync_dbus, + ) +from gabbletest import exec_test, elem, elem_iq +import constants as cs + +NS_FACEBOOK_MESSAGES = "http://www.facebook.com/xmpp/messages" + +def test(q, bus, conn, stream): + def send_own_message(to, text): + iq = elem_iq(stream, 'set', from_='chat.facebook.com')( + elem(NS_FACEBOOK_MESSAGES, 'own-message', to=to, self='false')( + elem('body')(text) + ) + ) + stream.send(iq) + q.expect('stream-iq', iq_type='result', iq_id=iq['id']) + + # First, test receiving an own-message stanza for a message sent to a + # contact we have an open channel for. + jid = '-5678@chat.facebook.com' + _, path, props = conn.Requests.EnsureChannel({ + cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_TEXT, + cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT, + cs.TARGET_ID: jid, + }) + channel = wrap_channel(bus.get_object(conn.bus_name, path), + 'Text', ['Messages']) + handle = props[cs.TARGET_HANDLE] + + text = u'omg omg its ur birthdayy <3 <3 xoxoxoxo' + send_own_message(to=jid, text=text) + e = q.expect('dbus-signal', signal='MessageReceived') + message, = e.args + assertLength(1, message) + header = message[0] + + assertEquals(handle, header['message-sender']) + assertEquals(cs.MT_DELIVERY_REPORT, header['message-type']) + assertEquals(cs.DELIVERY_STATUS_ACCEPTED, header['delivery-status']) + + assertContains('delivery-echo', header) + echo = header['delivery-echo'] + echo_header, echo_body = echo + + assertEquals(conn.GetSelfHandle(), echo_header['message-sender']) + assertEquals('text/plain', echo_body['content-type']) + assertEquals(text, echo_body['content']) + + channel.Text.AcknowledgePendingMessages([header['pending-message-id']]) + channel.Close() + + # Now test receiving an own-message stanza for a message sent to a contact + # we don't have a channel open for. It should be ignored (but acked). This + # is consistent with delivery failure reports. + q.forbid_events([EventPattern('dbus-signal', signal='MessageReceived')]) + send_own_message(to='-393939@chat.facebook.com', + text=u'please ignore this message') + sync_dbus(bus, q, conn) + +if __name__ == '__main__': + exec_test(test, params={'account': 'test@chat.facebook.com'}) diff --git a/tests/twisted/tools/exec-with-log.sh.in b/tests/twisted/tools/exec-with-log.sh.in index 0225e5038..11302e6f6 100644 --- a/tests/twisted/tools/exec-with-log.sh.in +++ b/tests/twisted/tools/exec-with-log.sh.in @@ -2,8 +2,10 @@ cd "@abs_top_builddir@/tests/twisted/tools" -GABBLE_DEBUG=all LM_DEBUG=net GIBBER_DEBUG=all WOCKY_DEBUG=all +GABBLE_DEBUG=all GIBBER_DEBUG=all WOCKY_DEBUG=all export GABBLE_DEBUG +export GIBBER_DEBUG +export WOCKY_DEBUG GABBLE_TIMING=1 export GABBLE_TIMING GABBLE_PLUGIN_DIR="@abs_top_builddir@/plugins/.libs" |